Guides

Voice calls (WUTS Voice)

Place and receive WhatsApp voice calls from your WUTS device — start a call over REST, carry live audio over a WSS PCM socket, and use the built-in browser softphone.

WUTS Voice lets your WUTS device place and receive real WhatsApp voice calls. Signaling and call control go over the normal REST API; the live audio is carried over a WebSocket (WSS) as raw PCM, so it works from a browser or any WSS-capable client. WUTS Voice is voice-only, 1:1.

Every REST request below is authenticated with your tenant token (Authorization: Bearer <token>) and the X-Device-ID header. See Authentication for how to obtain them.

WhatsApp only lets you call a number you already have an open conversation with. Calling a contact you have never messaged is rejected by WhatsApp's anti-spam layer (the call ends immediately with reason enc). If in doubt, send a text first, then call.

1. Start a call

POST /calls

curl -X POST https://api.wuts.com.br/calls \
  -H "Authorization: Bearer $WUTS_TOKEN" \
  -H "X-Device-ID: $DEVICE_ID" \
  -H "Content-Type: application/json" \
  -d '{"to":"5511999999999"}'
{
  "success": true,
  "call_id": "73EE9BC6A87B1AA5AED857F4DBB73B0E",
  "to": "5511999999999@s.whatsapp.net",
  "status": "ringing",
  "timestamp": "2026-07-16T18:19:47Z"
}

The destination is now ringing. Keep the call_id — you need it for audio and to hang up.

2. Carry live audio (WSS)

Audio flows over a dedicated WebSocket. Because a browser cannot set the Authorization header on a WS handshake — and your bearer must never appear in a URL — you first mint a single-use, 90‑second media token, then open the returned ws_url.

POST /calls/media-token

curl -X POST https://api.wuts.com.br/calls/media-token \
  -H "Authorization: Bearer $WUTS_TOKEN" \
  -H "X-Device-ID: $DEVICE_ID" \
  -H "Content-Type: application/json" \
  -d '{"call_id":"73EE9BC6A87B1AA5AED857F4DBB73B0E"}'
{
  "success": true,
  "token": "GqDmOiShYLmydinGTdK83Yb...",
  "ws_url": "wss://api.wuts.com.br/calls/media?ct=GqDmOiShYLmydinGTdK83Yb...",
  "expires_at": "2026-07-16T18:36:10Z"
}

Open ws_url immediately. Over the socket:

  • Send binary frames of raw PCM — signed 16‑bit little‑endian, mono, 16 kHz. Frames of ~20 ms (320 samples / 640 bytes) are ideal. This is your microphone.
  • Receive binary frames in the same PCM format — the other party's audio.
  • Send the text frame {"type":"hangup"} to end the call.

The server pings every 15 seconds to keep the socket alive across proxies. Audio only flows once the other side answers; anything you send while it is still ringing is discarded.

const ws = new WebSocket(ws_url);
ws.binaryType = "arraybuffer";
ws.onmessage = (e) => {
  if (e.data instanceof ArrayBuffer) playPcm16(e.data); // peer audio
};
// ...feed 16 kHz Int16LE mic frames:
ws.send(int16Frame.buffer);

3. Hang up

POST /calls/terminate

curl -X POST https://api.wuts.com.br/calls/terminate \
  -H "Authorization: Bearer $WUTS_TOKEN" \
  -H "X-Device-ID: $DEVICE_ID" \
  -H "Content-Type: application/json" \
  -d '{"call_id":"73EE9BC6A87B1AA5AED857F4DBB73B0E"}'

Sending {"type":"hangup"} on the media socket ends the call too.

4. Inspect calls

GET /calls/active returns calls currently ringing or connected; GET /calls/history returns a rolling list of recent calls (most recent first), including the termination reason.

curl -H "Authorization: Bearer $WUTS_TOKEN" -H "X-Device-ID: $DEVICE_ID" \
  https://api.wuts.com.br/calls/active

5. Incoming calls

Inbound calls arrive as call.* webhooks. Accept a ringing call with POST /calls/accept (pass the call_id from the call.offer event), then open a media socket the same way as above to carry audio. To decline, use POST /calls/terminate.

The browser softphone

The WUTS manager ships a built‑in softphone. Open an instance page in the WUTS panel and use the WUTS Voice card to dial, mute, and hang up with full two‑way audio straight from your browser — it uses exactly the REST + WSS flow described above, so you never handle tokens by hand.

Limits & notes

  • Voice only, 1:1. Video and group calls are not carried (an inbound video/group call is detected and can be declined cleanly).
  • Established conversations only — see the warning at the top about the enc rejection.
  • Media tokens are single‑use and expire in 90 seconds; mint one per call, right before opening the socket.

On this page