Bloom is now Rainbrand

By Tomas Ward

Published

Updated

6 min read

How do agents retrieve brand context at runtime?

Retrieve task-specific brand guidance and files, pass them to the tools making the work, and record the version used. Implement deliberate reuse and a visible failure path.

TL;DR

Retrieve brand context before the step that creates branded work. Identify the intended brand, describe the task, wait for usable guidance and files, then pass them to the producing tools. Record the version used. Reuse context only while the task and relevant brand inputs still fit, and make retrieval failures visible instead of silently producing generic work.

Fetch the context before creating the work

Fetch the relevant rules and files before calling the creation model or renderer. Retrieval is only useful if those inputs reach the tool making the output.

Your application can fetch them before invoking a model, or the agent can request them during the task:

Retrieval pointWho initiates itWhat to make explicit
Application step before generationYour codeThe fetch must complete before creation starts
Agent tool call during the taskThe model, following its instructionsWhen to call, what to request and what to do if it fails
Local snapshot read during the taskThe agent or applicationWhich version is installed and when to replace it

Anthropic describes retrieving context as needed; OpenAI shows how a model requests a tool call and your application runs it. A local snapshot stays at its installed version.

Keep retrieval instructions separate from brand details

Keep changing brand details in maintained context. Standing instructions should cover:

  • Which brand to use, and how to resolve an ambiguous request.
  • Which tasks need brand context.
  • How to pass the guidance and files to the tools making the output.
  • What to report when retrieval fails or the guidance does not cover the task.

For customer-facing products, resolve the authorised Brand ID on your server; do not let the model guess from a name. Supply the task's format, dimensions, audience, channel, copy and constraints. Keep temporary offers or creative directions distinct from established brand facts.

Request and wait for usable context

Rainbrand maintains brand context for agents and products. A Brief selects guidance and files for the supplied Brand ID and task.

  • MCP (Model Context Protocol): call create_brief, then get_brief with its brief_id and wait: true. Repeat while pending or processing.
  • REST: set your API key, replace the placeholder with an accessible ready brand, and supply your task:
curl --request POST https://www.rainbrand.com/api/v1/briefs \
  --header "x-api-key: $RAINBRAND_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "brandSessionId": "<YOUR_BRAND_ID>",
    "task": "A 1600 × 900 campaign image for [brand], aimed at [audience]. Use the headline [exact headline] and call to action [exact copy]."
  }'

Read GET /briefs/{id}?wait=true with the returned data.id and the same authentication. Poll while pending or processing; use only a completed result. A failed result supplies error and retry details.

The create request returns HTTP 202 with this documented response:

{
  "data": {
    "id": "<RETURNED_BRIEF_ID>",
    "status": "pending"
  }
}

For Node.js 20+, save this as request-brief.mjs; it uses built-in fetch. Set RAINBRAND_API_KEY, RAINBRAND_BRAND_ID and RAINBRAND_REQUEST_KEY, replace the task, and run node request-brief.mjs. Retain a unique request key per task; reuse it only to recover the same POST and body.

import { setTimeout as delay } from "node:timers/promises";

const apiKey = process.env.RAINBRAND_API_KEY;
const brandId = process.env.RAINBRAND_BRAND_ID;
const requestKey = process.env.RAINBRAND_REQUEST_KEY;
if (!apiKey || !brandId || !requestKey) {
  throw new Error("Set the API key, authorised Brand ID and request key");
}

const task =
  "A 1600 × 900 campaign image for [brand], aimed at [audience]. " +
  "Use the headline [exact headline] and call to action [exact copy].";
const baseUrl = "https://www.rainbrand.com/api/v1";
const deadline = Date.now() + 5 * 60_000;

async function request(path, options = {}) {
  for (let attempt = 0; attempt < 3; attempt++) {
    const remaining = deadline - Date.now();
    if (remaining <= 0) throw new Error("Brief wait budget exhausted");

    const response = await fetch(`${baseUrl}${path}`, {
      ...options,
      headers: {
        "x-api-key": apiKey,
        "Content-Type": "application/json",
        ...options.headers,
      },
      signal: AbortSignal.timeout(Math.min(30_000, remaining)),
    });
    const body = await response.json();

    if (response.status === 429 && attempt < 2) {
      const header = response.headers.get("Retry-After");
      const seconds = header === null ? NaN : Number(header);
      const waitMs =
        Number.isFinite(seconds) && seconds >= 0
          ? Math.max(1_000, seconds * 1_000)
          : 1_000 * 2 ** attempt;
      if (waitMs < deadline - Date.now()) {
        await delay(waitMs);
        continue;
      }
    }
    if (!response.ok) {
      throw new Error(
        `${response.status}: ${body.error?.message ?? "Request failed"}`
      );
    }
    if (!body.data) throw new Error("Response has no data");
    return body.data;
  }
}

async function main() {
  const created = await request("/briefs", {
    method: "POST",
    headers: { "idempotency-key": requestKey },
    body: JSON.stringify({ brandSessionId: brandId, task }),
  });
  if (!created.id) throw new Error("No Brief ID returned");
  console.error(`Brief ID: ${created.id}`);

  while (Date.now() < deadline) {
    const brief = await request(
      `/briefs/${encodeURIComponent(created.id)}?wait=true&timeout=20`
    );
    if (brief.status === "pending" || brief.status === "processing") {
      await delay(1_000);
      continue;
    }
    if (brief.status === "failed") {
      throw new Error(
        `${brief.error?.code ?? "BRIEF_FAILED"}: ` +
          `${brief.error?.message ?? "Brief failed"}; ` +
          `new attempt may succeed: ${brief.error?.retryable === true}`
      );
    }
    if (
      brief.status !== "completed" ||
      !brief.skillId ||
      typeof brief.guidance !== "string" ||
      !brief.guidance.trim() ||
      !Array.isArray(brief.assets)
    ) {
      throw new Error("Brief has no usable completed context");
    }

    // This is the input record for your producing tools.
    console.log(
      JSON.stringify(
        {
          brandId,
          briefId: brief.id,
          skillId: brief.skillId,
          task,
          guidance: brief.guidance,
          assets: brief.assets,
        },
        null,
        2
      )
    );
    return;
  }
  throw new Error("Wait budget exhausted; resume reading the saved Brief ID");
}

await main().catch((error) => {
  console.error(error.message);
  process.exitCode = 1;
});

The example's failure handling is deliberate:

  • Five minutes overall and up to three attempts per rate-limited request are workflow choices.
  • Numeric Retry-After delays have a one-second minimum. Otherwise retries wait one then two seconds; a delay beyond the remaining budget stops the request.
  • Other HTTP errors, network failures, unavailable versions and unusable/failed results stop the script. Pending work keeps the same Brief.
  • Retain the key and Brief ID to recover the POST or resume reading. A new attempt after terminal failure needs a new key.

The API quickstart covers this sequence, and Get a Brief defines the response:

REST fieldUse
data.idSave the Brief reference with the output
data.skillIdRecord the brand version used; it can be null if that version was deleted
data.guidancePass the completed Markdown guidance to the producing step
data.assetsRoute the selected files by their kind, media type and URL
data.errorInspect failure details rather than treating a failed Brief as empty guidance

Pass both guidance and files to the producing tools

Keep the task, guidance and files together. Renderers need actual font and logo files; image models may also need references and type specimens.

For banking platform Mercury, Codex built a payment-confirmation concept using the Brief's downloaded Arcadia fonts and original logo:

Mercury payment-review screen concept built by Codex, with the original knot logo in a pale sidebar and locally loaded Arcadia type in the headline and payment details

Codex rendered the screen with the supplied logo and font files

Pass supported inputs to your creation tools. If a format is unsupported, use a suitable renderer rather than silently dropping it. For a connected agent:

Add to your agent’s instructions
For [brand] work, request a Rainbrand Brief describing the output, audience, channel and exact copy. Wait for completion and inspect the guidance and files. Give the original task, guidance and relevant assets to each tool creating the work. Use the supplied logo and fonts where the tool supports them. If the guidance does not fit, correct the task and request another Brief. If retrieval fails or a required file cannot be used, report it before continuing.

For the complete brand locally, export a Brand Skill: saved identity, guidance and assets. Keep the folder structure and version, and replace it after relevant changes.

Reuse by task and version

The Brief tools recommend reuse for the same goal, audience, direction and sources. Before reuse:

  1. Check access to the intended brand.
  2. Check its version. Compare data.activeSkill.id with the Brief's data.skillId. If different or missing, retrieve fresh context.
  3. Check task and assets. Direct Library changes can require refresh too. Removal affects future Briefs, not completed ones.
  4. Open required files before handing them to creation tools.

Preserve original inputs with historical work. Cache expiry alone does not establish freshness, and an edit after your version check can still leave the task on earlier guidance; record what it actually used.

Requests use credits, so reuse closely related context instead of requesting it for every message.

Distinguish context reuse from prompt caching

A provider's prompt cache reuses processing for matching input; it does not fetch today's brand. OpenAI recommends stable material first and changing content later. Keep standing instructions stable, then supply retrieved context in messages or tool results.

File access has its own lifetime:

ContextFile links
Completed BriefNo scheduled expiry while the Brief, brand, version and asset remain available. Anyone holding a link can open it.
Retrieved Brand SkillTemporary signed URLs. Retrieve again after expiry and check the returned brand version.

Make failure visible before generation

Stop creation when required context is missing:

FailureResponse
Wrong or ambiguous brandResolve the intended accessible brand before requesting context
Brief still pendingContinue reading the same Brief
Brief failedRead its error; retry only when appropriate and within your retry budget
Missing permissionCorrect access instead of using another customer's or a default brand
Rate limitWait for the returned delay before retrying
Required asset unavailableRefresh access or report the missing input
Context omitted from a downstream toolRepair the handoff and repeat the affected creation step

Use bounded retries and Retry-After; record the failed stage. After creation, Review can check the finished image and expected copy against the brand. Your tools make corrections.

Verify the entire handoff

Inspect one record linking brand, Brief, version, inputs and output. Test unavailable retrieval, stale context and unreadable files; each should stop creation with a useful reason. The embedded-product guide covers the surrounding product workflow.

All 5 Developers articles
  • 8 min read

    Best brand APIs for AI agents

    Brand APIs return different things. Lookups describe any company’s public identity, platform APIs read the files a customer already stores, and a maintained brand returns guidance and files for each task. Compare what each returns, who keeps it current, how access works and how pricing differs.

  • 5 min read

    How to add brand context to your AI product

    Follow a generation job from the customer’s task and authorized brand through guidance, assets, rendering and review. Keep future jobs current with saved brand versions and applied corrections.

  • 6 min read

    How to manage your customers’ brands in an AI product

    Tie each customer to the right brand on your server, then handle creation, corrections, versions and deletion without exposing another customer's guidance or files.

One brand, every agent.

Connect your agents