Sogni: Learn logo
Markdown

💳Billing & Cost Control

Sogni Intelligence bills both LLM inference (chat and planning tokens) and creative media work in two token types, and exposes per-request controls to estimate, cap, and explicitly approve media costs before any paid work runs. This page collects the rules and primitives that appear individually on the chat, durable-run, and workflow endpoints.

Pass token_type to pick which balance pays. For workflows, use max_estimated_capacity_units as a hard cap and confirm_cost: false to get an estimate before starting. For chat runs, set runtime_config.requireJobConfirmation: true to pause before paid media work. These controls are not a total USD or account-wide budget: LLM inference bills separately as consumed. Watch run_waiting_for_user events for media-approval pauses.


#Token types

Token What it pays for How to get it
SOGNI Native Sogni Supernet inference — all Sogni-trained / Sogni-hosted models (Qwen LLMs, Z-Image, Chroma, Qwen Image Edit, FLUX, Wan video, Ace-Step 1.5 XL audio, etc.) Earned from running a worker node, staking, and seasonal leaderboard airdrops; also available on the open market
Premium Spark External-vendor models (OpenAI GPT Image 2 and GPT Image 2.5 Sunburst/Flare, ByteDance Seedance 2.0 / Fast / Mini and Seedance 2.5, Alibaba HappyHorse 1.1, Wan 3, and Wan 3 Uncensored) and can also pay for any native model Purchased at dashboard.sogni.ai with a credit card or with USDC/USDT stablecoins
Free Spark Native Sogni Supernet models only; cannot pay for vendor/premium models. Accounts subject to free API restrictions use Krea 2 Turbo (krea2_turbo_fp8_scaled) as of September 2026. Eligibility can differ by account and subscription; a model listing alone is not approval to use free Spark. Claim the Monthly Boost: 400 free Spark per UTC month, available when your free-Spark balance is under 800

Learn more: SOGNI token vs Spark Points.

#Selecting which token pays

Every paid endpoint accepts a token_type field:

token_type Behavior
"sogni" Pay in SOGNI when supported. Falls back to Spark for vendor-only jobs (e.g. GPT Image 2).
"spark" Pay in Spark for everything in the request. Required for vendor models.
"auto" Default. Pays with Spark first and falls back to SOGNI; vendor models always use Spark.

Send token_type in the JSON body. /v1/chat/completions also accepts an X-Token-Type header (the body wins); other endpoints read the body only. Tool execution inside a chat completion or chat run inherits the request's token_type; vendor-model tool calls are normalized to Spark even if the parent request asked for sogni / auto.

{
  "model": "qwen3.6-35b-a3b-gguf-iq4xs",
  "messages": [{"role": "user", "content": "Make a hero image"}],
  "token_type": "spark"
}

#LLM token spend

LLM inference is billable, not just media generation. Every chat turn fires many small billable LLM calls — the assistant rounds plus auxiliary cognition (vision analysis, prompt refinement, transition planning). These are denominated in the request's token_type (Spark or SOGNI) and surfaced as a single per-turn line item in Billing history (category Chat, with input/output/total token counts), distinct from media (category Media).

In durable chat runs the authoritative per-round cost arrives on the llm_spend SSE event (costInToken, costInUSD, tokenType, modelName, and token counts). Dedupe on payload.eventId when tallying.

LLM-token spend is not gated by confirm_cost — only worker / vendor / ffmpeg media work pauses for approval. The cost-control primitives below (max_estimated_capacity_units, confirm_cost, cost-approval pauses) govern media work; LLM tokens bill as they are consumed.


#Vendor-model gating

GPT Image 2 (gpt-image-2) and GPT Image 2.5 Sunburst/Flare (gpt-image-2.5-sunburst, gpt-image-2.5-flare), Seedance 2.0 (seedance2, seedance2-mini) and Seedance 2.5 (seedance2-5), HappyHorse 1.1 (happyhorse-1.1-t2v, happyhorse-1.1-i2v, happyhorse-1.1-r2v), Wan 3 (wan3.0-video), and Wan 3 Uncensored (wan3.0-spicy-video) are external-vendor models. The legacy seedance2-fast selector is still accepted but routes to the faster, lower-cost seedance2-mini. Two rules apply that don't apply to native models:

  1. They require Premium Spark. A request that asks for them with token_type: "sogni" is normalized to Spark for those tool calls; no automatic fallback to SOGNI tokens.
  2. Hosted chat only uses them when the account can pay. Name the vendor model explicitly to be sure you get it — ask for it in the user message so the LLM emits the right tool-call arguments, write the tool call yourself (sogni_tool_execution: false), or name it in a workflow step's arguments.model / arguments.videoModel. Hosted chat may also choose a GPT Image model for storyboard or character-sheet work; if the account is not eligible for Premium Spark, chat substitutes a Sogni-hosted model instead. Workflows never substitute: a workflow that names a vendor model on an ineligible account returns 402 with details.vendorModels before any step runs.

In chat completions, the simplest path is to name the vendor model in the user message so the LLM picks it up:

{
  "messages": [
    { "role": "user", "content": "Use GPT Image 2 to generate the product hero on wet asphalt with neon rim light." }
  ],
  "model": "qwen3.6-35b-a3b-gguf-iq4xs",
  "token_type": "spark",
  "sogni_tools": "creative-tools"
}

In durable workflows, name the vendor model directly inside the step's arguments:

{
  "input": {
    "title": "GPT Image 2 hero",
    "steps": [
      {
        "id": "hero",
        "toolName": "generate_image",
        "arguments": {
          "prompt": "Product hero on wet asphalt with neon rim light",
          "model": "gpt-image-2",
          "gptImageQuality": "high",
          "outputFormat": "png"
        }
      }
    ]
  },
  "token_type": "spark"
}

For the full vendor-model option matrix (quality flags, output formats, context-image limits, audio windows for Seedance), see Chat Completions → External Media Models.


#Estimated capacity units

Durable creative workflows return an estimated capacity-units value: a shared cross-model unit that the API uses to express "how much paid work is this going to do." Use it for hard caps and pre-flight approval — not as an exact billing total.

  • compose_workflow (planner) returns estimated_capacity_units alongside the plan and a fits_budget flag.
  • POST /v1/creative-agent/workflows rejects a request with 400 before persistence if the shared estimate exceeds max_estimated_capacity_units; details.estimatedCapacity carries the estimate.
  • The cost preview is best-effort. Final billing is reconciled against actual worker output (steps, resolution, duration, vendor-reported usage).
curl https://api.sogni.ai/v1/creative-agent/workflows \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": { "steps": [/* ... */] },
    "token_type": "spark",
    "max_estimated_capacity_units": 25
  }'

Pair max_estimated_capacity_units on the planner call and on the workflow start to hard-cap cost at both ends — the planner cap guards the LLM's plan; the start cap guards the final submission.


#Cost-approval flow

Chat runs and workflows approve paid media work differently.

#In durable chat runs

Start the run with runtime_config.requireJobConfirmation: true. When the model calls a paid media tool, the run holds every paid call from that round, emits run_awaiting_cost_confirmation for each one, then moves to status: "waiting_for_user" and emits run_waiting_for_user with the pending details. confirm_cost and max_estimated_capacity_units are recorded on chat runs but not enforced.

id: 5
event: run_awaiting_cost_confirmation
data: {"sequence":5,"type":"run_awaiting_cost_confirmation","at":"...","payload":{"toolCallId":"call_abc","toolName":"generate_video","estimatedCost":18,"tokenType":"spark"}}

id: 6
event: run_waiting_for_user
data: {"sequence":6,"type":"run_waiting_for_user","at":"...","payload":{"reason":"cost_approval_required","message":"Awaiting user approval before spending credits.","details":{"toolCallId":"call_abc","pendingToolCallIds":["call_abc"],"costApprovalPreview":{"totalEstimatedCapacityUnits":18,"tokenType":"spark","validityUntil":"2026-05-15T12:05:00.000Z"}}}}

To approve, call POST /v1/chat/runs/:id/confirm-cost with the pending tool call ID, decision: "confirm", and acceptedCostPreview echoed from details.costApprovalPreview (required for confirm; a mismatched preview returns 409, and the preview expires five minutes after the pause). This resumes the same run:

{
  "tool_call_id": "call_abc",
  "decision": "confirm",
  "acceptedCostPreview": {
    "totalEstimatedCapacityUnits": 18,
    "tokenType": "spark",
    "validityUntil": "2026-05-15T12:05:00.000Z"
  }
}

The confirm body may also carry an overrides object (allowlisted to qualityTier, safeContentFilter, and prompt / prompts) for prompt or quality edits the user made on the approval screen — see Durable Chat Runs → Cost approval. To reject the pending tool call, send decision: "cancel" to the same endpoint. To reject the full run, cancel it with POST /v1/chat/runs/:id/cancel.

Chat runs pause with cost_approval_required or safety_review_required. A cost pause whose details.flavor is "insufficient_credits" cannot be resolved by confirming; add credits and submit a new run, or send decision: "cancel". Workflows report a wider set of waitingReason values; see Creative Workflows.

#In durable workflows

An inline workflow (input.steps) starts immediately unless you ask for an estimate first. Submit with confirm_cost: false: a plan with a positive estimate returns 400 with details.estimatedCapacity and details.confirmationRequired: true, and nothing is saved. Show the estimate, then resubmit with confirm_cost: true (or without the field) to start. max_estimated_capacity_units applies either way.

A saved-template run (workflow_id + inputs) always returns 202 with waitingForCostApproval: true and a preview, and waits for POST /v1/creative-agent/workflows/:id/confirm-cost with decision: "confirm" and the echoed acceptedCostPreview.


#Per-tool cost metadata

Durable chat runs write a RunRecord (schema v2 — see Chat Completions → Replay Records). Each tool call inside a round carries optional cost_class and risk_level fields from a shared per-tool cost-metadata table, so UI clients can render cost/risk chips without re-deriving from raw arguments.

{
  "tool_calls": [
    {
      "id": "call_abc",
      "function": { "name": "generate_video" },
      "cost_class": "high",
      "risk_level": "medium"
    }
  ]
}

Sample classes: free (analysis/metadata), low (single image), medium (multi-image), high (video), vendor (external). The exact mapping is defined per tool and surfaced through the protocol package.


#Hard limits worth knowing

  • Video safety limit. Hosted tool execution blocks any single request that would generate more than 20 minutes of total video content across variations, long-video segments, or batch fan-out. Split larger jobs into multiple workflow runs.
  • Vision input cap. /v1/chat/completions accepts up to 20 vision images per request, each up to 10 MB and 1024 px on the longest side.
  • Context-image limits per model. GPT Image 2 edit accepts up to 16 context images; Qwen Image Edit 2511 accepts up to 3. The SDK and Sogni Socket enforce these before charging.
  • Per-account daily ceilings. Account-level rate and spend limits apply on top of any per-request controls; visible in the dashboard.

#Putting it together: a safe request pattern

For a chat run that may invoke paid media tools, send an Idempotency-Key header and this body:

{
  "model": "qwen3.6-35b-a3b-gguf-iq4xs",
  "messages": [
    { "role": "user", "content": "Make a 5-shot product teaser, 9:16, 15s." }
  ],
  "token_type": "spark",
  "runtime_config": { "requireJobConfirmation": true }
}

This gets you:

  • Spark billing (works for native and vendor models).
  • An explicit user approval step before any paid media work runs.
  • Idempotent retries — a network hiccup on submit won't start a second run.

For a workflow your app already planned (no LLM in the loop):

{
  "input": { "steps": [/* ... */] },
  "token_type": "spark",
  "max_estimated_capacity_units": 25,
  "confirm_cost": true
}

Your app already chose every step, so no LLM can add surprise work, and the hard cap rejects the plan before anything is saved if the estimate exceeds 25 units. To show the user a price first, send the same body with confirm_cost: false, read details.estimatedCapacity from the 400, then resubmit with confirm_cost: true.


#Where each control lives

Concept Where to read more
token_type request field, vendor-model gating Chat Completions → External Media Models
Durable chat run cost-approval flow Durable Chat Runs
Workflow max_estimated_capacity_units / confirm_cost Creative-Agent Workflows
Planner estimated_capacity_units / fits_budget Chat Completions → Workflow Planning
cost_class + risk_level on tool calls Chat Completions → Replay Records
Token-type enum + waiting-reason enum @sogni-ai/sogni-protocol
Last updated 2026-09-16