Reference

Platform behaviour

Rules that apply across every verb and noun: how unknown markup fails, how URLs are validated, how callbacks are signed, and how caching works.

Machine-readable reference

Everything on this site is generated from one structured document. AI agents and tooling should consume these directly:

  • /voiceml-spec.json — the canonical spec: every verb and noun with attributes, defaults, behaviour, callbacks, and platform rules, as JSON.
  • /llms.txt — index of this reference in the llms.txt convention.
  • /llms-full.txt — the complete reference as a single markdown document.
  • voiceml.org/llms.txt — the publish + integrate guide for the hosted markup service (its llms-full.txt covers Liquid templating and dashboard delivery).

Unknown elements

An unknown top-level element (a typo like <Saw>, or a vendor-specific extension from another platform) is skipped with a warning logged, and the document continues — bad markup never hangs up a call. Recognized-but-unsupported combinations are the exception: <Sim> under <Dial>, an unknown noun under <Connect>/<Start>/<Stop>, or an empty <Dial> stop the document with an unsupported-element error, visible in logs and in the parent verb's action callback when one fires.

Inbound-message documents

Inbound text messages (not calls) are answered by a separate, smaller document surface: only <Message> and <Redirect> execute — every other element is skipped. <Message> to defaults to the original sender and from to the receiving number; an explicit from must be account-owned. The message webhook may also answer with Content-Type: text/plain — the body is sent as the reply with no markup at all. <Media> attachments are not carried on these replies.

URL validation

Every URL attribute (action URLs, callback URLs, audio URLs, stream URLs) is validated against the RFC 3986 printable-ASCII charset before use; CR/LF and backslash are rejected outright. Webhook fetches are HTTPS-only, verified against the system CA bundle, and re-validated on every redirect hop; addresses that resolve to the platform's own infrastructure are refused.

Webhook signing

Every callback and document fetch is signed with your account's API key using the industry-standard request-signature scheme, so stock webhook-validation middleware in the official helper libraries accepts VoiceML requests without modification.

Limits and delivery

Document size

A VoiceML document may be at most 64 KiB; an oversized response surfaces as a document-retrieval failure, not a parse error.

Fetch timeout

Every webhook and document fetch times out after 15 seconds.

Webhook delivery

Webhooks are delivered exactly once — a failed callback is logged, never retried — so endpoints must be idempotent on the caller's side of any doubt.

Loops

The loop attribute on <Say> and <Play> accepts 0 for "repeat until the call ends"; internally every loop is capped at 1000 iterations.

Redirects

<Redirect> chains and action-URL fetches are capped at 10 hops. When a fetch is redirected, 301/302/303 re-fetch as GET with the form fields moved to the query string, 307 preserves the method and body, and 308 is not followed; every hop is re-signed and re-validated.

Caching

Hosted markup (voiceml.org)

Markup published to voiceml.org — the free VoiceML hosting service — is content-addressed: the URL is derived from a hash of the markup itself, so the content behind a given URL is permanent and can never change. It is served with a strong ETag and Cache-Control: private, max-age=315360000, immutable (ten years) — effectively forever. Markup that is a Liquid template ({{ }} or {% %}) is published to a separate /r/ URL instead, rendered for each call with that call's parameters — so it can branch on the key pressed, the caller, or the time of day — and never cached. Publishing identical markup always returns the same URL; to change what a call does, publish the new markup and point your number at the new URL. Publishing, templating, and delivering voicemail, recordings, and transcripts to the VoiceML dashboard are documented at https://voiceml.org/llms-full.txt.

Audio (<Play>)

Audio played by <Play> is pre-fetched to a local cache, deduplicated by upstream ETag, validated by content type, and capped at 50 MiB per file. Change the URL (fingerprinted name or query parameter) to force a refresh.

Self-hosted VoiceML documents

A VoiceML document served from your own server is cached only when that server opts in: Cache-Control: max-age=N caches the parsed document for N seconds; an ETag alone makes every reuse revalidate with If-None-Match; Cache-Control: no-store / no-cache (or no caching headers at all) means every request fetches fresh. Cache entries are keyed by URL, method, account, and the call's own identity (CallSid, From, To, Direction), so two calls never share a cached document and signing continues to work as expected.

The initial voice-URL fetch

The initial voice-URL fetch carries the standard call fields — CallSid, AccountSid, From, To, Caller, Called, CallStatus, Direction, ApiVersion — as form fields on POST or on the query string on GET, so a server can branch per call from the first request. Inbound From and To are E.164. ForwardedFrom is included when the call reached you via a forward. CallerName is not sent, and the location fields (FromCity, ToState, and so on) are currently sent empty.