Skip to main content

Generation

Generation endpoints run a GPU workflow — an image or video pipeline — and return a job you poll or receive by webhook. Every endpoint has its own field names, types, defaults and bounds; the catalog below is generated live from the API, so it always matches what is deployed.

POST /api/v1/generate/{slug}
  • Auth: Bearer key with the generate:image scope.
  • Requires: a positive credit balance (otherwise 402).
generate:image is on by default

Unlike chat:companion, the generation scope is granted to every API key at creation. If you have a key, you can call these endpoints — no extra provisioning step.

Request body

Every generation endpoint takes the same envelope. Only the contents of inputs differ, and those are what the catalog below documents per endpoint.

FieldTypeRequiredNotes
inputsobjectThe endpoint's own named inputs. Unknown keys are rejected.
webhook_urlstring-HTTPS URL notified when the job finishes. Falls back to your account-level webhook. The callback is signed — see Webhooks.

Media inputs

An input typed as an image, video or audio URL is a URL Phantom Router fetches, not an upload. It must be publicly reachable over HTTPS — we resolve and SSRF-validate it before the job is created or billed, so private, loopback and link-local addresses are refused with 400.

Signed URLs work, but the signature is stripped before the URL is stored in job metadata.

Response

A successful call returns 202 Accepted immediately — the GPU work happens asynchronously:

{ "job_id": "6a0c2d1e-4f3b-4b1a-9f2c-8d7e6a5b4c30", "status": "queued" }

Then either poll or wait for the webhook:

GET /api/v1/generate/jobs/{job_id}

:::danger Verify the webhook signature Every callback carries a Standard Webhooks signature. Verify it before you act on the payload — otherwise anyone who learns your webhook URL can forge a completed job whose outputs point at a host they control. See Webhooks. :::

A completed job carries status, an outputs object keyed by the endpoint's published output names, and whatever envelope fields that endpoint declares (created_at, completed_at, and optionally execution time). See Jobs for the shared job-history endpoints.

Output URLs are presigned and expire — download the artifact rather than storing the link.

Example

curl -X POST https://api.phantomrouter.ai/api/v1/generate/{slug} \
-H "Authorization: Bearer $PHANTOM_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": { "prompt": "a lighthouse at dusk" }
}'

Each card in the catalog below carries a copyable command with that endpoint's real fields already filled in.

Errors

StatusCodeWhen
400INVALID_REQUESTAn input failed validation, an unknown key was sent, or a media URL is not fetchable.
401UNAUTHORIZEDMissing or invalid key.
402PAYMENT_REQUIREDInsufficient credit balance.
403FORBIDDENKey lacks generate:image.
404NOT_FOUNDNo such endpoint — or one your key cannot reach. See below.
503SERVICE_UNAVAILABLEThe endpoint's GPU provider is not currently dispatchable. Retry with backoff.
A 404 is not always a typo

Endpoints can be restricted to keys holding an extra scope. A restricted endpoint returns exactly the same 404 as one that does not exist, so that a private endpoint cannot be discovered by probing. If an endpoint you were told about returns 404, ask for the scope rather than assuming the slug is wrong — the catalog below lists only the endpoints open to every key.

API versions, deprecation and sunset

An endpoint's contract never changes underneath you. When we improve a workflow in a way that would change the request or response shape, the change ships as a new endpoint at a new path (for example character-image-v2), and the endpoint you already call keeps working unchanged.

Use schema_version to detect contract changes: it is a stable hash of the endpoint's published schema, returned by GET /api/v1/generate/{slug}/schema and in the catalog JSON. It changes only when the contract itself changes — purely additive improvements (a new optional input, a new response field) do change it, but never invalidate an existing integration.

When an endpoint version is scheduled for retirement, its responses announce it with standard HTTP headers:

HeaderMeaning
DeprecationThe endpoint is deprecated, as @<unix seconds> (RFC 9745). It still works.
SunsetThe date after which it may stop working (RFC 8594).
Linkrel="successor-version" — the path of the replacement endpoint (RFC 8288).

The catalog JSON carries the same facts as deprecated, sunset and successor_slug keys on the affected endpoint. Watch for the Deprecation header (or those keys) in your integration and plan the move to the successor before the sunset date; after retirement the old path returns 404.

Catalog

Every published generation endpoint, with its exact request and response contract. Generated live from the API, so a newly published endpoint appears here without a docs deploy.

Generating a client?

The same data is served as JSON at GET /api/v1/public/generate — unauthenticated, CORS-open, and ETag-cached. Each endpoint is also available on its own at GET /api/v1/public/generate/{slug}. The per-endpoint schema_version changes only when the contract itself changes.

Loading the live endpoint catalogue from /api/v1/public/generate. It is also readable directly as raw catalogue JSON — no key required.