<Gather> that plays
your greeting and collects one keypress, then runs the branch for that digit. This
API is the authoring layer on top of WaveML — you describe the menu as JSON and
Wave turns it into the call flow.
This is the single-level menu — one greeting, one set of keypad options, each
ending the call or handing it off. Nested sub-menus and a visual flow builder are
separate, later features. For full branching logic today, return
WaveML from your own endpoint instead.
Authentication & tiers
All/v1/callflows endpoints take an API key with the callflows:read or
callflows:write scope, or a dashboard session. You can author and test
menus with a sandbox key (sk_sandbox_); serving a menu to a real inbound
call needs a production key (sk_live_), a production-tier org, and a
bound number (see Bind a number below).
The menu definition
A menu is a JSONdefinition: a greeting, the keypad branches, and an optional
fallback for when the caller presses nothing.
Prompt and actions
A prompt and every branch action is one of these. A branch action is a terminal leaf — it ends the call or hands it off.dial targets are restricted to Saudi (KSA) phone numbers — a toll-fraud
guard, since a <Dial> bridges the caller out to a new leg.Branch input (your webhook payload)
Give a branch aninput object and Wave merges it into the call.input
webhook when the caller picks that option — your
integration’s hook for what the caller chose.
- Up to 20 fields; values are string (≤512), number, or boolean.
- The keys
call_idanddigitsare reserved (Wave sets them) and rejected.
Create a menu
POST /v1/callflows — create a menu by name. Pass "activate": true to make it
live immediately.
201 Created returns the menu, including its version history:
name returns 409 — use PUT to add a version instead.
List and fetch menus
GET /v1/callflows lists one row per menu (its active or latest version):
GET /v1/callflows/{name} returns the full menu — the active (or latest) version’s
definition plus the complete versions history. 404 if the menu doesn’t exist.
Versioning and rollback
Menus are immutable and append-only. Editing never mutates a version in place — it appends a new one.PUT /v1/callflows/{name}with a newdefinition(and optionalactivate) adds the next version.404if the menu doesn’t exist.POST /v1/callflows/{name}/activatewith{ "version": N }makes versionNlive — rollback is just activating an older version.404if that version doesn’t exist.
Bind a number
POST /v1/callflows/{name}/bind-number points one of your Wave numbers at the
menu’s active version — inbound calls to that number then run the menu.
cURL
Returns
{ "bound": true }. Numbers are Wave-provided — see Virtual
Numbers.
Retire a menu
DELETE /v1/callflows/{name} soft-retires a menu: it stops serving callers and any
bound number is unlinked, but the version history is kept (you can recreate it
later). Returns { "retired": true }; 404 if the menu doesn’t exist.
The call.input webhook
When a caller presses a digit, Wave sends a call.input webhook. Its
data carries the pressed digits plus that branch’s input object — so your
backend learns exactly what the caller chose:
Audio prompts
Aplay prompt or action plays a pre-recorded file instead of TTS — good for a
produced greeting, and it sidesteps synthesis latency. Upload the file first, then
reference the returned audio_ref.
POST /v1/callflows/audio takes a base64-encoded WAV file (8 kHz mono 16-bit
PCM recommended — telephony quality) and returns a Wave-hosted audio_ref:
cURL
audio_ref in a play:
Audio upload is rolling out. Until it’s enabled for your account, the upload
returns
503 and menus use say (TTS) prompts. An audio_ref must be a file
you uploaded — arbitrary URLs are rejected.
