HTTP API for the content platform. Discovery endpoints are public. Customer-facing state-changing endpoints require a valid session and are same-origin protected; the billing webhook is instead authenticated by its payment-provider signature and is not callable by customers. This reference is generated from the live OpenAPI document, available at /api/openapi.json. Version 1.0.0.
Operations
Public infrastructure endpoints (health/liveness probes).
get/api/health
Liveness and readiness probe
Returns process uptime, server time, presence-only environment readiness booleans (never any secret values) and a bounded database reachability check. 200 when ready, 503 when not configured or the database did not answer. Not cached.
Authentication: None. Public endpoint.
Responses
200Ready: configured and the database answered.
503Degraded: required env missing or the database did not answer in time.
Discovery
Public, unauthenticated metadata endpoints for crawlers and integrations.
get/llms.txt
Machine-readable product description
Returns the llms.txt product overview (capabilities, data handling, plans, discovery links) as plain text for AI crawlers. Cacheable.
Authentication: None. Public endpoint.
Responses
200Plain-text product description.
get/agents.json
Agent preset catalog
Returns the agent preset catalog (preset and agent shape: id, name, expertise, tier, dependencies). Discovery metadata only; internal prompt text is not disclosed. Cacheable.
Authentication: None. Public endpoint.
Responses
200Agent preset catalog.
get/api/openapi.json
This OpenAPI document
Returns this OpenAPI 3.1 document describing the platform API. Cacheable.
Authentication: None. Public endpoint.
Responses
200OpenAPI 3.1 document.
Ingestion
Session-authenticated document ingestion.
post/api/ingest
Ingest a reference document
Accepts a reference document for the authenticated caller in one of two modes: a multipart file upload (DOCX, TXT, or MD; parsed in-process) or a JSON body containing an already client-parsed document (PDF text). Each successful ingest is written to the append-only audit log. Returns the canonical parsed document.
Authentication: Session cookie required (same-origin enforced for state-changing requests).
Request body (application/json)
Field
Type
Required
Description
parsed
object
Required
Canonical parsed-document contract. No PHI in the default configuration.
filename
string
Optional
Optional original filename for the audit record.
Request body (multipart/form-data)
Field
Type
Required
Description
file
string (binary)
Required
DOCX, TXT, or MD file (max 10 MB). PDF must be posted as JSON instead.
Responses
200Document ingested. Returns the canonical parsed document.
400Invalid JSON body, missing file field, or unexpected content type.
401No valid session.
403Request failed the same-origin check.
413Uploaded file exceeds the 10 MB limit.
415Unsupported format, or a PDF was uploaded via multipart (post it as JSON instead).
422Parsed-document body failed schema validation (invalid_request, with issues), or the file could not be parsed (unparseable_document, with a reference).
429Rate limit exceeded (rate_limited, per-user or per-tenant). Includes a Retry-After header.
500Internal parse contract violation.
Compliance
Session-authenticated, admin-gated organization data export.
post/api/compliance/export
Export organization data (right of access)
Returns a JSON snapshot of the caller's organization (members, plan/usage summary, productions, audit history) as a downloadable attachment. Admin/owner only; tenant-scoped; rate-limited; the export itself is recorded in the audit log. No PHI in the default configuration. POST (not GET) because it writes one audit row.
Authentication: Session cookie required (same-origin enforced for state-changing requests).
Responses
200A JSON data export (Content-Disposition: attachment).
401No valid session.
403No active membership, insufficient role (admin/owner required), or failed same-origin check.
429Rate limit exceeded (rate_limited). Includes a Retry-After header.
500Export could not be generated (opaque reference returned).
Chat
Session-authenticated chat answered by the multi-agent system, streamed as NDJSON.
post/api/chat
Ask the multi-agent system (streamed)
Answers the conversation with the chosen model and streams the answer as newline-delimited JSON events (see ChatStreamEvent). Session-authenticated, same-origin (CSRF) enforced, rate-limited per user and per tenant, one request in flight per user, and admitted by the spend gate (plan, Free daily cap, monthly allowance) before any model call; an unreadable plan or meter refuses with 503 rather than guessing. Checks run in this order: session (401), same-origin (403), per-user rate limit (429), JSON (400), schema (422), item risk (403), protected health information (422), model (400), active membership (403), per-tenant rate limit (429), in flight (429), spend gate (403/429/402/503), then the team and compare gates. Once the stream opens the status is 200; a later failure arrives as an error event. Aborting the request aborts the upstream model call; aborted calls are still metered.
Authentication: Session cookie required (same-origin enforced for state-changing requests).
Request body (application/json)
Field
Type
Required
Description
modelId
string
Required
Model id from the governed registry. Unknown ids are refused 400; models above the caller's plan 403.
messages
array of object
Required
Conversation so far, oldest first. Ignored when itemRisk is present (see itemRisk).
webSearch
boolean
Optional
Request native web search; honored only when the chosen model advertises the capability.
multiAgent
boolean
Optional
Review mode: draft, critique and refine with the same model.
team
object
Optional
Team mode. agentModels is a proposal only: every entry is re-checked against the caller's plan before the stream opens.
compare
object
Optional
Compare mode. Duplicate ids are collapsed before the plan and daily-limit checks; every model must be in the caller's plan or the whole request is refused.
itemRisk
object
Optional
Agent-Runner item execution only. When present, the server re-classifies the item and may refuse it (403), and the prompt sent to the model is rendered from kind/title/body alone - messages is not sent.
Responses
200The answer stream: application/x-ndjson; charset=utf-8, one ChatStreamEvent per line, never cached.
application/x-ndjson, one of:
phase: channel, label (optional), modelId (optional) - A phase (or agent, or compared model) starts on a channel.
delta: channel, text - A chunk of model text for a channel.
step_error: channel, message - One team agent or one compared model failed; the run continues. The message is a fixed, safe string (never provider exception text).
failover: channel, fromLabel, toLabel, modelId - The chosen model's provider failed before answering and another model in the caller's plan is answering instead. Labels only, never the error text.
done - Terminal: the run completed.
aborted - Terminal: the client aborted the request (the aborted calls are still metered).
error: message, reference, retryAfterSec (optional) - Terminal: the run failed after the stream opened. The HTTP status is still 200; this event carries the failure.
400Invalid JSON body; unknown or unavailable model (also for a team agent or a compared model); team and multiAgent together; compare with team or multiAgent; unknown team.
401No valid session.
402This month's included usage is used up and pay-as-you-go is not enabled (monthly allowance gate).
403Failed same-origin check; the itemRisk item was refused by the server-side risk gate; no active tenant membership; the model (or a team agent's or a compared model) is not in the caller's plan; no model in the caller's plan can run the team.
422Body failed schema validation (issues lists the Zod messages); or a user turn contains a Social Security number, medical record number or date of birth (phi_detected: the detail names the kinds, never the values; nothing is sent to a model or metered).
429RFC 9457 problem. code rate_limited (per-user or per-tenant burst limit; Retry-After is the wait), request_in_flight (the caller's previous message is still running; no Retry-After), daily_limit_reached (Free daily cap; Retry-After is the time to 00:00 UTC) or compare_exceeds_daily_limit (a compare needs more of today's messages than are left; Retry-After is the time to 00:00 UTC).
503The plan, the daily count or the monthly spend could not be read, so nothing was run.
Design
Session-authenticated asynchronous video generation (submit, poll, then stream the finished video).
get/api/design/video
Poll a video-generation job
Returns the current state of a job the caller submitted. Session-authenticated; read-only, so no same-origin check and no rate limit (a poller fires every few seconds by design). A job id the caller did not submit, or one older than the 24-hour ownership window, is answered 404 so its existence is never confirmed. The poll goes to the provider the job was submitted to. A failed job reports a fixed, general message; a completed job's createdAt is when it was submitted, and its video.url is the platform's content address for the job (GET /api/design/video/content). Job ownership is not shared across server instances: poll soon after submitting, and treat a 404 for a job you did submit as the job being no longer pollable.
Authentication: Session cookie required (same-origin enforced for state-changing requests).
Parameters
Name
In
Type
Required
Description
jobId
query
string
Required
The jobId from the submit response.
provider
query
string
Optional
Optional: the provider from the submit response. Omitted, the job's own provider is used. A provider that is not registered is answered 400; one other than the job's own provider is answered 404, as for an unknown job.
Responses
200Current job state.
400jobId is missing, empty or longer than 256 characters, or provider is not a registered provider.
401No valid session.
404No job with this id was submitted by the caller (or its ownership record expired), or provider is not the provider the job was submitted to.
422A provider moderation refusal while polling. The handler maps it, but the current provider adapter never raises one on a poll.
500Unexpected failure checking the job (reference returned).
502The video provider failed or returned an unusable response.
503Video generation is not configured.
post/api/design/video
Submit a video-generation job
Submits an asynchronous video render and returns a handle to poll with GET. Session-authenticated, same-origin (CSRF) enforced, active tenant membership required, rate-limited per user and per tenant, one submit in flight per user, and subject to the Free daily video cap (fails closed: an unreadable count is a 503). The prompt is screened by the safety pre-filter before it can reach a provider. One usage row is recorded per accepted submit. Checks run in this order: session (401), same-origin (403), per-user rate limit (429), membership (403), per-tenant rate limit (429), body size (413), JSON (400), schema (422), safety filter (422), in flight (429), daily cap (429/503), provider. A body that fails the size check, JSON, schema or the safety filter is refused before the in-flight check and the daily-cap read.
Authentication: Session cookie required (same-origin enforced for state-changing requests).
Request body (application/json)
Field
Type
Required
Description
prompt
string
Required
Length is checked after trimming whitespace.
aspect
enum: 1:1 | 16:9 | 9:16 | 4:3 | 3:4
Optional
durationSeconds
integer
Optional
Requested clip length in whole seconds, 1-30. A length outside that range is refused 422 rather than shortened. The provider renders the nearest length it supports.
model
string
Optional
Model id within the provider (checked after trimming). A model the provider does not declare is refused 400.
Responses
202Job accepted.
400The body is not valid UTF-8 JSON, or names a model the provider does not declare.
401No valid session.
403Failed same-origin check, or no active tenant membership.
404Video generation is not available: it is out of launch and its server-side switch is off. Nothing is consumed.
413The body (by its declared Content-Length, or by the bytes actually read) exceeds 13744 bytes, the largest body the schema can accept.
422Body failed schema validation (issues lists the Zod messages), or the prompt was refused by the safety filter (error carries the user-facing reason).
429RFC 9457 problem. code rate_limited (per-user or per-tenant; Retry-After is the wait), request_in_flight (the caller's previous submit is still running; no Retry-After) or daily_limit_reached (Free daily video cap; Retry-After is the time to 00:00 UTC).
500Unexpected failure starting the job (reference returned).
502The video provider failed or returned an unusable response.
503Video generation is not configured, or the daily usage count could not be read.
get/api/design/video/content
Stream a completed video
Streams the video of a completed job the caller submitted; a completed poll returns this address as result.video.url. Session-authenticated; read-only, so no same-origin check and no rate limit (a media element makes several requests per play). The platform reads the video from the provider server-side, so no provider address or credential reaches the caller. Authorized exactly as the poll is: a job the caller did not submit, one older than the 24-hour ownership window, or one asked for under another provider is answered 404. A job that no poll has yet reported completed is answered 409. The body is streamed, not buffered, with the provider's video media type (parameters dropped); any other media type is refused 502. One byte range (bytes=first-last, bytes=first- or bytes=-suffix) is passed on to the provider, and any other Range value is ignored, so the whole video is sent. The answer is 206 only when the provider itself answers the range; a provider that does not support ranges sends the whole video (200), and seeking is then limited to what the browser has already loaded. Job ownership is not shared across server instances, as for the poll.
Authentication: Session cookie required (same-origin enforced for state-changing requests).
Parameters
Name
In
Type
Required
Description
jobId
query
string
Required
The jobId from the submit response.
provider
query
string
Optional
Optional: the provider from the submit response. Omitted, the job's own provider is used. A provider that is not registered is answered 400; one other than the job's own provider is answered 404.
Range
header
string
Optional
Optional: one byte range. Several ranges, another unit or a reversed range are ignored.
Responses
200The whole video.
206The requested byte range, as the provider answered it.
400jobId is missing, empty or longer than 256 characters, or provider is not a registered provider.
401No valid session.
404No job with this id was submitted by the caller (or its ownership record expired), or provider is not the provider the job was submitted to. One body for all three, so a job's existence is never confirmed.
409The job exists but no poll has reported it completed yet.
416The provider cannot satisfy the requested range.
500Unexpected failure loading the video (reference returned).
502The provider failed, did not answer in time, redirected, answered something other than a video media type, or sent malformed range or encoding headers. A fixed message is returned; the provider's own text is not.
503Video generation is not configured.
Billing
Payment-provider webhooks. Server-to-server and signature-authenticated; not callable by customers.
post/api/billing/webhook
Stripe webhook receiver (not callable by customers)
Server-to-server endpoint called by Stripe; not callable by customers and not part of the customer API. There is no session and no same-origin check: the Stripe-Signature header (HMAC-SHA256 over the raw body with the platform webhook secret, 300-second replay window) is the authentication, and it is checked before the body is parsed or logged. DORMANT: a verified event is logged (metadata only) and acknowledged; no plan or entitlement change is made. Every event type is acknowledged the same way - there is no unknown-event refusal.
Authentication: Stripe-Signature header (server-to-server webhook; not callable by customers).
Parameters
Name
In
Type
Required
Description
Stripe-Signature
header
string
Required
Stripe's t=<timestamp>,v1=<signature> header. Missing or invalid: 400.
Request body (application/json)
Field
Type
Required
Description
id
string
Required
object
string
Required
type
string
Required
Any Stripe event type; none is refused.
created
number
Required
livemode
boolean
Required
api_version
string | null
Optional
data
object
Required
Responses
200Verified and acknowledged (any event type).
400The body could not be read or is not valid UTF-8; the signature is missing, invalid or outside the replay window (signature verification failed); or a verified body is not a well-formed Stripe event (malformed event payload, reference returned).
413The body (by its declared Content-Length, or by the bytes actually read) exceeds 1 MiB.
503Webhook verification is not configured on the server (reference returned).