# Omni — Active Developer Documentation # Quickstart Source: https://omni.channel/docs/quickstart Get from a new API key to a completed stateful task in a few minutes. Vault upload is a separate next step, so encryption does not block your first success. ### Create a Developer Preview key [#create-a-developer-preview-key] Open [API Keys](/dev/keys), choose **Developer Preview**, name the key, and copy its secret. The plaintext secret is shown once. ```bash export OMNI_API_ORIGIN="https://gateway.omnistatic.com" export OMNI_API_KEY="omni_sk_live_..." ``` ### Verify your identity [#verify-your-identity] - `GET /v1/me` — scopes: `['profile:read']` ```bash curl -sS "$OMNI_API_ORIGIN/v1/me" \ -H "Authorization: Bearer $OMNI_API_KEY" ``` The response contains your public `data.user_id`. A `401 unauthorized` means the key is missing, invalid, or revoked. ### Discover a workspace [#discover-a-workspace] - `GET /v1/workspaces` — scopes: `['workspace:read']` ```bash curl -sS "$OMNI_API_ORIGIN/v1/workspaces" \ -H "Authorization: Bearer $OMNI_API_KEY" ``` Choose an `id` from `data.workspaces` and export it: ```bash export OMNI_WORKSPACE_ID="your_workspace_id" ``` ### Create a stateful task [#create-a-stateful-task] - `POST /v1/tasks` — scopes: `['chat:write']` ```bash export OMNI_IDEMPOTENCY_KEY="quickstart-$(uuidgen)" curl -sS -X POST "$OMNI_API_ORIGIN/v1/tasks" \ -H "Authorization: Bearer $OMNI_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $OMNI_IDEMPOTENCY_KEY" \ -d '{ "workspace_id": "'$OMNI_WORKSPACE_ID'", "message": "Summarize what we have been working on." }' ``` Save the returned `data.task_id`. The server resolves the workspace's ongoing conversation; do not send a thread or conversation identifier. ### Poll to completion [#poll-to-completion] - `GET /v1/tasks/{task_id}` — scopes: `['chat:read']` ```bash export OMNI_TASK_ID="task_..." curl -sS "$OMNI_API_ORIGIN/v1/tasks/$OMNI_TASK_ID" \ -H "Authorization: Bearer $OMNI_API_KEY" ``` Poll with a short delay while status is `queued` or `running`. Stop at `completed` or `failed`. On completion, read `data.output.text` and `data.output.citations`. ## Next steps [#next-steps] * [Stream the response](/docs/guides/stream-response) instead of polling. * [Give your agent knowledge](/docs/guides/upload-knowledge) through encrypted Vault upload. * [Read persistent history](/docs/guides/read-history) after the task finishes. # Authentication Source: https://omni.channel/docs/authentication Developer integrations authenticate with a scoped personal access token created at [omni.channel/dev/keys](/dev/keys). ```http Authorization: Bearer $OMNI_API_KEY ``` ## Create a token [#create-a-token] 1. Sign in to Omni and open **API Keys**. 2. Choose the **Developer Preview** profile. 3. Give the token a name describing its environment or agent. 4. Copy the secret when it appears. It is shown only once. 5. Store it in a secret manager or local environment variable—not source control. ```bash export OMNI_API_KEY="omni_sk_live_..." ``` ## Recommended Preview scopes [#recommended-preview-scopes] | Scope | What it permits | | ---------------- | --------------------------------------------- | | `profile:read` | Read the authenticated public identity | | `workspace:read` | Discover accessible workspaces | | `chat:write` | Create stateful tasks | | `chat:read` | Read tasks, events, and workspace history | | `files:write` | Reserve, upload, and complete encrypted files | | `files:read` | List files and storage quota | | `files:delete` | Delete files | | `vault:write` | Register uploaded knowledge with Vault | | `vault:read` | Read Vault state and attach ready files | Missing permission returns `403 insufficient_scope`. Resource authorization is still evaluated for every request; possessing a scope does not grant access to another user's workspace or file. ## Revoke and rotate [#revoke-and-rotate] Create a replacement token, update the consuming application, verify it, and then revoke the old token from `/dev/keys`. A revoked token returns `401 unauthorized`. Treat a PAT like a password. Never put it in browser-delivered code, logs, screenshots, examples, or a Git repository. # API Keys Source: https://omni.channel/docs/api-keys Omni's [Access Token Manager](/dev/keys) creates named scoped credentials for the Developer API. ## Recommended setup [#recommended-setup] 1. Select **Developer Preview**. 2. Use a distinct name such as `Local research agent` or `CI smoke test`. 3. Create the key and copy the secret once. 4. Export it as `OMNI_API_KEY`. 5. Revoke it when the integration no longer needs access. The Developer Preview profile includes stateful tasks, workspace history, encrypted Files, Vault, citations, and file deletion. Use a separate key per environment so one integration can be revoked without disrupting another. The older **General API key** profile exists for compatibility integrations. New stateful integrations should choose **Developer Preview**. # Tasks Source: https://omni.channel/docs/tasks A task sends one message into an existing Omni workspace. Omni resolves the appropriate ongoing conversation and returns a public task identifier for polling or streaming. - `POST /v1/tasks` — scopes: `['chat:write']` ```json { "workspace_id": "workspace_...", "message": "What decisions did we make about launch?", "attachments": ["file_01ARZ3NDEKTSV4RRFFQ69G5FAV"] } ``` `attachments` is optional and accepts at most three ready public file IDs. Without attachments, Omni applies normal automatic workspace recall. Omni resolves the underlying conversation and infrastructure automatically. Unknown request fields return `validation_error`. ## Lifecycle [#lifecycle] ```text POST /v1/tasks → queued → running → completed | failed ``` Use `GET /v1/tasks/{task_id}` to poll. A completed task includes `output.text`, an assistant message ID, and validated citations when the answer uses grounded context. ## Retry safely [#retry-safely] `Idempotency-Key` is required when creating a task. Reusing the key with the same logical body returns the existing task. Reusing it with a different body returns `409 idempotency_conflict`. See [Stream a response](/docs/guides/stream-response) for live events and [Citations](/docs/citations) for grounded output. # Workspaces Source: https://omni.channel/docs/workspaces A workspace is the public boundary for an ongoing Omni experience: its persona or companion, persistent messages, and Vault knowledge. - `GET /v1/workspaces` — scopes: `['workspace:read']` ```bash curl -sS "$OMNI_API_ORIGIN/v1/workspaces" \ -H "Authorization: Bearer $OMNI_API_KEY" ``` Each item in `data.workspaces` exposes only public fields such as `id`, `name`, `kind`, `created_at`, and `updated_at`. Use the returned `id` with tasks, history, and Vault routes. Create and configure workspaces in Omni, then use this endpoint to discover the ones available to your key. Requests for inaccessible resources return a safe `404 not_found` rather than revealing whether another user's resource exists. # Files & Vault Source: https://omni.channel/docs/files-and-vault Files hold encrypted data. Vault prepares completed files for recall and citation. Upload a file, wait for `status` to be `ready`, then attach its `file_id` to a task. ## Public file lifecycle [#public-file-lifecycle] 1. Reserve an encrypted upload with `POST /v1/files/uploads`. 2. PUT ciphertext to the returned capability URL using its returned headers. 3. Confirm storage with `POST /v1/files/{file_id}/complete`. 4. Poll `GET /v1/workspaces/{workspace_id}/vault/files/{file_id}`. 5. Use the ready `file_id` in `attachments`. Omni selects the underlying storage automatically. Upload capability URLs are temporary and should not be logged. Start with [Upload knowledge](/docs/guides/upload-knowledge), then [Ask with a file](/docs/guides/ask-with-file). # History Source: https://omni.channel/docs/history - `GET /v1/workspaces/{workspace_id}/messages` — scopes: `['chat:read']` ```bash curl -sS "$OMNI_API_ORIGIN/v1/workspaces/$OMNI_WORKSPACE_ID/messages?limit=50" \ -H "Authorization: Bearer $OMNI_API_KEY" ``` Each public message contains: ```json { "id": "message_...", "role": "assistant", "content": "The launch decision was December [V1].", "created_at": "2026-09-13T10:00:00Z", "citations": [] } ``` Pages are bounded. If `data.next_cursor` is present, pass it as `cursor` on the next request. Treat the cursor as opaque: do not parse, construct, or persist assumptions about its contents. The API returns the durable public message history without exposing its underlying storage. # Citations Source: https://omni.channel/docs/citations Omni uses short handles inside answer text and returns matching structured citation objects. ## Conversation citation [#conversation-citation] `[C1]` points to a validated range in the workspace's persistent conversation: ```json { "handle": "C1", "source_type": "conversation", "conversation": { "sequence_epoch": 1, "first_sequence": 8, "last_sequence": 10 } } ``` ## Vault citation [#vault-citation] `[V1]` points to a validated range in a ready Vault file: ```json { "handle": "V1", "source_type": "vault_file", "vault_file": { "file_id": "file_01ARZ3NDEKTSV4RRFFQ69G5FAV", "filename": "launch-notes.txt", "version_id": "version_...", "chunk": { "ordinal": 0, "char_start": 120, "char_end": 284 } } } ``` The task status, `retrieval_citations`/`done` events, and persisted assistant message represent the same validated citation set. Do not manufacture handles client-side. Only the documented public citation fields are part of the contract. # Give your agent knowledge Source: https://omni.channel/docs/guides/upload-knowledge ### Using an AI coding agent? Read https://omni.channel/llms.txt and https://omni.channel/llms-full.txt. Use the documented TypeScript omni-file-v1 helper to upload my file to this Omni workspace, wait until Vault reports ready, and return the public file ID. Do not print my API key or upload capability URL. [View the TypeScript upload example](https://github.com/kennethmakjw/omni-channel-unified/blob/main/examples/typescript/vault-upload.ts). ### Encrypt locally and reserve [#encrypt-locally-and-reserve] Generate a fresh 32-byte FEK and encrypt the source bytes with `omni-file-v1`. Reserve with `POST /v1/files/uploads`, including the logical filename, MIME type, plaintext and expected ciphertext sizes, encryption metadata, and `vault_workspace_id`. Use a unique `Idempotency-Key`. Save `data.file.file_id` and the returned `data.upload` capability. ### Upload exactly as directed [#upload-exactly-as-directed] PUT the ciphertext to `data.upload.url` using its returned `method` and `required_headers`. The URL is a short-lived secret: use it directly and do not log it. ### Complete [#complete] Call `POST /v1/files/{file_id}/complete` with another stable idempotency key. Completion verifies the upload exists and matches the expected size. ### Wait for Vault [#wait-for-vault] Poll `GET /v1/workspaces/{workspace_id}/vault/files/{file_id}` until `data.file.status` is `ready`. Stop and surface an error if it becomes `failed` or the polling deadline expires. A confirmed upload is stored; a ready file can be attached to a task. Continue to [Ask with a file](/docs/guides/ask-with-file). # Ask with a file Source: https://omni.channel/docs/guides/ask-with-file After a Vault file reports `ready`, include its public `file_id` in the task's `attachments` array. ```bash curl -sS -X POST "$OMNI_API_ORIGIN/v1/tasks" \ -H "Authorization: Bearer $OMNI_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: ask-file-$(uuidgen)" \ -d '{ "workspace_id": "'$OMNI_WORKSPACE_ID'", "message": "What launch month does this file specify? Cite the source.", "attachments": ["'$OMNI_FILE_ID'"] }' ``` The `attachments` array tells Omni to use those files for this task. Poll the task until completion. A grounded answer can contain `[V1]`, with the same structured citation in `data.output.citations`. Read workspace history to verify the citation was persisted with the assistant message. # Automatic Vault recall Source: https://omni.channel/docs/guides/automatic-vault-recall When `attachments` is omitted, Omni can recall relevant knowledge from the workspace automatically. ```json { "workspace_id": "workspace_...", "message": "What did our research say about the launch window?" } ``` If ready Vault evidence supports the answer, the completed task contains validated `[Vn]` citations. Conversation context may appear as `[Cn]` citations. Use explicit `attachments` only when the user has selected particular files. # Stream a response Source: https://omni.channel/docs/guides/stream-response - `GET /v1/tasks/{task_id}/events` — scopes: `['chat:read']` Connect with bearer authentication and accept `text/event-stream`: ```bash curl -N "$OMNI_API_ORIGIN/v1/tasks/$OMNI_TASK_ID/events" \ -H "Authorization: Bearer $OMNI_API_KEY" \ -H "Accept: text/event-stream" ``` Public event names are: | Event | Meaning | | --------------------- | ------------------------------------------- | | `text_chunk` | Incremental assistant text | | `tool_start` | A safely named tool began | | `tool_end` | A safely named tool completed or failed | | `retrieval_citations` | Validated public citations became available | | `done` | Terminal successful event | | `error` | Terminal stream error | Record each SSE `id`. After a network interruption, reconnect with `Last-Event-ID` set to the last fully processed ID. The server resumes after that event; consumers must still tolerate duplicate delivery around network boundaries. Only the documented public events and fields are part of the stream contract. # Read persistent history Source: https://omni.channel/docs/guides/read-history ```bash curl -sS "$OMNI_API_ORIGIN/v1/workspaces/$OMNI_WORKSPACE_ID/messages?limit=50" \ -H "Authorization: Bearer $OMNI_API_KEY" ``` Read `data.messages` in the returned stable order. If `data.next_cursor` is present, request the next bounded page: ```bash curl -sS "$OMNI_API_ORIGIN/v1/workspaces/$OMNI_WORKSPACE_ID/messages?limit=50&cursor=$OMNI_CURSOR" \ -H "Authorization: Bearer $OMNI_API_KEY" ``` Treat the cursor as opaque. Message citations use the same validated structures returned by task status and task events. # Retry safely Source: https://omni.channel/docs/guides/retry-safely Use an `Idempotency-Key` for task creation and mutating Files operations. Keep the same key only while retrying the same logical request. * Same key and same request: Omni returns the existing/current resource. * Same key and different request: `409 idempotency_conflict`. * New logical operation: generate a new key. Retry only when `error.retryable` is `true`, normally for `rate_limited` or `service_unavailable`. Honor `retry_after_seconds` when present; otherwise use bounded exponential backoff with jitter. Do not retry validation, authentication, scope, not-found, or idempotency-conflict errors without first changing the request or credential. # Delete a file Source: https://omni.channel/docs/guides/delete-file ```bash curl -sS -X DELETE "$OMNI_API_ORIGIN/v1/files/$OMNI_FILE_ID" \ -H "Authorization: Bearer $OMNI_API_KEY" \ -H "Idempotency-Key: delete-$OMNI_FILE_ID" ``` Deletion requires `files:delete`. Repeating the same logical deletion with the same idempotency key is safe. After deletion, the workspace Vault lookup returns a safe `404 not_found`, and the file is excluded from future retrieval. Historic assistant messages may retain their citation record as part of durable conversation history, but the deleted source is no longer retrievable. File deletion is destructive. Verify the public `file_id` and workspace before sending the request. # API Reference Source: https://omni.channel/docs/api Base origin: `https://gateway.omnistatic.com` All public operations use the `/v1/...` path surface. ## Identity and workspaces [#identity-and-workspaces] - `GET /v1/me` — scopes: `['profile:read']` - `GET /v1/workspaces` — scopes: `['workspace:read']` - `GET /v1/workspaces/{workspace_id}/messages` — scopes: `['chat:read']` ## Stateful tasks [#stateful-tasks] - `POST /v1/tasks` — scopes: `['chat:write']` - `GET /v1/tasks/{task_id}` — scopes: `['chat:read']` - `GET /v1/tasks/{task_id}/events` — scopes: `['chat:read']` ## Files [#files] - `POST /v1/files/uploads` — scopes: `['files:write']` - `POST /v1/files/{file_id}/upload-url` — scopes: `['files:write']` - `POST /v1/files/{file_id}/complete` — scopes: `['files:write']` - `GET /v1/files` — scopes: `['files:read']` - `GET /v1/files/{file_id}` — scopes: `['files:read']` - `DELETE /v1/files/{file_id}` — scopes: `['files:delete']` - `GET /v1/storage/quota` — scopes: `['files:read']` ## Vault [#vault] - `GET /v1/workspaces/{workspace_id}/vault/files` — scopes: `['vault:read']` - `GET /v1/workspaces/{workspace_id}/vault/files/{file_id}` — scopes: `['vault:read']` # Errors Source: https://omni.channel/docs/api/errors Every Developer Preview route returns the same safe envelope: ```json { "error": { "code": "rate_limited", "message": "Request limit reached", "retryable": true, "retry_after_seconds": 5, "request_id": "request_...", "docs_url": "https://omni.channel/docs/api/errors#rate_limited", "details": {} } } ``` Keep `request_id` when reporting a problem. Raw upstream exceptions are not part of the public contract. | Code | Retry? | Caller action | | ------------------------------------------------------------- | -----: | ----------------------------------------------------------------------------------- | | `validation_error` | No | Fix fields, types, identifiers, or unknown fields. | | `unauthorized` | No | Supply a valid, non-revoked PAT. | | `insufficient_scope` | No | Create or use a key with the required scope. | | `not_found` | No | Verify the public ID and workspace. The API does not reveal inaccessible resources. | | `idempotency_conflict` | No | Use the original body or a new key for new work. | | `vault_not_ready` | No | Poll Vault state until `ready`, then make a new request. | | `file_too_large` | No | Reduce the upload to a supported size. | | `upload_missing` | No | PUT ciphertext before completing. | | `upload_size_mismatch` | No | Re-encrypt/reserve using the exact expected size. | | `quota_exceeded` | No | Delete unneeded files or wait for quota policy to change. | | `rate_limited` | Yes | Honor `retry_after_seconds`, then retry with jitter. | | `service_unavailable` | Yes | Retry with bounded exponential backoff and the same idempotency key. | # Limits Source: https://omni.channel/docs/api/limits The current public contract verifies these bounds: | Surface | Current bound | | ---------------- | ----------------------------------------------- | | Task message | Required, up to 100,000 characters | | Task attachments | Up to 3 unique public file IDs | | History `limit` | 1–100; default 50 | | Idempotency key | Required on task creation; up to 128 characters | Use `GET /v1/storage/quota` for the authenticated account's current byte allowance. Use returned quota values rather than hard-coding capacity assumptions. # Changelog Source: https://omni.channel/docs/api/changelog ## 2026-09 — Stateful Developer Preview [#2026-09--stateful-developer-preview] * Added the workspace-centric stateful task API. * Added persistent workspace history. * Added projectless encrypted Files and Vault facades. * Added resumable public task events and structured conversation/Vault citations. * Established `https://gateway.omnistatic.com` as the current public developer origin. * Kept Chat Completions operational as a compatibility surface. This changelog tracks public API changes. # Chat Completions Source: https://omni.channel/docs/compatibility/chat-completions Use Chat Completions only for an existing OpenAI-style integration. New integrations should use [`POST /v1/tasks`](/docs/tasks). - `POST /v1/chat/completions` — scopes: `['chat']` Existing clients can continue sending their established OpenAI-style request shape. The endpoint is not formally deprecated, but new stateful behavior is available through Tasks.