API reference
Every endpoint of the OpenCharts REST API, generated live from the OpenAPI 3.1 spec so it always matches production. Authenticate with Authorization: Bearer oc_…. Create keys on the Developers hub.
https://www.opencharts.com/api/v190 operationsv1.0.0Meta
1 endpoint/openapi→ 200no authFetch this OpenAPI document.
Account
2 endpoints/account→ 200read scopeGet the authenticated account (whoami).
/account/credits→ 200read scopeGet the plan + AI credit balance.
Personas
12 endpoints/personas→ 200read scopeList personas.
/personas→ 201write scopeCreate a persona.
/personas/{id}→ 200read scopeGet a persona.
Path params: {id}
/personas/{id}→ 200write scopeUpdate a persona.
Path params: {id}
/personas/{id}→ 204write scopeDelete a persona.
Path params: {id}
/personas/{id}/publish→ 200write scopePublish a persona.
Path params: {id}
/personas/{id}/deployments→ 200read scopeList a persona's deployments.
Path params: {id}
/personas/{id}/deployments→ 201write scopeCreate a persona deployment.
Path params: {id}
/personas/{id}/conversations→ 201write scopeStart a persona conversation.
Path params: {id}
/personas/{id}/conversations/{cid}/end→ 200write scopeEnd a persona conversation.
Path params: {id}, {cid}
/personas/{id}/conversations/{cid}/events→ 200read scopeList conversation events.
Path params: {id}, {cid}
/personas/faces→ 200read scopeList available persona faces.
Projects
4 endpoints/projects→ 200read scopeList projects.
/projects/{id}→ 200read scopeGet a project.
Path params: {id}
/projects/{id}→ 200write scopeUpdate a project's metadata or content.
Path params: {id}
/projects/{id}→ 204write scopeDelete a project.
Path params: {id}
Creation
6 endpoints/flowcharts→ 201write scopeCreate a flowchart project.
/whiteboards→ 201write scopeCreate a whiteboard project.
/notes→ 201write scopeCreate a notes project.
/presentations→ 201write scopeCreate a presentation from a brief or outline. The slides generate in the background: poll the returned `job` (a deck_-prefixed id) at GET /jobs/{id} until it completes. Send an Idempotency-Key header to make retries safe.
namerequiredstringDeck title.
descriptionstringbriefstringPlain-language brief: audience, goal, key points. Theo authors the outline from it.
outlinearray<object>Explicit slides, used as written. Omit when passing `brief`.
slideCountintegerTarget slide count for a brief-authored outline (default 8). Ignored with `outline`.
themeIdstringBuilt-in theme id (for example `modern-dark`).
visualStylestringImage art style for the slides (default `illustration`).
slideModecreator | content`creator` (default): every slide is a fully designed image. `content`: editable text slides with image backgrounds.
brandUrlstringThe brand's homepage; the deck theme is extracted from it.
companyNamestringThe brand's display name (used with `brandUrl`).
tagsarray<string>/templates→ 200read scopeList templates.
/templates/{id}/use→ 201write scopeCreate a project from a template.
Path params: {id}
Sheets
10 endpoints/sheets→ 200read scopeList sheets.
/sheets→ 201write scopeCreate a sheet.
/sheets/{id}→ 200read scopeRead a sheet's rows.
Path params: {id}
/sheets/{id}/range→ 200read scopeRead a sheet range.
Path params: {id}
/sheets/{id}/ops→ 200write scopeApply or propose structured sheet ops.
Path params: {id}
opsrequiredarray<object>proposebooleansummarystring/sheets/{id}/edit→ 200ai scopeEdit a sheet with a natural-language instruction.
Path params: {id}
instructionrequiredstringproposeboolean/sheets/{id}/commits→ 200read scopeList a sheet's commits.
Path params: {id}
/sheets/{id}/commits/{commitId}→ 200read scopeGet a commit diff.
Path params: {id}, {commitId}
/sheets/{id}/commits/{commitId}/resolve→ 200write scopeMerge or reject a proposed commit.
Path params: {id}, {commitId}
/sheets/{id}/rollback→ 200write scopeRoll a sheet back to a commit.
Path params: {id}
Boards
10 endpoints/boards→ 200read scopeList boards.
/boards→ 201write scopeCreate a board.
/boards/{id}→ 200read scopeGet a board.
Path params: {id}
/boards/{id}→ 200write scopeUpdate a board.
Path params: {id}
/boards/{id}→ 204write scopeDelete a board.
Path params: {id}
/boards/{id}/duplicate→ 201write scopeDuplicate a board.
Path params: {id}
/boards/{id}/cards→ 200read scopeList a board's cards.
Path params: {id}
/boards/{id}/cards→ 201write scopeCreate a card.
Path params: {id}
/boards/{id}/cards/{cardId}→ 200write scopeUpdate a card.
Path params: {id}, {cardId}
/boards/{id}/cards/{cardId}→ 204write scopeDelete a card.
Path params: {id}, {cardId}
Chat
1 endpoint/chat→ 200ai scopeRun a Theo chat turn (UI-message SSE stream by default; `stream: false` for the assembled message + artifacts).
conversationIdstringContinue an existing conversation (prior history loads server-side).
messagestringThe prompt (shorthand for a single user message).
messagesarray<object>modefast | thinkv1 launch modes.
streambooleantrue = AI SDK UI-message SSE stream; false = assembled JSON message + artifacts.
Conversations
6 endpoints/conversations→ 200read scopeList conversations.
/conversations→ 201write scopeCreate a conversation.
titlestringmodestringChat mode preference (e.g. `fast`, `think`).
/conversations/{id}→ 200read scopeGet a conversation.
Path params: {id}
/conversations/{id}→ 200write scopeUpdate a conversation's title or mode.
Path params: {id}
titlestringmodestringChat mode preference (e.g. `fast`, `think`).
/conversations/{id}→ 204write scopeDelete a conversation and its messages.
Path params: {id}
/conversations/{id}/messages→ 200read scopeList a conversation's messages (oldest first).
Path params: {id}
Media
7 endpoints/extract-flowchart→ 201ai scopeExtract a flowchart project from a document (PDF, PPTX, or image).
documentBase64requiredstringBase64-encoded document content (no `data:` prefix). Max 10MB decoded.
mimeTyperequiredapplication/pdf | application/vnd.openxmlformats-officedocument.presentationml.presentation | image/png | image/jpeg | image/webpfileNamestringOriginal file name (used as a title fallback).
/images→ 200ai scopeGenerate an image synchronously.
promptrequiredstringengineauto | theo-creative | theo-imagine | theo-photo | theo-rapid | theo-design | …Defaults to theo-ultra (highest fidelity) on paid plans; a Free-plan call falls back to theo-creative and the response carries a `notes` entry saying so. An explicit theo-ultra on Free returns 403. grok-theo (Grok + Theo, the xAI co-brand) is accepted only on workspaces that enable it; elsewhere it resolves to the default.
aspectRatio1:1 | 16:9 | 9:16 | 4:3 | 3:4stylestringreferenceImageUrlsarray<string>includeBase64boolean/images/moonlight→ 200ai scopeGenerate an image on Theo Moonlight, our own image model, synchronously (safe-for-work only; seedable; renders at the published defaults unless told otherwise).
promptrequiredstringThe scene: subject, composition, lighting, style.
negativePromptstringNot used by this model: leave it out or send an empty string. A non-empty value is refused with a 400. Every render still goes through the endpoint's safety checks; they are not a knob.
aspectRatio1:1 | 3:4 | 4:3 | 2:3 | 3:2 | 9:16 | …Frame shape. Every shape renders at about one megapixel; the response reports the exact width and height.
seedintegerFix the seed to reproduce a render. Omit for a random one; the response echoes the seed used.
/videos→ 202ai scopeStart a video generation job (pick a tier: Fast, Max, Cinema, or Cinema Ultra).
promptrequiredstringThe scene: subject, action, camera, setting, mood.
aspectRatio21:9 | 16:9 | 4:3 | 1:1 | 3:4 | 9:16Frame shape. Default 16:9. The standard tiers render 16:9 and 9:16 only; another shape resolves to the NEAREST ratio they do render.
modelTierfast | max | cinema | cinema-ultraWhich tier renders the clip. A premium tier on a workspace that has not enabled it lands on `max` with a note saying so.
resolution480p | 720p | 1080p | 4kOutput quality. Premium tiers only; the standard tiers always render 1080p. `cinema` reaches 4k, and `cinema-ultra` tops out at 720p (it trades quality for length).
clipSecondsintegerClip length in seconds. Premium tiers only; the standard tiers render a fixed ~8s clip. `cinema` reaches 15s and `cinema-ultra` 30s; over a tier's ceiling clamps to its longest.
referenceImageUrlsarray<string>ONE url = the opening frame to animate (image-to-video). SEVERAL = how the subject should look. Trimmed to the resolved tier's own budget (1 on the standard tiers, 9 on Cinema, 30 on Cinema Ultra); when only one survives it becomes the opening frame.
referenceImageUrlstringSingle first-frame anchor. Kept for compatibility; prefer `referenceImageUrls`. Passing both puts this one first.
/songs→ 202ai scopeStart a song generation job.
stylerequiredstringtitlestringlyricsstringautoLyricsbooleaninstrumentalbooleantierauto | premium | fast/code-builds→ 202ai scopeStart a Code Canvas build job.
promptrequiredstringnamestringframeworkhtml | react | autoplanFirstboolean/real-world/spaces→ 202ai scopeStart a Theo Real World space job: photos of ONE room (up to 8, or one short video of it) become an imagined, walkable 3D room at real scale (a quick room in about a minute, the full room a few minutes later); a single photo alone becomes a measured, imagined, walkable space (a 360 preview with the room's dimensions, then a walkable .spz).
imageUrlstringONE photo, as an http(s) URL the server can fetch (JPEG, PNG or WebP). Wide and level, with the floor and a wall in view. Send this OR `imageUrls` OR `videoUrl`.
imageUrlsarray<string>One to 8 photos of ONE room (JPEG, PNG or WebP), http(s) URLs the server can fetch, in the order they were taken: stand in one spot and turn between shots so each overlaps the last. Needs the room engine; a deployment without it answers 503.
videoUrlstringOne video of the room (MP4, MOV or WebM; 30 seconds and 100 MB or less), an http(s) URL the server can fetch: one slow, level sweep from one spot. The whole input; do not send photos beside it. Needs the room engine; a deployment without it answers 503.
qualitypreview | full`preview` stops at the first tier: the quick room (about 90 seconds; walkable in its own units), or the 360 panorama plus the dimensions on a one-photo space. `full` (default) continues to the final tier: the full room at real scale (about 8 minutes in total) or the walkable space (about 3 minutes), and costs more.
titlestringOptional label for the job; a room also reads it as a short description of the room.
Jobs
1 endpoint/jobs/{id}→ 200read | ai scopePoll any async job by its type-prefixed id.
Path params: {id}
Voice
4 endpoints/speech→ 200ai scopeSpeak one short line in one of Theo's voices, streamed back as raw 24 kHz mono PCM while it is produced. Send a longer reply one sentence per call. Charged per character spoken.
textrequiredstringOne sentence or short line to speak. Speak a longer reply one sentence per call.
voicestringA voice name from GET /speech/voices, such as `lucius`. Omit (or send `default`) for Theo's default voice.
/speech/voices→ 200read | ai scopeList Theo's voices by name (pass one as `voice` to POST /speech).
/live/session→ 200ai scopeOpen a real-time voice conversation with Theo. Returns a short-lived token, locked to the persona, tools, voice and turn detection, that you connect to the realtime engine with to stream microphone audio up and Theo's voice back. A new session is charged once; resuming a dropped one with its handle is free.
personaaibiWho Theo is in the conversation. `aibi` (the default) is Theo talking through the AIBI desk robot.
voicestringA voice name from GET /speech/voices, such as `lucius`. Omit (or send `default`) for Theo's default voice.
resumptionHandlestringThe latest resumption handle a dropped session sent. The new token continues that conversation and is not charged again.
deviceNamestringThe robot's own name (for example `AIBI-133E`), so Theo can refer to it.
ambientbooleanAn always-listening session: Theo stays quiet unless someone speaks to him. Needs `features.proactiveAudio`; refused with a 400 when the deployment's engine lacks it.
/live/action→ 200ai scopeRun one of a live conversation's server tools (search, presentations, notes, flowcharts, whiteboards, images, projects, account, memory). Returns the line to hand back to the conversation and what was made. Charged per tool, only when it succeeds.
actionrequiredsearch_web | create_presentation | create_notes | create_flowchart | create_whiteboard | generate_image | …The server tool the conversation called. Tools the caller runs itself (the robot's own) never come here.
paramsobjectThe call's arguments, exactly as the conversation produced them.
Agents
8 endpoints/chat/completions→ 200ai scopeOpenAI-compatible chat completion over a Theo Agent (`model: "agent:<agentId>"`; the agent runs inline and replies, as JSON or as a single-chunk SSE stream when `stream: true`).
modelrequiredstring`agent:<agentId>`.
messagesrequiredarray<object>streambooleanfalse (default): the assembled JSON. true: an SSE stream with keepalive comments while the agent works, then one `chat.completion.chunk` with the full reply and `[DONE]`.
userstringOptional end-user id; doubles as the thread when `metadata.thread_id` is absent.
metadataobject/agents/{id}/run→ 202ai scopeDispatch a Theo Agent.
Path params: {id}
/agents/{id}/messages→ 202ai scopeSend a message to a goal-driven agent on a conversation thread (returns the run id to poll for the reply).
Path params: {id}
messagerequiredstringthreadIdstringCaller-chosen conversation handle (letters, digits, `_ . : -`).
/agents/{id}/runs→ 200read scopeList an agent's runs.
Path params: {id}
/agents/{id}/runs/{runId}→ 200read scopeGet an agent run's status.
Path params: {id}, {runId}
/agents/{id}/runs/{runId}/result→ 200read | ai scopeGet an agent run's result: the reply, artifacts, pending approval, credits.
Path params: {id}, {runId}
/agents/{id}/runs/{runId}/approve→ 202ai scopeApprove or reject the action a paused agent run is waiting on (owner only).
Path params: {id}, {runId}
decisionrequiredapprove | rejectreviewTokenrequiredstringscopeonce | task | alwaysUse only a scope offered in the review. Reject always uses once.
/agents/{id}/usage→ 200read scopeGet an agent's run + credit usage over a window (`?days=7`, 1 to 90).
Path params: {id}
Research
2 endpoints/research→ 202ai scopeStart a deep-research job (returns a res_-prefixed job id; poll it at GET /jobs/{id}).
queryrequiredstring/research/{jobId}→ 200read | ai scopePoll a deep-research job (legacy alias of GET /jobs/{id}; accepts raw or res_-prefixed ids).
Path params: {jobId}
Drive
10 endpoints/drive/files→ 200drive:read scopeList Drive files plus the folder tree and storage meter (filters: folderId, kind, starred, trash, teamId, limit).
/drive/files→ 201drive:write scopeSave a new file into Drive as text (content) or any format as base64 (contentBase64, up to 4 MB).
namerequiredstringFile name WITH extension (e.g. `Q3 summary.md`, `chart.png`).
contentstringUTF-8 text content (text formats only).
contentBase64stringBase64-encoded bytes for any format, no `data:` prefix (4 MB decoded max).
folderIdstringDestination Drive folder id; omit or `root` for the Drive root.
descriptionstringtagsarray<string>/drive/files/{id}→ 200drive:read scopeGet a Drive file's metadata plus a time-limited downloadUrl for its raw bytes.
Path params: {id}
/drive/files/{id}→ 200drive:write scopeRename, move, star, tag or describe a Drive file.
Path params: {id}
namestringfolderIdstringDestination Drive folder id, or `root`.
isStarredbooleantagsarray<string>descriptionstring/drive/files/{id}/content→ 200drive:read scopeRedirect (302) to a signed, time-limited stream of the file's raw bytes; follow redirects to download.
Path params: {id}
/drive/files/{id}/text→ 200drive:read scopeRead a Drive file as text (PDF and Office documents are parsed), capped at about 12K characters.
Path params: {id}
/drive/files/bulk→ 200drive:write scopeMove, star, unstar, trash (30-day restore) or restore many Drive files at once.
fileIdsrequiredarray<string>actionrequiredmove | star | unstar | trash | restoretrash is the destructive floor (30-day restore); there is no permanent delete.
folderIdstringFor move: the destination Drive folder id, or `root`.
/drive/search→ 200drive:read scopeSearch Drive by name, tag, description, shared files and the text inside documents (q, kind, limit, teamId).
/drive/folders→ 200drive:read scopeList the Drive folder tree.
/drive/folders→ 201drive:write scopeCreate a Drive folder, optionally inside another Drive folder.
namerequiredstringparentFolderIdstringParent Drive folder id; omit for the Drive root.
colorstringOptional hex color.
stickerstringOptional single emoji.
Webhooks
6 endpoints/webhooks→ 200read scopeList webhooks.
/webhooks→ 201write scopeRegister a webhook (secret returned once).
namerequiredstringurlrequiredstringeventsrequiredarray<project.created | project.updated | project.shared | project.deleted | team.member_added | team.member_removed | …>Event names to subscribe to. The enum lists every currently emitted event (generated from the same registry the dispatcher validates against); unknown or not-yet-emitted events return a 400 naming them. Naming note: the `code` job type (job ids `code_...`) emits `code_build.*`, and Theo Agent runs emit `evi.run_*`.
/webhooks/{id}→ 200read scopeGet a webhook.
Path params: {id}
/webhooks/{id}→ 200write scopeUpdate a webhook.
Path params: {id}
namestringurlstringeventsarray<project.created | project.updated | project.shared | project.deleted | team.member_added | team.member_removed | …>isActiveboolean/webhooks/{id}→ 204write scopeDelete a webhook.
Path params: {id}
/webhooks/{id}/deliveries→ 200read scopeList a webhook's recent deliveries.
Path params: {id}
{ data, total, cursor? }; async work returns a job envelope polled at GET /jobs/{id}; errors are { error: { code, message, requestId } }. Register webhooks on the Developers hub to skip polling.