WhatsApp calling must be switched on for your number first: Update Calling Settings with
"status": "ENABLED", or Settings, Integrations, WhatsApp in FlowIQ. WhatsApp requires a verified business and a messaging limit of 2,000 or more.Endpoints
Events arrive on your webhook as the
call event.
Authentication and limits
Use your organisation API key (Settings, API Keys) as a Bearer token. The key decides which number you act on; you never pass an organisation id.
Limits per key: 240 reads and 60 actions a minute. Every response carries
organization_id and an X-Request-Id; errors carry a stable code; POST and PATCH accept an Idempotency-Key. See Conventions.
Three ways to answer
Set it for the number with Update Calling Settings. With
api you can also answer any single call by stream: Accept Call with stream instead of sdp.
Answer incoming calls with your own server
- Register a webhook for the
callevent (orall): Create Webhook. - Take the calls away from FlowIQ:
PATCH /v1/calling/settingswith{"answered_by": "api"}. The FlowIQ inbox stops ringing and FlowIQ’s voice AI stops picking up. Calls still show in the customer’s chat. Switch back any time with"flowiq". - When a customer calls, your webhook receives
kind: "call",call.event: "connect"and WhatsApp’s SDP offer incall.session.sdp. - Answer: create an SDP answer for that offer with your WebRTC stack and send it with Accept Call. Pre-accept first if you want the audio to flow the moment you accept.
- The call runs between WhatsApp and your media server. Audio never passes through FlowIQ. End it with End Call; when either side hangs up you receive
call.event: "terminate"with the duration.
Stream the audio to your own voice agent
The simplest way to put an AI voice agent on WhatsApp: no WebRTC, no SDP. FlowIQ answers the call and opens a WebSocket to you with the caller’s audio; you send audio back.- Point the number at your server:
PATCH /v1/calling/settingswith{"answered_by": "stream", "stream": {"url": "wss://agent.example.com/flowiq", "format": "pcm16_16k", "headers": {"Authorization": "Bearer …"}}}. Formats:pcm16_16k(default),pcm16_8k,pcm16_24k,mulaw_8k. - When a customer calls, FlowIQ connects to your URL (with your headers, plus
X-Flowiq-Call-IdandX-Flowiq-Organization-Id), answers the call, and streams. - The socket speaks JSON, shaped like Twilio Media Streams, so a voice agent written for Twilio works with little change (with
mulaw_8k, usually unchanged):
Calls streamed this way show
answered_by: "stream" in the call history. The call ends when the caller hangs up (you receive stop), when you send hangup or close the socket, or after 60 minutes.
Audio over the stream is uncompressed PCM (or µ-law): about 32 KB/s per direction at 16 kHz. One socket per call.
Call a customer
WhatsApp only lets you call people who have given you permission.- Check: Get Call Permission tells you whether you may call now (
can_start_call) and how many requests and calls you have left. - Ask: within 24 hours of the customer writing to you, Request Call Permission sends them an Allow calls button. Outside that window, send an approved template that has a call permission request with Send Template. Their answer arrives on your webhook as
kind: "call_permission". - Call: Start Call with your SDP offer. WhatsApp rings the customer; when they answer, your webhook gets
call.event: "connect"with their SDP answer incall.session, andkind: "call_status"events track ringing, accepted and rejected.
Or use SIP
Voice platforms that speak SIP (most voice-agent and contact-centre products do) can take the calls without any WebRTC code: setcalling.sip with Update Calling Settings and WhatsApp sends every call straight to your SIP server.
The call object
Call ids contain characters such as
= and /: URL-encode them when they go in a path (encodeURIComponent).
Calls that never got an end event from WhatsApp (a lost webhook) are closed by FlowIQ: missed after 3 minutes ringing, completed after 35 minutes in progress, with end_reason: "no_end_event".
