# Vodex API > The public Vodex /v1 REST API — assistants, telephony, calls, batches, SMS and webhooks. - [Vodex API](https://apidocs.vodex.ai/introduction.md): The REST API your systems integrate with — assistants, telephony, calls, batches, SMS and webhooks - [Quickstart](https://apidocs.vodex.ai/quickstart.md): From an API key to a completed call report - [Authentication](https://apidocs.vodex.ai/authentication.md): How requests to the Vodex API are authenticated, and how tenancy is enforced - [Webhooks](https://apidocs.vodex.ai/webhooks.md): The end-of-interaction report Vodex POSTs to you — verifying it, and reading the outcome - [Connect an AI assistant](https://apidocs.vodex.ai/ai-assistant.md): Read-only access to your workspace over MCP, with no API key to copy - [List assistants](https://apidocs.vodex.ai/api-reference/assistants/list-assistants.md): A summary per assistant, not the whole config — the prompt body stays out of the list. `hasPrompt: false` is what the list card flags, because saying what is *wrong* with an agent beats an always-green "Active" pill. - [Create or replace an assistant](https://apidocs.vodex.ai/api-reference/assistants/create-or-replace-an-assistant.md): `id` is optional — without it, the slug is derived from `config.name`. A save is validated three ways before it lands, so a bad config fails here rather than mid-call: - [Fetch one assistant's full config](https://apidocs.vodex.ai/api-reference/assistants/fetch-one-assistants-full-config.md) - [Delete an assistant](https://apidocs.vodex.ai/api-reference/assistants/delete-an-assistant.md): `balanced` is the base assistant and cannot be deleted. - [Test a custom tool](https://apidocs.vodex.ai/api-reference/assistants/test-a-custom-tool.md): Call your own tool server once, with the exact envelope an assistant sends mid-call, and get back what it said. The point is to find a broken URL, a wrong auth header or a slow endpoint while you are configuring an assistant — not while a customer is on the phone. - [The model catalog](https://apidocs.vodex.ai/api-reference/console/the-model-catalog.md): The set of engines, models, transcribers and voices an assistant may be built from — and the source `POST /v1/console/assistants` validates a saved config against. Reference data; poll it rather than hard-coding slot values. - [List voices](https://apidocs.vodex.ai/api-reference/console/list-voices.md): Fetched live from the provider where an API exists (Cartesia, ElevenLabs, MiniMax, Google Cloud TTS) and served from a curated list otherwise (Sarvam, Azure, Gnani, Gemini Live). Cached 5 minutes per `(provider, language)`. - [Mint a LiveKit token for a web call](https://apidocs.vodex.ai/api-reference/console/mint-a-livekit-token-for-a-web-call.md): The web-call path. The **resolved** AssistantConfig is embedded in the participant metadata — that is the worker's only config source for this call, which is what makes transient overrides possible without saving anything. - [Minute usage this period](https://apidocs.vodex.ai/api-reference/usage/minute-usage-this-period.md): Read this before starting a batch rather than discovering the limit as a `402` on a call you have already placed. Usage moves as calls land, so it is safe to poll. - [List calls](https://apidocs.vodex.ai/api-reference/calls/list-calls.md): Newest first. In DB mode the `calls` table is the index — one query, no trace parsing — and trace ingest happens off the request path, except for rows **on this page** that are still missing a trace, which are fetched inline so a call that just ended shows its turns now rather than on whichever refr… - [Place an outbound call](https://apidocs.vodex.ai/api-reference/calls/place-an-outbound-call.md): Returns as soon as the room exists and the INVITE is on its way — it does **not** wait for an answer, which takes seconds. The call is recorded in the calls list from this moment, so a carrier rejection (which produces no trace at all, because no agent ever joins) is visible rather than a call that… - [Fetch a call with its trace](https://apidocs.vodex.ai/api-reference/calls/fetch-a-call-with-its-trace.md): The row facts plus the call's full event trace, in one response. - [Stream a call recording](https://apidocs.vodex.ai/api-reference/calls/stream-a-call-recording.md): Gated the same way as the transcript, **before** any bucket object is resolved — audio is the most sensitive artifact here. The bucket and object come from the trace's `recording` event, which is the source of truth. Dev streams the bytes through the API via ADC; stage and prod move to signed URLs. - [List numbers](https://apidocs.vodex.ai/api-reference/phone-numbers/list-numbers.md) - [Add a number](https://apidocs.vodex.ai/api-reference/phone-numbers/add-a-number.md): **No trunkless numbers** (§2.5). A `platform` number needs a `carrier`; a `byo-trunk` number needs a `trunkId` whose trunk already declares that E.164. A number has **no region of its own**: the tenant's region decides which cell serves it (0016 §3), so `region` is not accepted here — sending it is… - [Remove a number](https://apidocs.vodex.ai/api-reference/phone-numbers/remove-a-number.md): Also shrinks the gateway trunk's number list — otherwise the cell keeps answering a number the control plane no longer knows about. - [Reassign or relabel a number](https://apidocs.vodex.ai/api-reference/phone-numbers/reassign-or-relabel-a-number.md): `region` is refused with a `400`. A number does not carry one — the tenant's region picks the cell (0016 §3) — and the gateway trunks live on **one** cell, so a field that moved nothing would leave the call path silently where it was. Moving a tenant is `moveTrunkToCell`, not a `PATCH` here. - [List BYO SIP trunks](https://apidocs.vodex.ai/api-reference/sip-trunks/list-byo-sip-trunks.md): Credentials are never echoed — only the username and the SIP URI to point a PBX at. - [Create a BYO SIP trunk](https://apidocs.vodex.ai/api-reference/sip-trunks/create-a-byo-sip-trunk.md): **IP ACL and credentials, never either alone.** `declaredNumbers` scopes the trunk: LiveKit inbound trunks are created for exactly those numbers, never as a catch-all. - [Delete a trunk](https://apidocs.vodex.ai/api-reference/sip-trunks/delete-a-trunk.md) - [Update a SIP trunk](https://apidocs.vodex.ai/api-reference/sip-trunks/update-a-sip-trunk.md) - [Rotate trunk credentials](https://apidocs.vodex.ai/api-reference/sip-trunks/rotate-trunk-credentials.md): An **in-place** update on the gateway trunk. Live calls are unaffected (INVITEs authenticate at call setup), but new INVITEs need the new credentials from this moment — coordinate the customer's PBX switch. True dual-slot overlap needs `livekit/sip` support for multiple credential pairs per trunk. - [The carrier registry](https://apidocs.vodex.ai/api-reference/carriers/the-carrier-registry.md): What the console can offer and how each carrier connects. This is the file where "adding a carrier touches trunk config and an adapter, never the worker or the media path" actually lives — `fields` keys land verbatim in the `POST /v1/integrations` body, so the connect form is data-driven rather than… - [List connected carrier accounts](https://apidocs.vodex.ai/api-reference/integrations/list-connected-carrier-accounts.md) - [Connect a carrier account](https://apidocs.vodex.ai/api-reference/integrations/connect-a-carrier-account.md): Credentials are **validated against the carrier first** — a saved integration always means "these credentials worked". One account, one integration, owning one SIP trunk; numbers are added to it over time. - [One integration, with its numbers](https://apidocs.vodex.ai/api-reference/integrations/one-integration-with-its-numbers.md): Adds this account's numbers and — when the carrier can text — the SMS delivery-receipt URL, which is where receipts land once a token has been minted (on the first send, or on an SMS settings save). - [Disconnect](https://apidocs.vodex.ai/api-reference/integrations/disconnect.md): Removes the gateway trunk and the stored credentials. This integration's numbers must go first. - [Replace integration credentials](https://apidocs.vodex.ai/api-reference/integrations/replace-integration-credentials.md): Re-validated against the carrier, then written as a **new Secret Manager version under the same reference** — so nothing downstream needs a row change. The gateway trunk's auth is updated in lockstep, as is the SMS credential mirror for messaging carriers. A rejection marks the integration `status:… - [List available numbers](https://apidocs.vodex.ai/api-reference/integrations/list-available-numbers.md): Empty for messaging carriers and for manually provisioned ones — there is no API to enumerate. - [Add numbers to an integration](https://apidocs.vodex.ai/api-reference/integrations/add-numbers-to-an-integration.md): Reuses the integration's single trunk. On the carrier side the numbers are attached and inbound is pointed at the **serving cell**; on the gateway side the same trunk simply gets a wider number list. The first number creates the LiveKit trunks on the cell serving the **tenant** (0016 §3) — an Indian… - [List batches](https://apidocs.vodex.ai/api-reference/batches/list-batches.md): Newest first, each with its row-status counts (one grouped query for the whole page, not one per batch). - [Create a batch](https://apidocs.vodex.ai/api-reference/batches/create-a-batch.md): `Idempotency-Key` is **mandatory**: async producers retry, and a replay must return the same batch rather than dial the list twice. A replay answers `202` with `Idempotent-Replay: true`; the same key with a different body is `409`. - [Fetch a batch](https://apidocs.vodex.ai/api-reference/batches/fetch-a-batch.md): Counts, the number pool, every run, and why the batch is pacing the way it is. - [List rows](https://apidocs.vodex.ai/api-reference/batches/list-rows.md) - [Append rows](https://apidocs.vodex.ai/api-reference/batches/append-rows.md): Same admission rules as create — a row the API refuses on Monday must not slip in on Tuesday. A **completed** batch that gains rows starts running again; the alternative is a consumer having to know that a batch which happens to have drained is now immutable, which is a race they cannot win. A **can… - [Append rows from a CSV](https://apidocs.vodex.ai/api-reference/batches/append-rows-from-a-csv.md): A raw `text/csv` body, not a JSON envelope. Every column that is not a known field (`rowRef`, `to`/`toE164`, `assistantId`, `from`) becomes a **variable** — that is the whole ergonomic point: export `to,name,balance,tz` from your CRM and it works, with no mapping UI to build and none to learn. - [Cancel one row](https://apidocs.vodex.ai/api-reference/batches/cancel-one-row.md): Idempotent — re-cancelling a row that already finished is not an error. A `dialing` row keeps its live call (there is no way to un-ring a phone), and `inflightCallId` names it; recording the cancellation now is what stops the **retry**. The caller's intent — "no more calls to this person" — is fully… - [Pause a running batch](https://apidocs.vodex.ai/api-reference/batches/pause-a-running-batch.md): A compare-and-swap — a batch that is not `running` is `409`, never a silent overwrite. - [Resume a paused batch](https://apidocs.vodex.ai/api-reference/batches/resume-a-paused-batch.md) - [Cancel a batch](https://apidocs.vodex.ai/api-reference/batches/cancel-a-batch.md): Pending rows become `cancelled`. Rows already `dialing` are left alone — there is no way to un-ring a phone — and the settle path keeps a cancelled row cancelled. - [Requeue rows as a new run](https://apidocs.vodex.ai/api-reference/batches/requeue-rows-as-a-new-run.md): Bumps `runNo` and requeues the selected rows in one transaction — attempts keyed to a run the batch never reached would be worse than no restart at all. Per-row attempt counters reset **within the new run**; `batch_attempts` keeps every prior attempt, keyed by `runNo`. - [List dial attempts](https://apidocs.vodex.ai/api-reference/batches/list-dial-attempts.md): Every attempt on the batch — this is the compliance export. - [List callbacks](https://apidocs.vodex.ai/api-reference/callbacks/list-callbacks.md): The work list behind the `schedule_callback` tool. A consumer normally learns of an appointment from the interaction outcome (`outcome.callback`) rather than by polling this, but the list is what makes a missed webhook recoverable. - [One callback](https://apidocs.vodex.ai/api-reference/callbacks/one-callback.md): Another tenant's callback is a `404`, not a `403` — the id of a row you cannot see is itself information. A malformed uuid is the same `404` rather than a `400`, because the lookup never happens either way. - [Update a callback](https://apidocs.vodex.ai/api-reference/callbacks/update-a-callback.md): One route for three acts, because to a consumer they are the same one: the appointment it learned about from the interaction outcome is no longer right. - [Per-number spacing, caps, and cooldown](https://apidocs.vodex.ai/api-reference/number-rotation/per-number-spacing-caps-and-cooldown.md): Spacing and daily caps are per-**number** and global across every batch, so they are not batch fields and cannot be set at create time. A cooled number was flagged by the health sweep: a caller ID the carriers have marked does not fail loudly, it just stops being answered, so nothing else in the sys… - [Set rotation for one number](https://apidocs.vodex.ai/api-reference/number-rotation/set-rotation-for-one-number.md): The number must be yours. Clearing a cooldown by hand is an override of the sweep's judgement, so the record says a human did it (`cooldownReason: "set manually"`, failures reset) — otherwise the next person sees an uncooled number with a stale reason. - [SMS provider capabilities](https://apidocs.vodex.ai/api-reference/sms/sms-provider-capabilities.md): Capabilities plus credential status in one call — the configuration form is entirely data-driven from this, which is the anti-`if (provider === 'twilio')` device. Templates are required for Pronto, optional for Twilio, and impossible for Vonage. - [Store provider credentials](https://apidocs.vodex.ai/api-reference/sms/store-provider-credentials.md): Verified against the provider **before** storing (Twilio and Vonage) — a typo'd token stored now is a failed send with a worse error later. The value goes to Secret Manager; only the reference and a fingerprint are kept. - [Remove provider credentials](https://apidocs.vodex.ai/api-reference/sms/remove-provider-credentials.md) - [List SMS configurations](https://apidocs.vodex.ai/api-reference/sms/list-sms-configurations.md) - [Create an SMS configuration](https://apidocs.vodex.ai/api-reference/sms/create-an-sms-configuration.md): A configuration carries a provider `templateId` **XOR** a free-text `body`, never both. `senderId` must match `senderKind`: an `MG…` SID for a messaging service, an E.164 for a fixed number, and either an E.164 or nothing for `provider_managed` (Pronto's campaign number). - [Delete an SMS configuration](https://apidocs.vodex.ai/api-reference/sms/delete-an-sms-configuration.md) - [Update an SMS configuration](https://apidocs.vodex.ai/api-reference/sms/update-an-sms-configuration.md): The **merged** shape is validated, not just the patched keys — a partial patch must not be able to leave a combination the create path forbids. Unsetting the default directly is refused: promote another configuration instead, so a tenant is never left with configurations and no default. - [List SMS senders](https://apidocs.vodex.ai/api-reference/sms/list-sms-senders.md): Live from Twilio. Empty for providers whose sender is typed or provider-managed. - [Provider content templates](https://apidocs.vodex.ai/api-reference/sms/provider-content-templates.md): Live listing only where the provider offers one (Twilio Content). Empty otherwise. - [Render exactly what a send would produce](https://apidocs.vodex.ai/api-reference/sms/render-exactly-what-a-send-would-produce.md): The **same** resolution the send path runs, minus the provider call — so the confirm modal and the send cannot drift. - [Send an SMS](https://apidocs.vodex.ai/api-reference/sms/send-an-sms.md): `Idempotency-Key` is **mandatory** — retrying a timeout must never double-text anyone. Only a `201` is replayed; a validation failure or a provider rejection **releases** the key so a corrected retry can reuse it. The same key with a different body is `409`, and a second request while the first is i… - [Message log](https://apidocs.vodex.ai/api-reference/sms/message-log.md) - [Read the tenant's webhook endpoint](https://apidocs.vodex.ai/api-reference/webhooks/read-the-tenants-webhook-endpoint.md): The secret is never readable — only whether one exists. `hasSecret` is true for any tenant the platform has minted one for, which is all of them: the same secret signs custom tool calls, so it is not conditional on having a webhook endpoint. - [Set the webhook endpoint](https://apidocs.vodex.ai/api-reference/webhooks/set-the-webhook-endpoint.md): Owner, admin, or an API key; viewers may not. - [Stop deliveries](https://apidocs.vodex.ai/api-reference/webhooks/stop-deliveries.md): The secret reference stays: history stays verifiable, and re-enabling must not silently invalidate a consumer's stored secret. - [Rotate the signing secret](https://apidocs.vodex.ai/api-reference/webhooks/rotate-the-signing-secret.md): One secret signs two things: webhook deliveries, and the `tool.call` POSTs the agent makes to your own server. Every tenant has one whether or not a webhook endpoint is configured, so if you use custom tools and no webhooks, this is where you get the value to verify `x-vodex-signature` against. - [Rotate the signing secret (alias)](https://apidocs.vodex.ai/api-reference/webhooks/rotate-the-signing-secret-alias.md): The same operation as `POST /v1/signing-secret/rotate`, under the older path. Neither is deprecated — this is the one existing integrations call; the other is named for what the secret *is* rather than for one of the two things it signs. See that operation for the semantics. - [Delivery records](https://apidocs.vodex.ai/api-reference/webhooks/delivery-records.md): "Did they get it?" — the support question this table exists to answer, without asking the customer to go and read their own logs. Newest first, capped at 100. - [Fetch a delivery](https://apidocs.vodex.ai/api-reference/webhooks/fetch-a-delivery.md): The whole record, including the exact JSON that was POSTed. This is what answers "we got something, but the signature does not verify": the `x-vodex-signature` header is rebuilt with a fresh timestamp on every attempt, so it is not stored — but `rawBody` plus the `t` from the delivery the consumer r… - [Resend a delivery](https://apidocs.vodex.ai/api-reference/webhooks/resend-a-delivery.md): Fires immediately rather than waiting for the next sweep — someone pressing resend is watching. A delivery id from another tenant is `404`. - [Read recording retention](https://apidocs.vodex.ai/api-reference/tenants/read-recording-retention.md): Retention is enforced by GCS lifecycle rules keyed on the per-tenant object prefix, not by a column — this reads the rules back. - [Set this tenant's recording retention](https://apidocs.vodex.ai/api-reference/tenants/set-this-tenants-recording-retention.md): The tenant in the URL must be the caller's own — these routes rewrite bucket lifecycle rules keyed by tenant prefix, so anything else is `404` (not `403`: "not yours" should look like "no such tenant"). Upserts the rule. GCS caps a bucket at 100 lifecycle rules. - [Fall back to the bucket default](https://apidocs.vodex.ai/api-reference/tenants/fall-back-to-the-bucket-default.md) ## OpenAPI Specs - [api-public-v1](/openapi/api-public-v1.yaml)