Skip to main content

Answer Webhook

The Answer Webhook is called when a call is answered (callee picks up the phone). Your server must respond with a Stream JSON configuration that tells Superfone where to stream the call's audio.

Critical

This webhook MUST return a valid Stream JSON response within 10 seconds, or the call will fail. If the primary answer_url fails, Superfone tries the fallback_answer_url (if configured).

When It's Called​

The answer webhook is triggered when:

  • Inbound calls: A caller dials a number linked to your SFVoPI app, and the call is answered
  • Outbound calls: You initiate a call via the API, and the callee answers

Webhook Payload​

Superfone sends a POST or GET request (based on your app configuration) with the following payload:

Payload Structure (FLAT)​

Important

The payload is FLAT — all fields are at the top level, including request_uuid for your convenience.

FieldTypeDescription
call_uuidstringUnique identifier for this call
request_uuidstringRequest UUID for outbound calls (format: sfv_ob_req_xxxxxxxxxxxx). Not present for inbound calls.
fromstringCaller's phone number (E.164 format, e.g., +918000000003)
tostringCallee's phone number (E.164 format, e.g., +918000000001)
directionstringCall direction: INBOUND or OUTBOUND
call_statusstringCurrent call status: IN_PROGRESS
answered_atstringISO 8601 timestamp when call was answered (e.g., 2026-02-02T10:00:00.000Z)

Example Payloads​

{
"call_uuid": "call-uuid-1738491600-abc123",
"request_uuid": "sfv_ob_req_a8k3m2x9p1z0",
"from": "+918000000003",
"to": "+918000000001",
"direction": "OUTBOUND",
"call_status": "IN_PROGRESS",
"answered_at": "2026-02-02T10:00:00.000Z"
}

Required Response: Stream JSON​

Your server must respond with HTTP 200 and a JSON body containing the stream object:

Response Schema​

{
"stream": {
"url": "wss://your-server.com/ws",
"codec": "PCMA",
"sample_rate": 8000,
"direction": "BOTH",
"bidirectional": true,
"stream_timeout": 86400,
"keep_call_alive": true,
"extra_headers": {
"X-Session-Id": "abc123",
"X-Custom-Header": "value"
}
}
}

Stream Object Fields​

FieldTypeRequiredDefaultDescriptionValidation
urlstringYes—WebSocket URL where Superfone will stream audio (must start with wss:// or ws://)Valid WebSocket URL
codecstringYesPCMAAudio codec: PCMA (G.711 A-law, recommended), PCMU (G.711 μ-law), or L16 (16-bit linear PCM)Enum: PCMA, PCMU, L16
sample_ratenumberYes8000Audio sample rate in Hz. PCMA/PCMU: 8000 only. L16: 8000, 16000, 24000, 32000, 44100, 48000.See description
directionstringYesBOTHAudio stream direction: INBOUND (from callee), OUTBOUND (to callee), or BOTH (bidirectional)Enum: INBOUND, OUTBOUND, BOTH
bidirectionalbooleanNotrueWhether audio flows in both directions (deprecated — use direction instead)Boolean
stream_timeoutnumberNo86400Maximum stream duration in seconds (1-86400, i.e., 1 second to 24 hours)Min: 1, Max: 86400
keep_call_alivebooleanNotrueWhether to keep call active after stream endsBoolean
extra_headersobjectNo{}Custom HTTP headers to send in WebSocket handshake requestKey-value pairs (string keys, string values)
Recommended: codec: "PCMA", sample_rate: 8000

The SFVoPI telephony leg runs A-law @ 8 kHz natively. Picking PCMA + 8000 makes SFVoPI a pure passthrough — no transcoding, lowest latency. Use L16 only if your pipeline specifically needs raw linear PCM over the wire. Avoid PCMU (forces a pointless A-law ↔ μ-law roundtrip). Do not request sample rates > 8000 — the trunk is 8 kHz, anything higher is upsampled on SFVoPI's side for no real quality gain.

tip

Use extra_headers to pass session identifiers, authentication tokens, or other metadata to your WebSocket server.

Webhook Handler Examples​

const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhook/answer', (req, res) => {
const payload = req.body;

console.log('[Answer Webhook] Call answered:', {
call_uuid: payload.call_uuid,
request_uuid: payload.request_uuid,
from: payload.from,
to: payload.to,
direction: payload.direction,
answered_at: payload.answered_at
});

// Return Stream JSON
const streamConfig = {
stream: {
url: 'wss://your-server.com/ws',
codec: 'PCMA',
sample_rate: 8000,
direction: 'BOTH',
stream_timeout: 86400,
extra_headers: {
'X-Call-UUID': payload.call_uuid,
'X-Request-UUID': payload.request_uuid || 'N/A'
}
}
};

res.json(streamConfig);
});

app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});

Timeout and Fallback​

Primary Answer URL​

Superfone waits 10 seconds for your answer_url to respond. If it fails (timeout, connection error, non-200 status, invalid JSON), Superfone tries the fallback_answer_url (if configured).

Fallback Answer URL​

If the primary answer_url fails, Superfone tries the fallback_answer_url with a 5-second timeout. If the fallback also fails, the call is terminated with FAILED status.

Failure Scenarios​

ScenarioBehavior
Primary succeedsCall proceeds with Stream JSON from primary
Primary fails, fallback succeedsCall proceeds with Stream JSON from fallback
Both failCall terminated, hangup webhook sent with call_status: "FAILED"
tip

Configure a fallback_answer_url pointing to a backup server or a static WebSocket endpoint to ensure high availability.

Error Responses​

If your webhook returns an error, Superfone logs it and tries the fallback (if configured):

Status CodeMeaningSuperfone Action
200Success — Stream JSON acceptedProceed with audio streaming
400Bad Request — Invalid Stream JSONTry fallback, then fail call
500Internal Server ErrorTry fallback, then fail call
TimeoutNo response within 10 secondsTry fallback, then fail call

Invalid Stream JSON Examples​

// Missing required field 'url'
{
"stream": {
"codec": "PCMU",
"sample_rate": 8000,
"direction": "BOTH"
}
}
// Invalid codec value
{
"stream": {
"url": "wss://your-server.com/ws",
"codec": "OPUS",
"sample_rate": 8000,
"direction": "BOTH"
}
}
// Invalid sample_rate value
{
"stream": {
"url": "wss://your-server.com/ws",
"codec": "PCMU",
"sample_rate": 48000,
"direction": "BOTH"
}
}

Use Cases​

1. Dynamic WebSocket Routing​

Route calls to different WebSocket servers based on call metadata:

app.post('/webhook/answer', (req, res) => {
const { from, to, direction } = req.body;

// Route to different servers based on direction
const wsUrl = direction === 'INBOUND'
? 'wss://inbound-server.com/ws'
: 'wss://outbound-server.com/ws';

res.json({
stream: {
url: wsUrl,
codec: 'PCMA',
sample_rate: 8000,
direction: 'BOTH'
}
});
});

2. Session Tracking with Extra Headers​

Pass session identifiers to your WebSocket server:

app.post('/webhook/answer', (req, res) => {
const { call_uuid, request_uuid } = req.body;

// Generate session ID
const sessionId = `session_${Date.now()}_${call_uuid}`;

res.json({
stream: {
url: 'wss://your-server.com/ws',
codec: 'PCMA',
sample_rate: 8000,
direction: 'BOTH',
extra_headers: {
'X-Session-Id': sessionId,
'X-Call-UUID': call_uuid,
'X-Request-UUID': request_uuid || 'N/A'
}
}
});
});

3. Call Time Limits​

Set a custom stream timeout based on call type:

app.post('/webhook/answer', (req, res) => {
const { direction } = req.body;

// Shorter timeout for outbound calls
const timeout = direction === 'OUTBOUND' ? 300 : 3600;

res.json({
stream: {
url: 'wss://your-server.com/ws',
codec: 'PCMA',
sample_rate: 8000,
direction: 'BOTH',
stream_timeout: timeout
}
});
});

Notes​

  1. Response Time: Respond as quickly as possible (ideally <1 second) to minimize call setup delay
  2. WebSocket Availability: Ensure your WebSocket server is running and reachable before returning the Stream JSON
  3. HTTPS Required: Use wss:// (secure WebSocket) in production; ws:// is only for local development
  4. Idempotency: Use call_uuid to track processed calls and avoid duplicate processing
  5. Logging: Log all webhook requests for debugging and audit purposes
  6. Error Handling: Return HTTP 200 even if you encounter internal errors — log the error and return a fallback Stream JSON

Next Steps​