Docs API reference
Markdown Get an API key

API referenceChat and agents

Durable Chat Runs

The durable counterpart to POST /v1/chat/completions. Use it when a single chat turn may run long, call multiple tools, hit a safety gate, or need a human to approve spend before it happens. The server persists the run, streams typed events over SSE, and exposes cancel and cost-approval controls.

Run status is one of queued, running, completed, partial_failure, waiting_for_user, failed, or cancelled.

#Start a run

POST /v1/chat/runs Auth required

Returns 202 Accepted with the persisted run. Retrying with the same idempotency key returns that run instead of starting another: 200 OK with idempotent: true once it has left queued, or 202 with idempotent: false while it is still queued. Reconcile on data.run.runId. Fields may be sent in snake_case or camelCase; unknown fields return 400. Starting runs too quickly returns 429 with retryAfter (seconds) in the body.

#Headers

NameTypeInDescription
Idempotency-KeystringheaderOptional. X-Idempotency-Key is also accepted, and the body field idempotency_key works as a fallback. Keys longer than 200 characters are ignored, so the request runs as if no key was sent.

#Body

NameTypeInDescription
messagesrequiredarraybodyNon-empty OpenAI-style message array.
toolsarraybodyCustom OpenAI function-tool array.
tool_choicestring | objectbodyOpenAI tool choice.
modelstringbodyOptional LLM model id.
samplingobjectbodyOptional sampling parameters: max_tokens (default 4096), temperature, top_p, top_k, min_p, repetition_penalty, frequency_penalty, presence_penalty, task_profile, and think (default false).
media_referencesarraybodyOptional media references for hosted tools. Each url must be an http:// or https:// URL; inline data: URIs return 400, as do data: image URLs inside messages. Upload local files first with the media URL endpoints.
media_contextobjectbodyOptional initial media context: images[], videos[], audio[], plus uploadedImages[], uploadedVideos[], and uploadedAudio[] for caller-supplied uploads. Every entry must be an http(s) URL.
max_estimated_capacity_unitsnumberbodyRecorded on the request snapshot so callers can show the ceiling alongside the run. Not enforced on chat runs.
confirm_costbooleanbodyRecorded on the request snapshot. To pause chat runs for cost approval, set runtime_config.requireJobConfirmation: true; paid media tool calls then emit run_awaiting_cost_confirmation and wait for confirm-cost.
session_idstringbodyOptional caller session identifier.
client_message_idstringbodyOptional caller message identifier for client-side correlation.
token_typestringbodyspark, sogni, or auto.
billing_modestringbodyauto (default), subscription, or tokens, as on chat completions.
app_sourcestringbodyOptional caller label.
runtime_configobjectbodyRun-time tuning: qualityTier (fast, hq, or pro), safeContentFilter (boolean, default true; chat runs set the Sensitive Content Filter here, and a top-level safe_content_filter returns 400), and requireJobConfirmation (boolean; pause before paid media tool dispatch). Unknown keys are ignored.

#Request

curl https://api.sogni.ai/v1/chat/runs \
  -H "Authorization: Bearer $SOGNI_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "user", "content": "Storyboard a 5-shot neon teaser, 9:16, 15s."}
    ],
    "runtime_config": { "requireJobConfirmation": true }
  }'

#Response

{
  "status": "success",
  "data": {
    "run": {
      "runId": "run_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "ownerWalletAddress": "0x…",
      "status": "queued",
      "schemaVersion": "…",
      "backbone": { "schemaVersion": "…", "modelKnowledgeVersion": "…", "routingPolicyVersion": "…" },
      "timestamps": { "createdAt": "…", "updatedAt": "…" },
      "scope": { "ownerWalletAddress": "0x…", "appSource": "sogni-api" },
      "request": { "…": "request snapshot" },
      "messages": [],
      "toolCalls": [],
      "toolResults": [],
      "mediaContext": { "images": [], "videos": [], "audio": [], "uploadedImages": [], "uploadedVideos": [], "uploadedAudio": [] },
      "events": [{ "sequence": 0, "type": "run_created", "at": "…" }],
      "createTime": 1731950400000,
      "updateTime": 1731950400000
    },
    "idempotent": false
  }
}

#Read a run

GET /v1/chat/runs/:id Auth required

Returns the full run snapshot: current status, request, and events. A paused run carries waiting: { reason, message, details }; chat runs pause for cost_approval_required or safety_review_required. Long conversations are trimmed from the oldest turns to fit the model's context window, without an error.

#Read the event log

GET /v1/chat/runs/:id/events Auth required

Returns the persisted event log. Pass ?after=<sequence> to fetch only events past a known sequence number.

#Query parameters

NameTypeInDescription
afterintegerqueryOnly return events with sequence > after.

#Stream events

GET /v1/chat/runs/:id/events/stream Auth required

Server-Sent Events stream. Replays persisted events, sends a run_status frame with the current status, then follows new events until the run reaches completed, failed, partial_failure, or cancelled. A waiting_for_user run keeps the stream open. Resume with Last-Event-ID (takes precedence) or ?after=<sequence>. Lines starting with : are heartbeats. Treat run_status frames as idempotent. The log keeps the newest 2,000 events.

#Event types

  • Lifecycle: run_created, run_resumed, run_completed, run_partial_failure, run_failed, run_cancelled
  • Rounds: llm_round_started, assistant_message_delta (each round's full text, not token deltas), assistant_message_completed
  • Tool calls: tool_call_dispatched, tool_call_progress, tool_call_resolved
  • Media and state: media_context_updated
  • Billing: llm_spend carries the authoritative per-round LLM token cost (costInToken, costInUSD, tokenType, modelName, and token counts; dedupe on eventId), and billing_preview_updated
  • Pause and resume: run_waiting_for_user, run_awaiting_cost_confirmation (emits toolCallId and an estimate), run_cost_confirmation_resolved
  • Synthetic: run_status frames emitted by the stream on status transitions
  • Ignore event types you don't recognize; new types may be added.
id: 42
event: tool_call_dispatched
data: {"sequence":42,"type":"tool_call_dispatched","at":"…","payload":{…}}

id: 43
event: run_awaiting_cost_confirmation
data: {"sequence":43,"type":"run_awaiting_cost_confirmation","at":"…","payload":{"toolCallId":"call_…","toolName":"generate_video","estimatedCost":120,"tokenType":"spark"}}

#Cancel a run

POST /v1/chat/runs/:id/cancel Auth required

Cooperative cancel for a queued, running, or waiting_for_user run. Flips it to cancelled, signals active work, and appends run_cancelled. aborted reports whether active work was signalled directly. A run that is already finished returns 409.

#Body

NameTypeInDescription
reasonstringbodyOptional cancellation reason. Defaults to user_cancelled.

#Response

{
  "status": "success",
  "data": { "run": { "runId": "run_…", "status": "cancelled" }, "aborted": true }
}

#Confirm cost

POST /v1/chat/runs/:id/confirm-cost Auth required

Resolves a run paused with waiting_for_user and cost_approval_required. confirm returns the run to running and dispatches the held paid tool calls; cancel declines them and ends the run as completed with finalResponse.finishReason: "cancelled". One decision applies to every paid call held in the pause.

#Body

NameTypeInDescription
tool_call_idrequiredstringbodyID of the paused tool call (alias toolCallId). Use waiting.details.toolCallId from the run snapshot or the run_waiting_for_user event.
decisionrequiredstringbody"confirm" or "cancel".
acceptedCostPreviewrequiredobjectbodyRequired for decision: "confirm" (alias accepted_cost_preview). Echo waiting.details.costApprovalPreview from the run snapshot or the run_waiting_for_user event: { totalEstimatedCapacityUnits, tokenType, validityUntil }. A mismatched value returns 409; a missing or malformed object returns 400. The preview expires 5 minutes after the pause; after that, confirm returns 409 and the run can only be cancelled.
overridesobjectbodyOptional allowlisted edits applied to the tool call named in tool_call_id on resume: qualityTier (fast, hq, or pro), safeContentFilter, and prompt or prompts. Prompt edits are cost-neutral; cost-inflating keys are silently dropped.
reasonstringbodyOptional caller-supplied reason recorded with the decision.
idempotency_keystringbodyOptional. The Idempotency-Key and X-Idempotency-Key headers are also accepted. A concurrent duplicate returns 200 with idempotent: true. If the run has already resumed, a retry returns 409; read the run to reconcile.
Some pauses can't be confirmed. A cost_approval_required pause with waiting.details.flavor: "insufficient_credits" returns 409 for confirm; add credits and start a new run, or send decision: "cancel". A safety_review_required pause returns 409 here for either decision; release it with POST /cancel.