API referenceGetting started
Getting Started
Everything on this page applies to every endpoint in the reference: where the API lives, how you authenticate, how errors and rate limits behave, and how spend is billed. Read it once, then go build.
#Introduction
The Sogni REST API lives at https://api.sogni.ai. One surface covers an
OpenAI-compatible chat endpoint, synchronous creative tools, a durable agent runtime,
a creative-workflow engine, project status, account balances, worker analytics, and
model discovery. Hosted chat and workflows stream over Server-Sent Events; everything
else is an ordinary JSON read or write.
#What's here
#Quick start
The shortest path to a result: a chat completion that runs Sogni creative tools server-side.
curl https://api.sogni.ai/v1/chat/completions \
-H "Authorization: Bearer $SOGNI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "Generate a cinematic image of a neon alley in Tokyo during rain."}
]
}'
#Authentication
Authenticated endpoints accept a bearer token in the Authorization header.
Create API keys in the Sogni account dashboard. Public endpoints, including aggregate worker
analytics and generation-model discovery, do not require a key.
Authorization: Bearer YOUR_API_KEY
#Two credential types
- API keys are long-lived UUIDs scoped to a wallet, intended for backend and SDK use. They are required for durable chat runs and creative workflow execution. Generate or rotate yours at dashboard.sogni.ai/api-key.
- Session tokens are short-lived browser JWTs issued by the Sogni auth flow. Most read endpoints accept either credential type.
The legacy api-key header is still accepted, but new integrations should use Authorization: Bearer.
#Versioning
Each endpoint has its own versioned path. Most use /v1/*; newer-shape
replacements include owner-scoped project status at /v2/projects/:id and
account balances at /v4/account/balance. Use the exact path shown for each
endpoint. Do not change the version prefix across your whole integration.
#Errors
The API uses standard HTTP status codes. Successful responses return 200,
201, or 202; failures return 4xx for caller mistakes
and 5xx for server-side problems. Error bodies are JSON.
| 200 | OK. Synchronous success. |
| 201 | Created. A workflow (inline start or reseed), a workflow template (create or fork), or a replay record was saved. |
| 202 | Accepted. A durable chat run or a personal LoRA import was accepted, a workflow template run is waiting for cost approval, or a workflow resume was scheduled. |
| 304 | Not modified. A cached public read matched your If-None-Match tag; reuse your cached body. |
| 400 | Validation error. A body or query parameter failed validation. |
| 401 | Authentication missing or invalid. |
| 402 | Insufficient balance. The selected token can't cover the request, or a vendor-model step needs Premium Spark. |
| 403 | Forbidden. The credential or account isn't allowed to perform the action, such as a subscriber-only feature without an active plan or publishing a public workflow template. |
| 404 | Resource not found, or hidden from the caller. |
| 409 | Conflict. The run or workflow is in the wrong state for the action (for example not awaiting cost approval, a stale or expired cost preview, or already finished), an idempotency key was reused with a different confirm-cost body, too many workflows are active, or a resource already exists. |
| 413 | Payload too large. A replay record exceeds 1 MB, or a workflow template exceeds its size limit. |
| 422 | Unprocessable entity. A workflow template failed schema validation or could not compile. |
| 429 | Rate limited. Wait for the Retry-After header or the retryAfter body field (seconds) when present; otherwise back off with jitter. |
| 500 | Internal error. Retry after backoff; report persistent failures. |
| 502 | Upstream model error, such as a failed model stream or a malformed tool call. Retry. |
| 503 | Temporarily unavailable. Retry with exponential backoff and jitter; do not treat it as data loss. |
| 504 | Upstream timeout. Retry with backoff. |
#Error envelopes
LLM routes (/v1/chat/completions, /v1/models) emit the OpenAI-compatible error shape:
{
"error": {
"message": "'messages' is required and must be a non-empty array",
"type": "invalid_request_error",
"param": null,
"code": "invalid_request_error"
}
}
All other endpoints emit the Sogni envelope:
{
"status": "error",
"errorCode": 102,
"message": "Creative workflow requires input.steps"
}
Sogni envelopes may also include details (structured context, such as why a
409 happened) and retryAfter in seconds. OpenAI-shaped errors may
include details. Some 429 bodies are a plain
{ "error": "Too Many Requests" }, and workflow template validation failures return
{ "status": "error", "data": { "validation": { "issues": [ … ] } } }. Branch on the
HTTP status first.
#Rate limits
When a request is rate-limited, the API returns 429 Too Many Requests.
Honor the Retry-After header when present. Otherwise, retry with exponential
backoff and jitter. Reduce concurrency instead of immediately repeating failed requests.
409 because
too many workflows are active, cancel or finish an existing run before submitting another,
or batch work into a single multi-step workflow. Check the error message: other conflicts
also use 409. The active-workflow 409 carries
details.activeWorkflowCount and details.activeWorkflowLimit, and it
does not use up your start allowance. A 429 on a workflow start means starts
arrived too quickly or overall workflow capacity is full. A start-rate refusal carries the
wait in the Retry-After header and, in seconds, in the body's
retryAfter; a start sent before then is refused again. Retry with the same
Idempotency-Key.
Default limits for workflows, render concurrency, and SDK connections, and how to design around them, are in Production Integrations.
#Idempotency
Write endpoints that can produce side effects accept an idempotency key. Reusing the same key for the same caller returns the original result instead of starting a duplicate run. Use it to make retries safe across network failures and double-clicks.
#Headers
Idempotency-Key: 7c9e6f7c-23a1-4f06-9d33-2dd5d6c8f5fb
X-Idempotency-Key is accepted as an alias.
#Supported endpoints
POST /v1/chat/runsstarts a durable chat run and also acceptsidempotency_keyin the body.POST /v1/chat/runs/:id/confirm-costresolves a chat-run cost-approval pause and also acceptsidempotency_keyin the body.POST /v1/creative-agent/workflowsstarts a durable workflow (header only).POST /v1/creative-agent/workflows/:id/confirm-costresolves a workflow cost-approval pause and also acceptsidempotency_keyin the body.POST /v1/creative-agent/workflows/:id/reseedstarts a new take of a finished workflow (header only). A reseed mints new seeds, so without a key a retry starts a second take.
#Billing and tokens
Spend is denominated in two token types. Pick one explicitly with token_type on a
request, or let the API choose.
| Token | How acquired | Used for |
|---|---|---|
| sogni | Native. Earned through Supernet participation or staking. | Pays for Sogni-native models (such as Krea, Qwen, LTX, WAN, MiniMax H3, and ACE-Step) when selected, or as the fallback for auto. |
| spark | Purchased with cash, or earned. | Paid first under auto. Premium Spark is required for external vendor models: gpt-image-2, gpt-image-2.5-sunburst, gpt-image-2.5-flare, seedance2, seedance2-mini, seedance2-5, happyhorse-1.1-*, wan3.0-video, and wan3.0-spicy-video. |
#Selecting a token
Accepted values are auto (default), sogni, and spark. When tokens are billed, auto pays with Spark first and falls back to SOGNI.
{ "token_type": "auto" }
/v1/chat/completions also accepts an X-Token-Type header (the body field wins); other
endpoints read token_type from the body only. Vendor-model jobs are normalized to
spark automatically, regardless of preference.
#Plan or tokens
token_type only picks which token pays. Whether an active
Unlimited plan or your token balance pays is a
separate field, billing_mode, accepted by
chat completions,
tool execution,
chat runs, and
workflows (start, resume, and reseed).
| billing_mode | Behavior |
|---|---|
| auto | Default. An active Unlimited plan covers the work it can, and no tokens are spent on that work. Everything else bills your token balance according to token_type. |
| subscription | Bill eligible work against the plan only. Work the plan can't cover fails instead of billing tokens. |
| tokens | Always bill your token balance, even when a plan is active. |
With an active plan, changing token_type between spark and
auto does not move a job off the plan. To spend Premium Spark you hold, which gets
fastest-priority queue access ahead of plan-covered jobs, send
billing_mode: "tokens". Vendor models are never plan-covered and always bill
Premium Spark. A request that asks to pay in SOGNI (token_type: "sogni") is not
plan-covered either.
{ "token_type": "spark", "billing_mode": "tokens" }
#Vendor model gating
Models from external vendors (OpenAI GPT Image 2 and GPT Image 2.5 Sunburst / Flare,
ByteDance Seedance 2.0 / Mini,
Seedance 2.5,
Alibaba HappyHorse 1.1,
Wan 3, and Wan 3 Uncensored) bill
Premium Spark. Hosted chat may choose a GPT Image model for storyboard or character-sheet work;
if the account is not eligible for Premium Spark, chat uses a Sogni-hosted model instead.
Workflows never substitute: a workflow that names a vendor model returns 402 with
details.vendorModels before any step runs, including a confirm_cost: false
estimate.
#Cost approval
- Creative workflows use a two-step confirmation. Submit with
confirm_cost: falseto get a400whosedetails.estimatedCapacityholds the estimate, then resubmit withconfirm_cost: true.max_estimated_capacity_unitsis a hard cap: plans over it are rejected with400before anything is saved. Template runs (workflow_id) always return202with apreviewand wait forPOST /v1/creative-agent/workflows/:id/confirm-cost. - Chat runs opt in with
runtime_config.requireJobConfirmation: true. When the model calls a paid media tool, the run pauses inwaiting_for_userwith reasoncost_approval_required, holds every paid call from that round, and emitsrun_awaiting_cost_confirmationfor each held call. Resume or decline withPOST /v1/chat/runs/:id/confirm-cost, echoingwaiting.details.costApprovalPreview.confirm_costandmax_estimated_capacity_unitsare recorded on chat runs but not enforced.