Skip to main content
Use WebRTC live audio when a user needs to speak and listen from a browser.

Flow

  1. Connect and scan the WaGo token.
  2. Start or answer a call.
  3. Fetch WebRTC config.
  4. Create a browser RTCPeerConnection.
  5. Add the microphone track.
  6. Send the SDP offer to WaGo.
  7. Set WaGo’s SDP answer as the remote description.

Get WebRTC config

curl -H "token: YOUR_TOKEN" \
  http://localhost:1337/call/webrtc/config
Response:
{
  "iceServers": [
    {
      "urls": ["stun:stun.l.google.com:19302"]
    }
  ],
  "turn": {
    "enabled": false,
    "status": "coming_soon"
  }
}
TURN is not configurable yet. WaGo currently uses STUN only.

Send an SDP offer

curl -X POST http://localhost:1337/call/webrtc/offer \
  -H "Content-Type: application/json" \
  -H "token: YOUR_TOKEN" \
  -d '{
    "callID": "CALL_ID",
    "type": "offer",
    "sdp": "BROWSER_SDP_OFFER"
  }'
WaGo returns an SDP answer:
{
  "callID": "CALL_ID",
  "type": "answer",
  "sdp": "WAGO_SDP_ANSWER",
  "turn": {
    "enabled": false,
    "status": "coming_soon"
  }
}

Browser example

const config = await fetch("https://your-wago-server.com/call/webrtc/config", {
  headers: { token: "YOUR_TOKEN" }
}).then((res) => res.json());

const pc = new RTCPeerConnection({
  iceServers: config.data?.iceServers || config.iceServers
});

const stream = await navigator.mediaDevices.getUserMedia({
  audio: {
    echoCancellation: true,
    noiseSuppression: true,
    autoGainControl: true
  }
});

for (const track of stream.getAudioTracks()) {
  const sender = pc.addTrack(track, stream);
  const transceiver = pc.getTransceivers().find((item) => item.sender === sender);
  if (transceiver) transceiver.direction = "sendrecv";
}

pc.ontrack = (event) => {
  const audio = document.querySelector("audio");
  audio.srcObject = event.streams[0];
  audio.play();
};

const offer = await pc.createOffer();
await pc.setLocalDescription(offer);

await new Promise((resolve) => {
  if (pc.iceGatheringState === "complete") return resolve();
  pc.addEventListener("icegatheringstatechange", () => {
    if (pc.iceGatheringState === "complete") resolve();
  });
});

const answer = await fetch("https://your-wago-server.com/call/webrtc/offer", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    token: "YOUR_TOKEN"
  },
  body: JSON.stringify({
    callID: "CALL_ID",
    type: pc.localDescription.type,
    sdp: pc.localDescription.sdp
  })
}).then((res) => res.json());

const payload = answer.data || answer;
await pc.setRemoteDescription({
  type: payload.type,
  sdp: payload.sdp
});

Close the WebRTC bridge

curl -X POST http://localhost:1337/call/webrtc/close \
  -H "Content-Type: application/json" \
  -H "token: YOUR_TOKEN" \
  -d '{
    "callID": "CALL_ID"
  }'
This closes the browser media bridge. It does not hang up the WhatsApp call. Use /call/hangup to end the call.

HTTPS requirement

Browser microphone access requires HTTPS in production. localhost is the main exception for local development.