Mirai v1 API

Self-serve · every paid plan

The modelling agency, as an API.

Generate campaign imagery and video with AI clones of real, consented models, then license the results — the same pipeline, roster, and approval workflow that runs Mirai Studio, over 11 REST endpoints. Keys are minted in the brand dashboard; no sales call required.

Base URL · https://miraitalent.ai/api/v1

Every draft is watermarked until the model approves it and a license is paid. No API parameter can bypass this.

Quickstart

Four steps to a first render.

The API rides your Studio subscription — there is no separate API contract, tier, or metering system to negotiate.

  1. 01

    Create a brand account

    Sign up at miraitalent.ai — the same account runs the Studio web app and the API.
  2. 02

    Subscribe to a plan

    Every paid plan includes self-serve API access, from $49/month. Compare plans.
  3. 03

    Mint a key

    In the dashboard API tab. The plaintext is shown once; only its hash is stored. Up to 10 active keys per brand.
  4. 04

    Call the API

    Generate a draft, poll GET /assets/{id}, then license approved finals via POST /licenses.
curl https://miraitalent.ai/api/v1/generate \
  -H "Authorization: Bearer mirai_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model_id": "<a model id from GET /models>",
    "prompt": "editorial studio portrait, soft daylight",
    "format": "portrait"
  }'

Generation takes 1–5 minutes: once it starts, the response streams heartbeat whitespace, then one JSON document — see the transport note on POST /generate.

Auth · scopes · limits

One bearer key. Three scopes. Disclosed ceilings.

Requests send Authorization: Bearer mirai_sk_.... Only the SHA-256 of a key is stored; a revoked key fails exactly like an unknown one. These limits are the enforced values, rendered from the same module the rate limiter reads.

Scopes

generate
Create draft images (POST /generate) and clips (POST /videos).
license
Create license checkouts for approved assets (POST /licenses).
read
Roster, character sheets, and asset polling (all GET endpoints).

Rate limits · 60s windows

  • POST /generate20/min per key · 40/min per brand
  • POST /videos6/min per key · 10/min per brand
  • POST /licenses10/min per key
  • GET endpoints60/min per key

Per-brand ceilings apply across all of a brand's keys — minting extra keys never buys more throughput. 429s carry X-RateLimit-Limit / -Remaining / -Reset headers.

Billing

One pool with the Studio.

The API draws from the same plan allowances and credits as the Studio web app. Image calls count against your monthly image allowance; video renders count against your plan's video allowance, then pre-paid packs, then per-clip overage where your plan enables it. Failed renders are never billed.

Commercial licences are separate from generation and follow the public rate card, paid per approved asset through Stripe Checkout.

Plan allowances · shared by Studio + API

Starter$49 / mo30 images / moAdd-on packs
Growth$149 / mo100 images / mo3 clips / mo
Scale$499 / mo400 images / mo12 clips / mo
EnterpriseCustom volume + order-form terms — talk to us

Overage rates per plan are disclosed on /pricing and in your dashboard. Both surfaces and this table render from the same configuration that runs checkout.

Endpoint reference

The contract, generated from the spec.

This reference is rendered at build time from the same OpenAPI 3.1.0 document the server publishes at GET https://miraitalent.ai/api/v1/openapi.json — it cannot drift from what the routes enforce.

POST /generate

Generate a draft image with a model's AI clone

scope: generate20/min · key · 40/min · brand

Renders a watermarked draft anchored to the model's canonical identity kit. Sparse prompts are automatically enhanced; every prompt receives Mirai's editorial house style and passes a safety scan before delivery.

Streaming transport: generation takes 1-5 minutes, so once it starts the HTTP status is ALWAYS 200 and the body streams heartbeat whitespace until the final JSON. Read the full body, then JSON.parse it (leading whitespace is valid JSON) and check for an error field before treating it as success. Pre-flight failures (auth, validation, entitlement) return real 4xx statuses with JSON bodies.

Supports the Idempotency-Key header.

Parameters

Idempotency-Keyheader
Any string unique to this logical request. Retries with the same key replay the stored result instead of re-rendering.

Request body · application/json

model_idrequired
uuidModel id from GET /models.
promptrequired
string · ≤4000 charsCreative direction. Sparse prompts are auto-enhanced; Mirai's house style is always applied.
formatoptional
portrait | landscape | square | portrait_4_3 | portrait_16_9 | landscape_4_3 | landscape_16_9 | square_hd · default portrait_4_3Output aspect preset (friendly aliases accepted: portrait, landscape, square).
rendereroptional
openai/gpt-image-2/edit | fal-ai/nano-banana-2/edit | bytedance/seedream/v5/pro/edit | fal-ai/flux-2-pro/editfal endpoint that should make the pixels. Omit for the house default. Selects the PRIMARY only: the fallback still applies, and the response reports what actually ran. Renderers outside this list are refused (code: renderer_not_approved); approved renderers Mirai does not drive itself are refused with the reference path named (code: renderer_not_available_here).

Responses

200GenerateImageResult | Error
Streamed. Heartbeat whitespace followed by ONE JSON document — either GenerateImageResult or Error (in-body error with HTTP 200; see the streaming transport note).
400Error
Missing model_id/prompt, prompt over the length cap, unsupported format (code: invalid_format), or brand country required for a region-restricted model (code: brand_country_missing).
401Error
Missing, malformed, or revoked API key. Repeated failures are rate limited per IP.
402Error
No active subscription / no remaining credits, or the subscription is past_due.
403Error
Key missing the generate scope, model outside a trial's allow-list, or region restriction (code: region_restricted).
404Error
Model not found or not on the bookable roster.
409Error
Model not ready for generation (no reference photos), or a request with this Idempotency-Key is already in progress.
429Error
Rate limit exceeded — 20 requests/minute per key, 40/minute per brand across all keys.

POST /videos

Animate one of your generated images into a video clip

scope: generate6/min · key · 10/min · brand

Starts an image-to-video render (Seedance 2.0; face-reveal prompts route to Kling v3 Pro with the model's canonical headshot as an identity anchor). The source is the asset_id of one of YOUR generated images — raw URLs are not accepted.

Open-IP sources: when the source asset came from POST /ip/{slug}/generate, the render follows the character sheet instead of the fashion pipeline — the sheet's Seedance motion notes are the default motion direction (your prompt overrides them), the era style lock replaces the photorealism clause, and the sheet's legal exclusions are enforced in the prompt.

Async contract: responds 202 immediately with an asset_id. Poll GET /assets/{asset_id} until status leaves processing (typically 1-5 minutes): ready delivers the watermarked draft_url; failed carries an error message and nothing was billed.

Billing: draws on your plan's video allowance, then pre-paid video credit packs, then per-video overage where your plan enables it; a failed render refunds any consumed credit. Supports the Idempotency-Key header — a replay returns the SAME asset_id.

Parameters

Idempotency-Keyheader
Any string unique to this logical request. A replay returns the same 202 body (same asset_id) instead of starting a second render.

Request body · application/json

image_idrequired
uuidasset_id of one of YOUR generated images to animate (raw URLs are not accepted).
promptoptional
string · ≤4000 charsMotion direction. Omit for a calm identity-locked default. Mirai's cinematic realism clause is always appended.
durationoptional
5 | 10 · default 10Clip length in seconds.

Responses

202GenerateVideoAccepted
Render started. Poll the asset for the result.
400Error
Missing image_id, prompt over the length cap, unsupported duration (code: invalid_duration), or watermark-only source (code: source_has_watermark).
401Error
Missing, malformed, or revoked API key. Repeated failures are rate limited per IP.
402Error
No active subscription, past_due, or video quota exhausted with no pack/overage (code: video_quota_exceeded).
403Error
The API key does not carry the required scope.
404Error
Source image not found (or not owned by the calling brand — indistinguishable by design).
409Error
Source image unusable, or a request with this Idempotency-Key is already in progress.
429Error
Rate limit exceeded — 6 requests/minute per key, 10/minute per brand across all keys.

GET /models

List the bookable model roster

scope: read60/min · key

Public-safe fields only — no reference photos, clone configuration, earnings, or contact details. Only models who have granted partner-distribution consent appear; anyone not bookable or not consented is omitted entirely rather than flagged. Each entry carries the model's binding restrictions (excluded_categories, excluded_brands) so an integrator can refuse an out-of-scope brief before writing it, and the response carries approved_renderers — the exhaustive list of engines a likeness may be sent to.

Responses

200ModelList
The roster.
401Error
Missing, malformed, or revoked API key. Repeated failures are rate limited per IP.
403Error
The API key does not carry the required scope.
429Error
Rate limit exceeded — 60 requests/minute per key.

GET /models/{id}/references

Short-lived reference URLs for a consented model

scope: read60/min · key

Returns signed, expiring proxy URLs to the model's canonical reference kit, for feeding into an APPROVED renderer as identity references. Both query parameters are required: the declared renderer must be on the approved list (the 400 response enumerates it), and the declared brand is checked against the model's exclusions before anything is served — a brand she has excluded is refused outright. URLs expire in 15 minutes, every fetch is logged, and revocation applies to already-issued URLs immediately. The response carries her binding restrictions; they travel with the references.

Parameters

idpath · required
rendererquery · required
The exact endpoint id these references will be used with, e.g. bytedance/seedream/v5/pro/edit. Must be on the approved-renderer list; anything else is refused with the list attached.
brandquery · required
The brand this shoot is for. Checked against the model's excluded brands before any reference is issued.

Responses

200
Signed reference URLs, slot roles, expiry, and the model's binding restrictions.
400
Missing/unapproved renderer (approved_renderers enumerated in the body) or missing brand.
401Error
Missing, malformed, or revoked API key. Repeated failures are rate limited per IP.
403
Model has not granted partner-distribution consent, or the declared brand is excluded by the model.
429Error
Rate limit exceeded — 60 requests/minute per key.

GET /licenses/quote

Price a licence without committing

scope: read60/min · key

Non-binding and side-effect free. Called with image_ids alone it returns the full priced option matrix for those assets (formats, durations, per-format territories) so no integrator ever holds a stale copy of the rate card; called with format and duration it returns the exact quote. Every gate the real POST /licenses enforces is evaluated here too and returned as blockers rather than errors, including the instant-scope boundary: images auto-approved under a model's standing digital consent can be licensed for digital use only, and print / out-of-home requires her individual approval. Digital licences are always Global; regions exist for Print + OOH at the same price.

Parameters

image_idsquery · required
Comma-separated asset ids from /generate. One model per quote.
formatquery
durationquery
territoryquery

Responses

200
quote (when format+duration given), options (always, priced for these assets), blockers, and next_step.
400
Unknown format/duration/territory, or mixed models.
401Error
Missing, malformed, or revoked API key. Repeated failures are rate limited per IP.
403
Assets not found in your account.
429Error
Rate limit exceeded — 60 requests/minute per key.

GET /assets/{id}

Fetch the state of one of your generated assets

scope: read60/min · key

One id namespace covers images and videos; the response is discriminated by kind. Returns YOUR assets only — not-owned and not-found are both 404 so ids can't be probed.

The clean (un-watermarked) url is non-null IF AND ONLY IF the asset is licensed (model approval + paid license). draft_url is always the watermarked deliverable.

For video assets this is the polling endpoint of the POST /videos async contract — watch status (processing → ready | failed).

Parameters

idpath · required
Asset id returned by POST /generate or POST /videos.

Responses

200Asset
The asset, discriminated by kind.
401Error
Missing, malformed, or revoked API key. Repeated failures are rate limited per IP.
403Error
The API key does not carry the required scope.
404Error
No such asset in the caller's account.
429Error
Rate limit exceeded — 60 requests/minute per key.

GET /characters/{modelId}

Fetch a roster model's character sheet

scope: read60/min · key

The structured identity profile distilled at model approval — appearance descriptors, delivery notes, categories — for writing prompts that stay coherent with the real person. Visibility matches GET /models: only bookable models exist; unknown and unavailable ids 404 identically.

A roster model without a sheet yet also 404s with code character_sheet_not_ready — the sheet is enrichment, built at approval time.

Parameters

modelIdpath · required
Model id from GET /models.

Responses

200CharacterSheetResponse
The character sheet.
401Error
Missing, malformed, or revoked API key. Repeated failures are rate limited per IP.
403Error
The API key does not carry the required scope.
404Error
Model not found / not bookable, or no sheet yet (code: character_sheet_not_ready).
429Error
Rate limit exceeded — 60 requests/minute per key.

GET /ip

List the open-IP character catalog

scope: read60/min · key

The Mirai × FAL open-IP catalog: public-domain / free-use characters with legal dossiers, GenAI-optimized character sheets, and voice specs. Query filters: tier (A|B|C), risk (low|medium|high), franchise, commercial_use (true|false).

Tier C is a watchlist — entries not yet public domain; they always carry commercial_use: false and the detail route withholds prompt packs for them.

Parameters

tierquery
Filter by tier.
riskquery
Filter by trademark risk.
franchisequery
Filter by franchise key (e.g. oz, hundred-acre-wood).
commercial_usequery
true = only licensable entries; false = only watchlist/restricted.

Responses

200IpCatalogList
The catalog index.
401Error
Missing, malformed, or revoked API key. Repeated failures are rate limited per IP.
403Error
The API key does not carry the required scope.
429Error
Rate limit exceeded — 60 requests/minute per key.

GET /ip/{slug}

Fetch an open-IP character sheet with FAL prompt packs

scope: read60/min · key

Full character sheet (identity, legal dossier, visual DNA, voice spec) plus drop-in FAL prompt packs: image2 for openai/gpt-image-2/edit (including the 6-slot reference kit) and seedance for bytedance/seedance-2.0/reference-to-video. Legal avoid-lists are pre-merged into the negatives.

Watchlist (Tier C / commercial_use false) entries return prompt_packs: null and license_terms: null with a watchlist_note.

Parameters

slugpath · required
Character slug from GET /ip.

Responses

200IpCharacterResponse
The character sheet and prompt packs.
401Error
Missing, malformed, or revoked API key. Repeated failures are rate limited per IP.
403Error
The API key does not carry the required scope.
404Error
No such character.
429Error
Rate limit exceeded — 60 requests/minute per key.

POST /ip/{slug}/generate

Generate a licensed open-IP character image

scope: generate20/min · key · 40/min · brand

One-call Mirai × FAL flow: name a catalog character, receive a watermarked, licensable draft generated by the same gpt-image-2 chain as POST /generate. The character sheet anchors identity (visual DNA, palette, era style lock) and its legal avoid-list is enforced as an exclusion clause on every render. prompt is optional scene direction.

Open-IP images are born approval_status "approved" — there is no human likeness to consent — so the returned asset_id can go straight to POST /licenses. To animate, pass asset_id to POST /videos (Seedance); motion guidance lives in the character's prompt_packs.

Watchlist (Tier C / commercial_use false) slugs refuse with 403 ip_not_commercial and are never generated.

Parameters

slugpath · required
Character slug from GET /ip.

Request body · application/json

promptoptional
string · ≤4000 charsOptional scene direction (e.g. 'riding the steamboat at dawn'). The character sheet's identity prompt is always applied; this only sets the scene.
formatoptional
portrait | landscape | square | portrait_4_3 | portrait_16_9 | landscape_4_3 | landscape_16_9 | square_hd · default portrait_4_3Output aspect preset (friendly aliases accepted: portrait, landscape, square).

Responses

200IpGenerateImageResult
Draft created (long generations stream heartbeats; failures arrive as { error } in the body).
400Error
Invalid format or prompt too long.
401Error
Missing, malformed, or revoked API key. Repeated failures are rate limited per IP.
402Error
No active subscription/comp/credits, or subscription past_due.
403Error
Watchlist character (code: ip_not_commercial).
404Error
No such character.
429Error
Rate limit exceeded — 20 requests/minute per key.

POST /licenses

Create a license checkout for approved images

scope: license10/min · key

Creates a pending license plus a Stripe Checkout session; complete payment in a browser via checkout_url (the session and the pending license both expire after 1 hour). On payment, the license activates and the assets' clean URLs unlock on GET /assets/{id}.

The approval gate: only assets with approval_status: "approved" (the model's sign-off) can be licensed — this is the platform's load-bearing invariant and cannot be bypassed. All images in one license must be from the same model. Image licenses only on v1 (video licensing runs through the Studio).

Pricing: fee_cents = image_count x base(format) x multiplier(duration) x multiplier(territory). Base rates — Digital: $50/image; Print + OOH: $120/image; Social: $50/image; Print: $120/image. Duration multipliers — 6 months: x1; 12 months: x1.8; 24 months: x2.5; 10 years: x3.5.

Request body · application/json

image_idsrequired
array<uuid>Approved image asset ids — all from the same model.
formatrequired
Digital | Print + OOH | Social | PrintUsage format. Social and Print are legacy aliases of Digital and Print + OOH respectively.
durationrequired
6 months | 12 months | 24 months | 10 years
territoryoptional
Global | Asia | Middle East | Europe | Australia | North America | South America | Africa · default Global

Responses

200LicenseCheckout
Pending license created; pay via checkout_url.
400Error
Missing/invalid fields (codes: invalid_format, invalid_duration, invalid_territory, brand_country_missing) or images spanning multiple models.
401Error
Missing, malformed, or revoked API key. Repeated failures are rate limited per IP.
403Error
Images not owned by the caller, not approved (code: images_not_approved, with unapprovedIds), or region restriction (code: region_restricted).
404Error
Model not found.
409Error
Duplicate active/pending license (code: duplicate_license) or the model isn't payout-ready (code: model_not_payout_ready).
429Error
Rate limit exceeded — 10 requests/minute per key.

Error codes

Machine-readable, catalogued.

Errors are JSON: { "error": "...", "code": "..." }. The catalogue below is the spec's x-error-codes extension, rendered verbatim.

invalid_format
The format value is not in the accepted enum.
invalid_duration
The duration value is not in the accepted enum.
invalid_territory
The territory value is not in the accepted enum.
brand_country_missing
Set your brand's country in Settings before working with a region-restricted model.
region_restricted
This model is not available to brands based in your country.
images_not_approved
Only model-approved images can be licensed; unapprovedIds lists the offenders.
duplicate_license
An active or pending license already covers one of these images.
model_not_payout_ready
The model's payout account isn't ready; the booking is refused rather than stranding funds.
video_quota_exceeded
The plan's video allowance is used up and no credit pack or overage applies.
source_has_watermark
The video source image only has a watermarked file; generate a fresh image.
character_sheet_not_ready
The model is on the roster but has no character sheet yet.
key_limit_reached
A brand can hold at most 10 active API keys.

TypeScript SDK

@mirai/sdk

The official Node/TypeScript client: zero runtime dependencies, typed from this OpenAPI document, automatic retry on 429 (honouring Retry-After), idempotency-key support, and a waitForAsset helper for the video polling contract.

The npm release is being finalised. Until it lands, any OpenAPI 3.1.0 generator pointed at /openapi.json produces a typed client in your language today.

import { MiraiClient } from "@mirai/sdk";

const mirai = new MiraiClient({ apiKey: process.env.MIRAI_API_KEY! });

const { models } = await mirai.listModels();

const draft = await mirai.generateImage({
  model_id: models[0].id,
  prompt: "editorial studio portrait, soft daylight",
  format: "portrait",
});

const clip = await mirai.createVideo({ image_id: draft.asset_id });
const ready = await mirai.waitForAsset(clip.asset_id);

Start building

A key, a plan, and 11 endpoints between you and licensed campaign imagery.

Building a deeper integration — commerce platform, creative tool, agency workflow? Enterprise engagements add custom volume and order-form terms.