Skip to main content
POST
Send an SMS

Authorizations

Authorization
string
header
required

Tenant API key (vdx_live_…) as Authorization: Bearer.

Headers

Idempotency-Key
string
required

Required. A retry with the same key and body replays the stored 2xx response; the same key with a different body is 409. A failed attempt releases the key.

Required string length: 8 - 255

Body

application/json
to
string
required

E.164.

message
string

Free text; wins over the configuration's body.

smsConfigurationId
string<uuid>

Defaults to the tenant's default configuration.

templateId
string

Provider template; the provider renders it.

from
string

E.164 override.

variables
object
contactRef
string

Opaque consumer reference, echoed on the webhook.

callId
string

Links this message to a call.

Callbacks

POST
{$request.body#/webhookUrl}interactionCompleted

Body

application/json

The payload Vodex POSTs to your endpoint — not something this API serves. Documented here because it is the contract configured through PUT /v1/webhook-config.

interaction.completed, not call.completed: one stream for every channel, so a consumer writes one handler and switches on channel rather than integrating a new webhook when SMS or WhatsApp ships.

It is deliberately complete — a consumer that receives this needs no follow-up GET. analysis is an open envelope: summary and sentiment land there as additive keys, so a handler written today keeps parsing tomorrow's payloads.

Headers

The timestamp is inside the signed string, not merely alongside it: signing the body alone leaves a captured delivery replayable forever, because nothing in what was signed says when it was sent. Reject anything more than 300 seconds off in either direction. During a rotation both secrets sign, as repeated v1= values, so consumers can move without a coordinated deploy.

Retries

8 attempts at 10s, 30s, 2m, 10m, 30m, 2h, 6h, each with full jitter (a random point in [0, delay], so a consumer coming back from an outage is not hit by every queued delivery at the same instant). 408, 429, 5xx and transport failures retry; every other 4xx does not — repeating a request the consumer called wrong just burns the budget a real outage needs. A delivery that exhausts its attempts is the dead letter — status: failed with no nextAttemptAt — and stays visible at GET /v1/webhook-deliveries.

type
enum<string>
Available options:
interaction.completed
id
string

Idempotency key — stable across retries.

tenantId
string<uuid>
channel
enum<string>
Available options:
voice-ai,
sms
contactRef
string | null

Echoed from the dial request, never interpreted.

rawAddress
string | null

The counterparty address, for when no contactRef was passed.

direction
string
from
string | null
to
string | null
assistantId
string | null
engine
string | null
startedAt
string<date-time> | null
endedAt
string<date-time> | null
outcome
object

Orthogonal facts, never a prose string — a consumer should never have to sniff a string for what happened.

transcript
object[]

Turn-level, with offsets in seconds from the first traced event.

analysis
object

Open envelope — additive keys only.

quality
object
cost
object
artifacts
object

References, never bytes — audio is resolved through the authed API when a user presses play, so it never leaves its region unbidden.

variables
object

Exactly what the agent knew, frozen at call time — the assistant's config may have changed since.

Response

Retried — a timeout is transient.

Response

Sent.

id
string<uuid>
configurationId
string<uuid> | null
direction
enum<string>
Available options:
outbound,
inbound
toNumber
string
fromNumber
string | null
body
string
status
enum<string>
Available options:
queued,
sent,
delivered,
failed
provider
string
providerMessageId
string | null
error
string | null
contactRef
string | null
callId
string | null
createdAt
string<date-time>