Skip to content

Managed Space LLM API

The Space LLM API lets an active confidential Space package use Arinova’s OpenRouter-backed model catalog without receiving a provider credential. The package exchanges its app secret for a one-hour client_credentials token with the llm scope, then calls POST /api/v1/space-llm/generate. This is a raw HTTP contract; the Space SDK intentionally has no wrapper in v1.

{
"system": "optional system instructions",
"input": "required prompt",
"jsonSchema": { "type": "object" },
"model": "anthropic/claude-haiku-4.5",
"maxOutputTokens": 4096,
"idempotencyKey": "one-stable-key-per-logical-call"
}

system and jsonSchema are each limited to 8 KiB; input is required and limited to 24 KiB. maxOutputTokens defaults to 4096 and cannot exceed the live SPACE_LLM_MAX_OUTPUT_TOKENS setting (8192 by default). Omitting model uses the database route for space_llm_generation, initially anthropic/claude-haiku-4.5. An explicit model must be available in the refreshed Expert Hub OpenRouter catalog. Both its input and output prices must be at or below the live 30 USD-per-million-token ceiling. Provider keys never cross the API boundary.

The returned text is provider output. It may contain Markdown fences; the platform deliberately does not strip or reinterpret them. The caller owns display parsing and schema-level validation.

The Space owner pays from non-withdrawable API credit, spending won points before paid points and never spending promotional bonus lots. Before the provider call, Arinova snapshots the selected model’s input/output rates and reserves the ceiling implied by estimated input, requested maximum output, and the live markup (2.0 by default). Successful calls settle against actual tokens using those immutable rate snapshots and refund the difference to the original paid/won pools. Provider errors and the 30-second timeout refund the entire reserve.

Reservation and daily-cap writes are one transaction. The per-owner UTC-day cap defaults to 50,000 points; reserve counts immediately and every refund releases the difference against the original spend day. A warning is emitted at 80 percent. The coarse HTTP limit is 600 requests/minute per IP and fails closed if Redis is unavailable; a second 600 requests/minute bucket is keyed by app. Usage billing and the owner daily cap are the authoritative abuse and spend controls.

idempotencyKey is scoped by Space and app. Reusing a key with another request returns a conflict. A completed request returns the stored response with replayed: true. A currently reserved request returns SPACE_LLM_REQUEST_IN_FLIGHT; retry the same key later. If a process or caller disconnects, the 30-second reaper refunds reservations older than 180 seconds, after which the same key and same body may execute and charge again.

Completed/refunded operation evidence retains the prompt SHA-256, first 2 KiB, response, rate snapshot, usage, and wallet linkage for 48 hours. Reserved rows are never removed by retention cleanup. llm_usage_events stores usage and attribution but no prompt or response body.

A successful response contains requestId, text, replayed, model, token usage and micro-USD cost, reserve/actual/refunded points, and current daily spend/cap values.

Authentication failures use the OAuth string form:

{"error":"invalid_token","errorCode":"OAUTH_INVALID_TOKEN"}

Rate-limit rejection includes a structured message and details:

{"error":{"code":"SPACE_LLM_RATE_LIMITED","message":"Too many requests","details":{"retryAfterMs":100}}}

All route policy and billing failures use a stable code object:

{"error":{"code":"SPACE_LLM_DAILY_CAP_EXCEEDED"}}

Important route codes include SPACE_LLM_DISABLED, SPACE_LLM_MODEL_NOT_ALLOWED, SPACE_LLM_ROUTE_UNCONFIGURED, SPACE_LLM_IDEMPOTENCY_CONFLICT, SPACE_LLM_REQUEST_IN_FLIGHT, SPACE_LLM_INSUFFICIENT_POINTS, SPACE_LLM_PROVIDER_ERROR, and SPACE_LLM_PROVIDER_TIMEOUT. Operators can filter the LLM usage audit by space_id and can disable the API per Space through the audited kill switch.

Quiz Battle never generates questions while opening or locking a live room. Its off-peak pool-refill cron asks for internally fact-checked batches of 12, omits model to use the vetted default route, and sends maxOutputTokens: 4096 so Chinese batches are not truncated. It maintains a 3,000-question target per language, applies a 30-day presentation exclusion, and limits generation to 50 outbound attempts per UTC day across both languages. Live rooms reserve 10 questions from that vetted pool, so all LLM spend and retry handling stays in the batch job rather than the wager hot path. Disconnects need no custom wallet repair because the reservation reaper performs the refund.

Build a7f47a5ca54ddcf7806cd48b81ce1b9827042766