Skip to main content
Every request to the Omnia Voice API is authenticated with an API key sent in the X-API-Key header.

Creating a key

Generate keys in the dashboard under API Keys.
The key is shown once, at creation. Store it immediately in your secret manager — we keep only a hash and cannot recover it for you. If you lose a key, revoke it and create a new one.

Keys are scoped to a workspace

A key carries the workspace it was created in. Every request it makes reads and writes only that workspace’s agents, tools, numbers, and calls, and usage is billed to that workspace’s owner. If you run separate environments or serve multiple customers, give each its own workspace and its own key. That way a key can never reach across the boundary, and revoking one has a blast radius of exactly one workspace.
Keys created before workspaces existed remain scoped to the user who made them and continue to work unchanged.

Keeping keys safe

An API key in client-side JavaScript is public, no matter how it is bundled. Anyone can read it and spend your credits. Call the Omnia API from your server, and have the browser talk to your backend.For in-browser voice, your server creates the call and hands the client only the short-lived join URL. See the Voice SDK.
Read the key from the environment rather than committing it:
Add .env to .gitignore. If a key ever lands in a commit, revoke it — Git history is forever, and scrapers watch public repositories continuously.
Create the replacement first, deploy it, confirm traffic has moved, then revoke the old key. Because keys are independent, this is a zero-downtime change.
Separate keys per application or environment mean you can revoke one without taking everything else down, and usage attribution stays readable.

Rate limits

Requests are limited to 1,000 per hour per key. Every response carries the current state: Exceeding the limit returns 429. Back off and retry after the reset time rather than retrying immediately.
Rate limits apply to API calls, not to call minutes. A single agent can hold a two-hour conversation on one POST /calls/create request.

Errors

Failures return a consistent JSON body:
Codes come from two places: a status-derived default (BAD_REQUEST, NOT_FOUND, …) and explicit codes thrown by individual handlers (RESOURCE_NOT_FOUND, INVALID_VOICE, …). Branch on the HTTP status, which is stable, and treat code as a more specific hint.
A 404 is also returned when a resource exists but belongs to another workspace. This is deliberate: it prevents a key from confirming that an ID it cannot access exists.