AI Actors
API

Three on-ramps. One credit wallet.

Ounie accounts hit the REST endpoints with a bearer key. AI assistants — including the Ounie AI Team — connect over MCP and get a native tool surface. One-off scripts can skip the account and pay for a single hero frame in USDC via x402. All three drive the same pipeline, and account calls draw your shared Ounie credits (1 credit = 1¢) — refused when short, never overdrawn.

Overview
An AI UGC video ad, as an API.

An ad runs a pipeline: a frame (an AI actor holding your product, composed into one 9:16 still), a voice (your script spoken by a chosen voice in any of 20 languages via ElevenLabs), a video (the frame lip-synced to that voice), and optional captions burned into the MP4. In auto mode it runs end-to-end; in preview mode it stops at the frame so you can approve it before spending on the voice + video.

Scripts can optionally be grounded in up to 3 of your Ounie brains — facts from your own knowledge base folded into the words the actor says. Grounding is free and needs an ounie.com credential (session cookie or the master key); it's excluded on x402 because a keyless caller has no Ounie account.

Base URL https://aiactors.ounie.com · JSON everywhere · scripts up to 500 characters · the clip length = the spoken length of your script · max 5 ads in flight per account. Finished videos are stored on permanent public URLs.

Authentication
Three credentials, one rule: the caller owns everything it touches.
Session cookie

The dashboard's own auth. Every REST endpoint accepts it, and cookie calls can ground in your brains.

App key · aia_live_…

Minted at Dashboard → API keys (shown once, up to 5 active). Send it as Authorization: Bearer aia_live_… or, where headers can't be set, as ?api_key=aia_live_…. It generates and bills normally but has no ounie.com credential, so it cannot ground scripts in brains.

Master key · ounie_live_…

One key across the whole Ounie app fleet. Enable "Use across Ounie apps" on the key at ounie.com → Settings → API keys and it works here as a Bearer too — and because it IS an ounie.com credential, it unlocks brain-grounded scripts for agents.

Credits & refusal semantics
Reserve → settle → refund, per step. Refused, never overdrawn.

Each pipeline step reserves its price from the caller's shared Ounie wallet — the frame (40 cr), then the video (fast 15 cr/s, premium 22 cr/s). Delivery settles it; a failed or aborted step refunds automatically and in full — exactly once, however many pollers race. Because each step is billed separately, a delivered frame stays spent while a failed video refunds on its own (a 10-second premium ad ≈ 262 cr).

If the wallet can't cover a reserve, the call is refused up front with 402 insufficient_credits and a buy_credits_url (https://ounie.com/dashboard/settings) — an agent on your key can never overdraw you.

Free by design: GET reads, downloads, share pages, image uploads, and brain retrieval.

REST endpoints
The same routes the dashboard uses. All accept cookie or Bearer auth.
POST/api/adsCreate an ad
{
  "actor_id": "maya",              // from GET /api/actors …
  "actor_image_key": "…",          // … OR your upload (POST /api/media/upload?kind=actor)
  "product_image_key": "…",        // required (POST /api/media/upload?kind=product)
  "product_name": "Glow Serum",
  "script": "Stop scrolling — …",  // spoken words … OR:
  "generate_script": true,          // let the app write it (+2 cr)
  "brain_ids": ["…"],              // ground a generated script (≤ 3)
  "tier": "video_premium",         // video_fast | video_premium
  "duration_sec": 10,              // 4–15
  "auto_advance": true              // render frame then video (agents); false = stop at frame
}

Auto mode reserves frame + video up front and renders both — poll the ad. Preview mode reserves only the frame and returns it for approval (then call /animate).

GET/api/adsList ads

Query params: status, limit, offset. Free.

GET/api/ads/:idPoll one ad

Lazily advances a rendering step — each call can settle, refund, or advance the pipeline. This is how agents wait on the video. Free.

POST/api/ads/:id/animateAnimate an approved frame
{ "tier": "video_premium", "duration_sec": 10 }

Preview-mode step 2 — reserves the video and starts the render. Only from frame_ready.

POST/api/ads/:id/reframeRe-render the frame

Composes a fresh hero frame (40 cr), keeping the ad. Only from frame_ready.

POST/api/ads/:id/abortAbort a running ad

Refunds every live step in full and marks the ad aborted. Free.

POST/api/ads/:id/shareToggle the share page
{ "public": true }

Succeeded ads only. Returns share_url (/a/:id). Default private. Free.

DELETE/api/ads/:idDelete an ad

Terminal ads only (abort a running one first). Free.

POST/api/media/upload?kind=actor|productUpload an actor or product image

Multipart "file" or JSON {"url":"https://…"} — up to 10 MB, sniffed server-side, stored private. Returns an image_key. Free.

POST/api/scriptWrite a script
{ "product_name": "…", "product_description": "…", "brain_ids": ["…"], "topic": "…", "duration_sec": 10 }

Generates a hook + spoken words + scene direction (2 cr). Pass the result to /api/ads as "script" to avoid re-billing.

GET/api/actorsThe actor library

Public — the curated AI actors (id, name, tags, portrait_url) you can cast. No auth.

GET/api/brainsList your brains

For grounding a script. Cookie or master-key callers get their brains; a aia_live_ key gets an honest empty list. Free.

GET/api/creditsCredit balance

Spendable credits + monthly included remaining + buy_credits url. Free.

GET/api/pricingThe price list

Public — script/frame/video prices + the x402 endpoint. No auth.

MCP server
Native tools for Claude, Cursor, ChatGPT, the AI SDK — and the Ounie AI Team.
Endpoint  https://aiactors.ounie.com/api/mcp   (legacy SSE: /api/sse)
Header    Authorization: Bearer aia_live_…       (or ounie_live_…)

# Clients that can't set headers append the key to the URL —
# this is how the Ounie AI Team connects:
https://aiactors.ounie.com/api/mcp?api_key=aia_live_…
ToolWhat it doesCost
create_adCast an actor + product + script → renders the hero frame then the spoken video (auto mode). Poll get_ad until succeeded.frame 40 + video (15–22/s)
animate_adPreview-mode step 2: animate an approved frame into the spoken video.video (reserved)
get_adPoll an ad; advances a rendering step and settles or refunds when it finishes.free
list_adsList your ads, filterable by status.free
list_actorsThe curated AI-actor library. No auth required.public
list_voicesThe curated voice catalog. No auth required.public
list_languagesSupported spoken languages + caption styles. No auth required.public
generate_scriptWrite a UGC script from a product description, optionally grounded in your brains.2 credits
list_brainsYour Ounie brains for grounding a script (master ounie_live_ key only).free
get_credit_balanceSpendable Ounie credits in the shared wallet.free
get_pricingThe credit prices + the x402 endpoint. No auth required.public
whoamiThe authenticated key's owner and key id.free

Grounding a script over MCP needs the master ounie_live_ key — it's forwarded upstream to read your brains. A aia_live_ app key creates ads normally but writes ungrounded scripts (and list_brains tells it why).

x402 · keyless pay-per-call
No account, no key, no credits — the USDC is the payment. The hero frame only.
# 1 — ask; the server answers with payment terms
curl -X POST https://aiactors.ounie.com/api/x402/frame \
  -H "Content-Type: application/json" \
  -d '{"actor_id":"maya","product_image_url":"https://…/product.png","product_name":"Glow Serum"}'
# → 402 { "x402Version": 1, "accepts": [{ "maxAmountRequired", "payTo", "asset", … }] }

# 2 — sign the terms with your wallet and retry
curl -X POST https://aiactors.ounie.com/api/x402/frame \
  -H "Content-Type: application/json" \
  -H "X-Payment: <signed payload>" \
  -d '{"actor_id":"maya","product_image_url":"https://…/product.png","product_name":"Glow Serum"}'
# → 200 { "frame_url": "https://…/frame.png", "tx_hash": "0x…" }
EndpointWhatPrice
POST /api/x402/frameCompose one 9:16 hero frame from a public actor + product image URL500000 atomic USDC (≈ $0.50)

Plainly: x402 is the frame only, and brain-grounded scripts are excluded — an x402 caller has no Ounie account and no brains to ground in. The video needs an account because a multi-minute render outlives an x402 response; the reserve → settle → poll lifecycle exists for exactly that.

Errors
Errors are JSON: { "error": "code", … }. A charged-then-failed step always refunds.
CodeStatusMeaning
unauthorized401Missing or invalid credential — send a session cookie, a aia_live_ Bearer, or ?api_key=.
insufficient_credits402The wallet can't cover the reserve. The body includes required_credits, balance_credits, and buy_credits_url. Nothing was charged.
actor_required / product_image_required / script_required400A create_ad call needs an actor (id or image), a product image, and a script (or generate_script=true).
unknown_actor400actor_id isn't in the library — call list_actors.
content_blocked400The script or product copy failed content screening. Not charged.
not_ready_to_animate400animate_ad was called on an ad that isn't at frame_ready.
too_many_running429You already have 5 ads in flight — wait for one to finish or abort one.
not_found404No ad with that id belongs to this account.
service_unavailable503Generation isn't configured on this deployment.
generation_failed502The provider refused or failed a step. Reserved credits for that step were refunded.
The ad object
Returned by every ad route as { "ad": … }.
{
  "id": "9f2c…",
  "status": "succeeded",            // frame_rendering | frame_ready | video_rendering
                                    //   | succeeded | failed | aborted
  "actor": { "id": "maya", "label": "Maya", "uploaded": false },
  "product": { "name": "Glow Serum", "description": "…" },
  "script": {
    "source": "generated",          // user | generated
    "hook": "Stop scrolling.",
    "text": "Stop scrolling — this changed my mornings.",
    "scene_direction": "Holds the bottle to camera, smiles."
  },
  "grounded": true,
  "grounding_thin": false,          // true = brains had nothing relevant
  "brain_ids": ["…"],
  "tier": "video_premium",
  "duration_sec": 10,
  "aspect_ratio": "9:16",
  "frame_url": "https://…/frame.png",
  "video_url": "https://…/video.mp4",   // null until the video succeeds
  "error": null,
  "is_public": false,               // share page off by default
  "source": "api",                  // web | api | mcp | x402
  "created_at": "2026-07-13T…",
  "completed_at": "2026-07-13T…"
}