Reference
Reference
Section titled “Reference”The real surface area of the Chat service: its HTTP endpoints, the streaming event shapes, the models and tools it exposes, and the configuration operators use to run it.
Authentication
Section titled “Authentication”Every product endpoint is gated by your single sign-on session. After you sign in at account.destesi.io and open Chat, the service redeems a one-time token and sets a host-only chat_session cookie. Each request validates that cookie against Accounts; an invalid or missing session returns 401.
| Endpoint | Method | Purpose |
|---|---|---|
/healthz |
GET | Liveness — returns ok. |
/readyz |
GET | Readiness — 200 when an AI provider is wired, 503 degraded otherwise. |
/v1/sso/callback |
GET | SSO callback; redeems the exchange token and sets chat_session. |
/v1/auth/me |
GET | The current user (id, email, name). |
/v1/auth/logout |
POST | Clears the session cookie. Idempotent. |
/v1/me/products |
GET | Your launcher entitlements (proxied from Accounts). |
/v1/chat |
POST | The main agentic chat stream. |
/v1/chat/approvals/{tool_use_id} |
POST | Approve or cancel a paused tool, resuming the stream. |
/v1/models |
GET | The list of selectable models. |
Conversation history
Section titled “Conversation history”Server-side conversation storage. Every route re-checks that the conversation belongs to the calling (workspace, user) pair and answers 404 for anything else, so a conversation id from another workspace is indistinguishable from one that doesn’t exist. If no store is configured, all four return 503 persistence_disabled.
| Endpoint | Method | Purpose |
|---|---|---|
/v1/conversations |
GET | List your conversations, newest first. limit (default 30, max 100) and a before cursor. |
/v1/conversations/{id}/messages |
GET | One page of messages. limit (default 30, max 100) and a before cursor; pages ascend within themselves. |
/v1/conversations/{id} |
PATCH | Rename a conversation. |
/v1/conversations/{id} |
DELETE | Delete a conversation and its messages. |
Attachments and uploads
Section titled “Attachments and uploads”| Endpoint | Method | Purpose |
|---|---|---|
/v1/uploads |
POST | Upload a file from the composer (up to 50 MiB) and register it in Drive, returning a drive_file_id you can attach. |
/v1/attachments/{drive_file_id}/content |
GET | Fetch an attachment’s content for display in the conversation. |
POST /v1/chat
Section titled “POST /v1/chat”The primary endpoint. It always runs the agentic loop and streams the reply back as newline-delimited JSON (Content-Type: application/x-ndjson) — one JSON object per line.
Request body
Section titled “Request body”{ "model": "global.anthropic.claude-haiku-4-5-20251001-v1:0", "conversation_id": "conv_abc", "client_id": "msg_local_42", "messages": [ { "role": "user", "content": "Generate an image of a robot mascot." } ], "attachments": [ { "drive_file_id": "file_abc", "name": "spec.md" } ]}messages(required) — the conversation so far. Each message has arole(user,assistant, orsystem) andcontent.contentis either a JSON string (plain text) or a JSON array of content blocks (for replaying prior tool calls and results). An emptymessagesarray returns400 missing_messages.attachments(optional) — Drive files to fold into the conversation as context. Each needs adrive_file_id;nameis echoed back for display.model(optional) — overrides the default model for this request.conversation_id(optional) — the conversation this turn belongs to. Supply it to append to an existing thread; omit it and a new conversation is created and announced in the stream. A conversation id that isn’t yours is rejected with404before the stream opens.client_id(optional) — your own idempotency key for this user message. A retried POST carrying the sameclient_idupdates the same stored row instead of duplicating it.
Response: streamed events
Section titled “Response: streamed events”Each line is one event, keyed by type:
{"type":"conversation","id":"conv_abc","title":"New chat"} // emitted only when this turn CREATED a conversation{"type":"attachments","items":[ ... ]} // per-attachment status (ok | truncated | attached | skipped){"type":"text","delta":"..."} // a chunk of the assistant's reply{"type":"tool_call","tool":"generate_image","input":{ ... }} // the model invoked a tool{"type":"tool_result","tool":"generate_image","result":{ ... }}// the tool's output{"type":"tool_result","tool":"...","error":"..."} // a tool failed{"type":"tool_approval_needed","tool_use_id":"...","tool":"gmail_send_email","input":{ ... },"editable_fields":[ ... ],"expires_at":"..."}{"type":"done","reply":"..."} // terminal — full assistant text{"type":"done","paused_for_approval":true,"tool_use_id":"..."} // terminal — waiting on an approvalIf no suite tools are configured for the workspace, the stream is simply a series of text events followed by done — there are no tool_call or tool_result events.
The conversation frame arrives first when the turn created a new conversation; echo that id back as conversation_id on every subsequent turn in the thread. When you already sent a conversation_id, no conversation frame is emitted.
POST /v1/chat/approvals/{tool_use_id}
Section titled “POST /v1/chat/approvals/{tool_use_id}”When an action requires approval (sending email, posting to GitHub), /v1/chat ends with done carrying paused_for_approval: true and a tool_use_id. To resume, POST your decision:
{ "decision": "approve", "input": { "subject": "Edited subject", "body": "Edited body" } }decision—"approve"or"cancel".input(optional) — the edited values for the tool’seditable_fields. Omit to approve the draft as-is.
The response is another NDJSON stream carrying the tool result and the assistant’s follow-up. A pending approval is durable (stored, so it survives a reload or a service restart), single-use, and has a 30-minute TTL; a resume for an expired or already-resumed approval returns 404.
The tools Chat exposes are determined per-workspace by which products and connections are wired: a family whose dependency is unwired is omitted from the registry entirely, so the model never sees it and never promises it. With everything wired the registry is 47 tools.
The agentic loop is bounded — the model gets a limited number of tool-call rounds before it must produce a final answer.
Images and voice
Section titled “Images and voice”| Tool | What it does | Requires approval |
|---|---|---|
generate_image |
Generate an image (512 / 768 / 1024 px). | No |
generate_audio |
Synthesize speech with a Text-to-Speech voice. | No |
elevenlabs_list_voices |
List the voices in your connected ElevenLabs account. | No |
elevenlabs_generate_audio |
Synthesize speech with an ElevenLabs voice. | No |
| Tool | What it does | Requires approval |
|---|---|---|
gmail_search |
Search the inbox (Gmail query syntax). | No |
gmail_read_email |
Read a message by ID. | No |
gmail_send_email |
Compose and send an email. | Yes |
GitHub
Section titled “GitHub”| Tool | What it does | Requires approval |
|---|---|---|
github_list_repos |
List accessible repositories. | No |
github_read_path |
Read a file or list a directory. | No |
github_search_code |
Search code across accessible repos. | No |
github_search_issues |
Search issues and pull requests. | No |
github_get_issue |
Read an issue or PR with comments. | No |
github_get_pr_diff |
Get a pull request’s unified diff. | No |
github_create_comment |
Comment on an issue or PR. | Yes |
github_create_issue |
Open a new issue. | Yes |
github_edit_issue |
Edit, close, or reopen an issue. | Yes |
| Tool | What it does | Requires approval |
|---|---|---|
slack_list_channels |
List channels the connected account can see. | No |
slack_read_channel |
Read recent messages in a channel. | No |
slack_send_message |
Post a message to a channel. | Yes |
Linear
Section titled “Linear”| Tool | What it does | Requires approval |
|---|---|---|
linear_search_issues |
Search issues. | No |
linear_get_issue |
Read one issue. | No |
linear_create_issue |
Create an issue. | Yes |
linear_update_issue |
Update an issue. | Yes |
| Tool | What it does | Requires approval |
|---|---|---|
drive_list_files |
List files in your Drive library. | No |
drive_read_file |
Read a Drive file’s text content. | No |
drive_get_download_url |
Mint a short-lived download URL for a Drive file. | No |
Build status
Section titled “Build status”| Tool | What it does | Requires approval |
|---|---|---|
snap_list_branches |
List Snap branches. | No |
snap_branch_status |
Read one Snap branch’s status. | No |
review_status |
Read the code-review status of a pull request. | No |
Both Snap tools are read-only; Chat never mutates a Snap branch.
| Tool | What it does | Requires approval |
|---|---|---|
whatsapp_list_conversations |
List customer conversations. | No |
whatsapp_read_conversation |
Read one conversation. | No |
Promo and video production
Section titled “Promo and video production”| Tool | What it does | Requires approval |
|---|---|---|
capture_app |
Screenshot a live, already-running web app plus its on-screen regions. | No |
compose_promo |
Assemble captured frames into a re-renderable promo composition. | No |
render_promo |
Render a composed promo to MP4. | No |
prepare_promo_preview / teardown_promo_preview |
Stand up and tear down a preview environment to film. | No |
prepare_promo_render / teardown_promo_render |
Stand up and tear down the render sandbox. | No |
produce_promo |
Produce a narrated promo of a connected repo or live URL, end to end. | No |
Connect bridge
Section titled “Connect bridge”| Tool | What it does | Requires approval |
|---|---|---|
list_connections |
List the third-party apps this workspace has connected. | No |
connect_read |
Make a read-only call against a connected app’s API. | No |
connect_write |
Make a mutating call against a connected app’s API. | Yes |
request_connection |
Return a sign-in link so you can connect a provider. | No |
check_connection |
Check whether a provider is connected yet. | No |
connect_read and connect_write take a provider id and a relative path — never a full URL — and Connect injects the credential server-side. Chat never sees the token.
Connect action catalog
Section titled “Connect action catalog”| Tool | What it does | Requires approval |
|---|---|---|
list_connected_actions |
Browse Connect’s typed action catalog for this workspace. | No |
run_connected_action |
Run a read-only catalog action. | No |
run_connected_write_action |
Run a catalog action that changes something. | Yes |
Models
Section titled “Models”GET /v1/models returns the selectable models. The current set (Anthropic Claude models, served via AWS Bedrock):
{ "models": [ "global.anthropic.claude-haiku-4-5-20251001-v1:0", "global.anthropic.claude-sonnet-4-6", "global.anthropic.claude-opus-4-5-20251101-v1:0" ]}The default is the Haiku profile. Pass any of these — including the global. prefix, which is part of the id — as the model field on /v1/chat.
Voices & languages
Section titled “Voices & languages”generate_audio draws on the Text-to-Speech catalog. Voice names, engines, and the languages each one covers are documented once, in Text-to-Speech — Chat does not maintain a second copy that would drift out of date. The default language is auto (detected from your text).
Attachment limits
Section titled “Attachment limits”Attachments come either from your Drive library or from POST /v1/uploads (50 MiB per file). What happens next depends on the type:
- Text extracted:
text/*,application/json,application/xml. Up to ~50 KiB per attachment (beyond that the attachment is reported astruncated) and ~200 KiB per request across all files. - Presence block only: images, video, audio, and PDF. The file’s name, MIME type,
drive_file_id, and a short-lived download link are given to the model — enough for it to acknowledge the file and pass it to a tool — but the bytes are not inlined as text. These are reported asattached, notskipped. skipped: only when the file could not be fetched at all, or an unrecognised type was hit.
Attachments replay across turns: once attached to a conversation, later turns in that conversation still carry the file, so you don’t re-attach it to ask a follow-up.
Operator configuration
Section titled “Operator configuration”Self-hosting Chat? It’s a Go service backed by Postgres for conversation history and pending approvals. Configuration is environment-driven; a capability is enabled only when every variable it needs is set — otherwise the matching tool is omitted from the registry and the model never sees it. Every request is scoped to the caller’s workspace.
| Variable | Purpose | Default |
|---|---|---|
CHAT_ADDR |
HTTP listen address. | :9102 |
IDENTITY_API_URL |
Accounts service base URL (server-to-server). | http://localhost:9091 |
CHAT_WEB_URL |
Default redirect target after SSO. | http://localhost:4329 |
CHAT_WEB_URL_ALLOWLIST |
Allowed ?web= origins (comma-separated). |
— |
CHAT_CORS_ALLOWED_ORIGINS |
CORS origins for the API (comma-separated). | — |
CHAT_DATABASE_URL |
Postgres DSN for conversation history and durable approvals. Unset → history routes 503 persistence_disabled, chat still streams, and approvals fall back to in-memory (they will not survive a restart). |
— |
AI provider
Section titled “AI provider”Chat needs at least one provider, or /v1/chat returns 503 agent_not_configured. When both are set, the direct Anthropic provider is used.
| Variable | Purpose | Default |
|---|---|---|
AWS_REGION |
Bedrock region (credentials come from the standard AWS chain). | — |
CHAT_BEDROCK_MODEL_ID |
Bedrock inference profile. | global.anthropic.claude-haiku-4-5-20251001-v1:0 |
ANTHROPIC_API_KEY |
Enables the direct Anthropic provider when set. | — |
CHAT_ANTHROPIC_MODEL_ID |
Model ID for the direct provider. | claude-haiku-4-5-20251001 |
Suite wiring
Section titled “Suite wiring”Each capability is gated independently. If its variables are unset, the corresponding tool is simply not offered.
| Variable | Enables |
|---|---|
SUITE_TOOL_SECRET |
Suite tool dispatch overall. Unset → text-only chat. |
IMAGEGEN_API_URL |
generate_image. |
TTS_API_URL |
generate_audio. |
DRIVE_API_URL |
Drive attachments, the drive_* read tools, and auto-save of generated artifacts. |
CONNECT_API_URL + CONNECT_CONTROLLER_SECRET |
Everything credential-backed: Gmail, GitHub, Slack, Linear, ElevenLabs, WhatsApp, the Connect bridge, and the Connect action catalog. Credentials are fetched from Connect at call time. |
STUDIO_API_URL + SUITE_TOOL_SECRET |
compose_promo / render_promo. |
RENDER_WORKER_URL + RENDER_WORKER_SECRET |
capture_app. |