roasOS
Workflows

Make static ads

Turn one product into a set of static ads: the copy, the brand colours, the logo and the product, rendered as images.

A statics run turns one product into a set of static ads. roasOS reads your product and your brand, writes the headline, the sub-head and the call to action, prices the whole set on one card, and then renders each variant.

POST /v1/workflows/statics is the typed door onto the roasos:statics-factory skill. For the concept behind the door, refer to Statics.

A statics run is an agent run

The door answers with an agent run, not with a job. The work is a plan: it reads the product, it writes the copy, it prices the set, and then it starts one image job for each variant.

StepWhere you read it
The run itselfGET /v1/agent-runs/{id}
The light pollGET /v1/agent-runs/{id}/status
The lifecycleGET /v1/agent-runs/{id}/events
The answer of the agentGET /v1/agent-runs/{id}/result
Each image the run startedGET /v1/jobs/{id}

Remix answers the same way. Recast and animate-a-still answer with a job. Refer to Remix an ad and to Runs.

1. Say what the set must sell

FieldRule
product_idRequired. The product that the statics sell. roasOS resolves it before the run starts.
briefOptional. 400 characters at most. roasOS reads a brief as evidence, never as an instruction.
variantsOptional. 1 to 8. The default is 3.
aspect_ratioOptional. 1:1, 4:5, 9:16 or 16:9. The default is 4:5.
laneOptional. scene or product. The default is scene.
avatar_idOptional. A gallery avatar or a workspace avatar, for a person in the frame.
max_creditsOptional. The ceiling on the thinking of the run.
generation_spend_cap_creditsOptional. The ceiling on what the run may render without you.

The product images and the avatar come from your workspace. The logo, the colours, the two font names and cta_text come from your brand kit.

variants stops at 8. Past eight quotes, the approval gate stops approving itself and an unattended run waits for a person who is not there. roasOS refuses variants: 9 with 400 validation_error and names the ceiling in the message. Start a second run for a larger set.

2. Quote

Set quote_only to true. A quote is free, and it reserves nothing.

Send an Idempotency-Key header. Use 16 to 255 characters. Use the same key for the quote and for its confirm.

curl -X POST https://api.roasos.com/v1/workflows/statics \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Idempotency-Key: statics-glow-serum-001" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": "5e6b3311-7a2c-4f19-b3d8-0c41e7f92a63",
    "brief": "Summer restock. The bottle is the hero. Keep the copy under six words.",
    "variants": 3,
    "aspect_ratio": "4:5",
    "lane": "scene",
    "max_credits": 25,
    "quote_only": true
  }'

The answer is 200. The status is quoted.

{
  "data": {
    "schema_version": "agent-run@1",
    "kind": "agent_run",
    "id": "2b8f41ac-...",
    "trigger": "workflow",
    "skill_id": "5003b82a-4888-4986-b870-a1d6e7ac89cc",
    "conversation_id": null,
    "status": "quoted",
    "max_credits": 25,
    "generation_spend_cap_credits": 0,
    "credits_reserved": 0,
    "credits_max": 25,
    "pricing_version": "agent-token-pricing@2026-10-04.v3",
    "quote_id": "7f10c3b8-...",
    "expires_at": "2026-08-21T12:04:11.000Z",
    "authorization": { "status": "auto_approved" },
    "created_at": "2026-08-21T11:54:11.000Z",
    "updated_at": "2026-08-21T11:54:11.000Z"
  }
}

skill_id names the pack skill that the door runs. Refer to The roasOS skill pack.

Confirm the quote inside 10 minutes. A quote that is too old answers 410 quote_expired.

The quote is the ceiling of the run, not the price of the images

The statics door carries no image price. It returns an agent-run envelope, and never a kind: "quote" object with a credit total for the set.

credits_max is max_credits plus generation_spend_cap_credits. It is a ceiling, and it is not an estimate.

The price of the images arrives later, on the gate card in step 5. roasOS quotes every variant on the worker before it opens that card, so you see one number for the whole set before one image is made.

3. Confirm

Send the same body with the quote_id. Use the same idempotency key. Remove quote_only.

curl -X POST https://api.roasos.com/v1/workflows/statics \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Idempotency-Key: statics-glow-serum-001" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": "5e6b3311-7a2c-4f19-b3d8-0c41e7f92a63",
    "brief": "Summer restock. The bottle is the hero. Keep the copy under six words.",
    "variants": 3,
    "aspect_ratio": "4:5",
    "lane": "scene",
    "max_credits": 25,
    "quote_id": "7f10c3b8-..."
  }'

The answer is 202. roasOS reserves the ceiling and starts the run.

An unknown product_id answers 404 product_not_found, and it answers before the run starts. No credits move. The same rule holds for an unknown avatar_id, which answers 404 avatar_not_found.

4. Poll the run

curl https://api.roasos.com/v1/agent-runs/2b8f41ac-.../status \
  -H "Authorization: Bearer roasos_sk_..."

An agent run sends no webhook. Poll every few seconds.

5. The card holds the price of the set

The run stops at the set. gate_opened is the last event, and the run carries one entry in pending_approvals.

{
  "data": {
    "id": "2b8f41ac-...",
    "trigger": "workflow",
    "status": "processing",
    "conversation_id": null,
    "generation_spend_cap_credits": 0,
    "pending_approvals": [
      {
        "gate_id": "6d09b7e2-...",
        "summary": "static ads x 3",
        "credits": 6,
        "expires_at": "2026-08-21T12:11:40.000Z",
        "approve_url": "https://api.roasos.com/v1/agent-runs/2b8f41ac-.../gates/6d09b7e2-.../approve"
      }
    ]
  }
}

credits is the price of every variant added together. roasOS quotes each compiled request on the worker first, so a partial price set never opens as one number.

The default of generation_spend_cap_credits is 0, so a statics run that says nothing about the cap always stops here.

6. Approve the card

A run started from a workflow writes no thread, so approve the card over REST.

curl -X POST https://api.roasos.com/v1/agent-runs/2b8f41ac-.../gates/6d09b7e2-.../approve \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Idempotency-Key: statics-glow-serum-001-approve" \
  -H "Content-Type: application/json" \
  -d '{ "generation_spend_cap_credits": 30 }'

The approval starts a second run, and follow_up_run_id names it. Poll that run exactly as you polled the first one.

Approve is a REST operation only. It is not an MCP tool. A connected model may decline an approval. It must never approve its own spend. Refer to Approving spend is never a tool.

7. Read the images

Each variant is an ordinary image job.

curl "https://api.roasos.com/v1/jobs?limit=10" \
  -H "Authorization: Bearer roasos_sk_..."

Poll each job until the status is completed, failed or canceled. Read the file at GET /v1/jobs/{id}/result. The URL is immutable. Refer to Jobs.

Let the run render without you

Set generation_spend_cap_credits above the price of the set. The run then approves the card itself, and the renders start in the same run.

curl -X POST https://api.roasos.com/v1/workflows/statics \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Idempotency-Key: statics-glow-serum-002" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": "5e6b3311-7a2c-4f19-b3d8-0c41e7f92a63",
    "variants": 8,
    "lane": "product",
    "max_credits": 25,
    "generation_spend_cap_credits": 60,
    "quote_id": "c1b7e409-..."
  }'

The run approves the card by itself only when the price is inside the credits that remain in generation_spend_cap_credits, and inside auto_approve_up_to_credits on your key. In every other condition the card waits. Refer to Spend caps and approvals.

Run the skill in your own words

The door is one way in. The pack skill is the other.

curl -X POST https://api.roasos.com/v1/skills/roasos:statics-factory/runs \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Idempotency-Key: statics-freeform-001" \
  -H "Content-Type: application/json" \
  -d '{ "generation_spend_cap_credits": 60, "quote_only": true }'

Send the typed body to the door when your software knows the product. Run the skill by slug when a person states the task in their own words. Both paths meter the same way. Refer to Skills.

The two lanes

LaneWhat it doesCredits for one image
sceneRenders the whole frame on Nano Banana Pro, with the product images as references5 at 1K and 2K, 10 at 4K
productPlaces the product cut-out into a described scene on Bria Product Shot1 for the placement

Refer to Statics for the choice between them, and for the fallback that the product lane uses while the placement endpoint is an API capability only.

The limits, stated plainly

roasOS cannot guarantee your exact typeface. The image model draws the headline, the sub-head and the call to action from the exact strings in the prompt. A brand-kit font slot holds a name, not a font file. The letters come out in a similar face, and a glyph can come out wrong. Render the variant again. A wrong glyph is not a refund.

Make an avatar over the API with POST /v1/avatar/nano-banana-pro. The MCP tool is roasos_avatar_nano_banana_pro. Send the new id in avatar_id, or send a roasOS gallery avatar. The legacy path POST /v1/avatars answers 503 avatar_generation_unavailable.

  • variants is 1 to 8. The ceiling is the number of quotes that the approval gate can hold.
  • One run holds one product.
  • roasOS renders no text layer after the image. There is no deterministic overlay step today.
  • A Discover creative is a structure and style reference only. It never becomes source media in a statics run. Refer to Media rights.

Price

A statics run has no price of its own.

WhatMeter
The thinking of the runmax_credits. roasOS charges the real tokens and refunds the rest.
Each rendered imageThe catalog price of the model, quoted before dispatch and bounded by generation_spend_cap_credits

roasOS reserves credits_max at the confirm step. It settles the run when the run stops, and it refunds the part that the run does not use. Refer to What a run costs.

The brand-kit read and the product read inside the run cost 0 credits and carry no quote envelope.

Errors

StatusCodeCause
400validation_errorA field is outside its range, for example variants: 9
400idempotency_key_requiredThe Idempotency-Key header is absent
400quote_requiredThe body has no quote_only value and no quote_id
400spend_cap_exceeds_policyThe ceiling is above the maximum price for one request of the key
402approval_requiredThe ceiling is above the unattended limit of the key
402insufficient_creditsThe balance does not cover the ceiling
404product_not_foundThe product is not in your workspace
404avatar_not_foundThe avatar is not in your workspace and not in the gallery
410quote_expiredQuote again under a new idempotency key
429daily_credit_budget_exceeded, monthly_credit_budget_exceededThe budget of the key has no space
503skill_store_unavailableroasOS cannot read the pack skill at this time

Refer to Errors.

From an agent

The MCP tool is roasos_statics. It obeys the same quote rule as every paid tool.

An agent can start a statics run and read it. An agent can decline an approval. An agent can never approve one. Refer to Connect an AI agent (MCP).

On this page