DOCUMENTATION / BUILD AND LAUNCH

Creative generation and revisions

Generate private images and copy, iterate with immutable versions, and publish a chosen asset for an approved Meta campaign.

Markdown

Discover and authorize

GET /api/v1/capabilities exposes data.creative separately from live_spend_enabled and billing_enabled. Check creative.enabled before requesting new jobs. A configured response means the service has a configured generation gate, not that provider access, capacity, or credentials have been exercised successfully.

Generation requires a live campaigns:write key or owner session; reads use campaigns:read. Sandbox keys are rejected. No Facebook connection, advertiser assignment, pixel, ad funding, or advertising approval is needed to generate. Publishing requires an enabled advertiser assignment, owner consent, and the same approved destination origin. Campaign launch has additional funding, tracking, and owner-approval checks.

Generation is explicitly paid OpenAI work. Copy uses gpt-6.1-sol; images use gpt-image-2.5-flare at high quality. Generation does not debit the advertising balance. usage.input_tokens and output_tokens describe the copy call; provider_cost_usd is currently null, not free and not an estimated or settled invoice. The API has no per-job dollar price or dollar spending-cap input. Establish the owner's permission and your own maximum number of jobs before starting.

Generate a first draft

Supply the product facts. The URL is context and a future destination: MarketingRouter does not crawl it. Include only supported claims, benefits, and offers. Do not invent reviews, statistics, logos, prices, or guarantees. The API rejects unknown fields, arbitrary model names, dimensions, external reference images, and budget fields.

export BASE_URL="https://marketingrouter.com"
# MR_API_KEY is a live key stored in your secret manager.
curl "$BASE_URL/api/v1/creatives" \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: field-notes-creative-v1' \
  -d '{"url":"https://example.com","name":"Field Notes","description":"A shared workspace for recording and organizing research notes.","audience":"Small research teams","benefits":["Shared notes","Searchable records"],"brand_style":"Minimal editorial illustration, warm neutrals and deep green","format":"square","variants":1}'

Use the origin serving these docs. The strict fields are url, name, description, audience, benefits, optional brand_style and offer, format, and variants. Defaults: square, one variant, and a minimal art direction. Choose 1–3 variants per original job.

FormatWidth × height
square1024 × 1024
portrait1024 × 1280
story1008 × 1792

POST returns 202 with the standard data/meta envelope, data.id, Location, and Retry-After: 5. Keep the job ID, exact request body, and idempotency key. An identical replay returns the same job with 202, even if generation already finished. A changed body with the same key returns idempotency_conflict. Do not create a fresh key merely because a response was delayed.

Poll and review

GET /api/v1/creatives/{id} returns the job. GET /api/v1/creatives returns the most recent ten jobs in data.items, including revisions, newest first; it has no pagination. Existing jobs remain readable if new generation is disabled.

curl "$BASE_URL/api/v1/creatives/$JOB_ID" \
  -H "Authorization: Bearer $MR_API_KEY"
Job stateAction
queuedThe accepted job is waiting. Read again after Retry-After.
processingRead stage (copy or images) and keep polling the same ID.
readyInspect every concept and saved image before choosing a variant.
attention_requiredStop automatic generation. Inspect error and preserved partial outputs.

Each variant has index, concept, image_url, image_expires_at, creative_file_id, and publish_status. concept includes angle, headline, primary_text, image_prompt, alt_text, and review_notes. Review factual claims, spelling, legibility, brand fit, and ad suitability. A generated or uploaded image is not an ad-policy certification.

Image previews are stored privately. Signed image_url values expire after 900 seconds; treat them as sensitive and do not log or publicly repost them. Read the job again to refresh an expired URL. Image expiry never requires regeneration. A read can schedule already-queued authorized work, but cannot reserve a new job or repeat an uncertain paid call.

Iterate without losing the original

POST /api/v1/creatives/{id}/revise creates a new job with one variant at index 0. It requires a saved parent image and a new Idempotency-Key for this intentional revision. The source job and any published asset remain unchanged. revision_of records the source job_id, source variant_index, mode, and instructions. Follow that link to compare versions.

Copy-only revision preserves the exact source image and dimensions. Optional headline and primary_text override those fields exactly; other copy is generated from the existing facts and instructions. Do not send format in copy mode.

{
  "variant_index": 0,
  "mode": "copy",
  "instructions": "Make the wording more direct without introducing new claims.",
  "headline": "Keep your research together"
}

Image-only revision preserves exact copy and edits the saved image as a reference. Do not send headline or primary_text in image mode. Omit format to keep its dimensions.

{
  "variant_index": 0,
  "mode": "image",
  "instructions": "Preserve the composition and use a warm cream and forest green palette.",
  "format": "portrait"
}

Both revises copy and edits the reference image. Both is the default if mode is omitted. Use an explicit mode to communicate intent.

curl "$BASE_URL/api/v1/creatives/$JOB_ID/revise" \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: field-notes-creative-v2' \
  -d '{"variant_index":0,"mode":"both","instructions":"Focus on organizing shared research. Keep the illustration recognizable and make the visual quieter."}'

Save the returned child ID and poll it. The same revision request and key return the same child with 202. A copy-only revision still consumes one job slot even though it does not generate another image. Revisions inherit the original product brief; instructions should refine it, not introduce unsupported product facts.

Publish one chosen variant

Publishing explicitly transfers a saved image into public advertising media owned by this workspace. It does not generate again, create a campaign, or activate advertising. Check GET /api/v1/advertiser first: assignment must be enabled, consent recorded, and the product URL origin must match the assigned destination.

curl "$BASE_URL/api/v1/creatives/$CHOSEN_JOB_ID/publish" \
  -H "Authorization: Bearer $MR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"variant_index":0}'

The returned job contains variants[index].creative_file_id and publish_status. Wait for publish_status ready before using that file ID. uploading or processing is not readiness. Repeat this same POST with the same job and index to reconcile an in-progress publication; GET only reads its stored state. No caller Idempotency-Key is needed: publication uses a durable internal upload identity. Do not regenerate to recover an upload failure. Share only the selected image the owner intends to advertise.

Build the campaign from the chosen version

Use the selected concept's exact headline and primary_text, its ready creative_file_id, and the assigned Facebook identity in a campaign. Set a future schedule and the owner's intended media budget. These example strings and dates are placeholders; substitute the chosen version and a valid schedule.

{
  "name": "Field Notes launch",
  "url": "https://example.com",
  "objective": "traffic",
  "budget_cents": 10000,
  "geo": ["US"],
  "mode": "live",
  "meta": {
    "headline": "Keep your research together",
    "primary_text": "Organize shared research notes in one workspace. Explore Field Notes.",
    "creative_file_id": "file_REPLACE",
    "facebook_social_account_id": "sacc_REPLACE",
    "starts_at": "2030-01-02T12:00:00Z",
    "ends_at": "2030-01-05T12:00:00Z"
  }
}

POST this to /api/v1/campaigns with a new campaign Idempotency-Key. POST /api/v1/campaigns/{id}/prepare creates a non-delivering draft. Read balance and advertiser readiness, then return approval_url to the owner. Only owner approval of the exact plan can activate it. Reconcile before reporting delivery. Generation and publication never imply approval.

For an existing campaign, use POST /api/v1/campaigns/{id}/revise with the changed copy, image ID, and any new schedule. This creates another plan and leaves the original running state intact. Prepare and approve the new campaign separately; pause an old campaign explicitly when appropriate.

Keep the agent loop bounded

The beta limit is ten jobs per workspace and 100 across the service for the beta lifetime, without periodic reset. Original jobs, revisions, failed jobs, and uncertain attempts consume this allowance. Each original job produces up to three variants; each revision produces one. Capacity is checked atomically, so concurrent agents share the same limits.

Before starting, choose a smaller local budget, such as one original job and at most two revisions. Poll every 5 seconds initially, then back off toward 30 seconds with jitter; stop unattended polling after a bounded window such as 15 minutes and retain the job ID. Read an existing job later rather than submitting it again. Do not interpret elapsed time as proof of failure or permission for another paid call.

Stop on attention_required, creative_generation_limit, creative_generation_capacity, unavailable generation, provider access/billing errors, or uncertain outcomes. Partial saved variants can still be reviewed; do not silently replace missing variants. Ask the owner or operator to decide after errors that may involve charges. No paid generation call is automatically retried after uncertainty.

Work with the human owner

Creative Studio at /dashboard?tab=creatives uses the same workspace jobs, IDs, variants, and revision_of lineage as the API. An owner can inspect or revise an agent's draft, and the agent can continue from the resulting job ID. Preserve the exact chosen job and variant in your work record so the human can compare versions. Only the latest ten jobs appear in the list; retained IDs remain readable individually.

The submission goal is 15 minutes once the account, assets, tracking, and funding are ready. Model runtime and Meta review vary. This is not a delivery SLA or an acquisition guarantee. See errors and recovery for asynchronous failures and owner approval for spending boundaries.