Last updated August 14, 2026

VoiceBlender v0.12.1 API v1.0.0 http://localhost:8080/v1

VoiceBlender bridges SIP and WebRTC voice calls with multi-party audio mixing, real-time speech-to-text, text-to-speech, AI agent integration, recording, and webhook-based event delivery.

Configuration

VariableDefaultDescription
INSTANCE_ID(auto-generated UUID)Instance identifier included in all API responses and webhook events
HTTP_ADDR:8080REST API listen address
ALLOWED_IPS(empty = allow all)Comma-separated allowlist of IPs and CIDR ranges (IPv4 and IPv6) gating every HTTP endpoint, including /v1/vsi, /v1/legs/websocket, /v1/legs/moq, /metrics, and pprof. Bare addresses become /32 (v4) or /128 (v6); malformed entries fail server startup. Only X-Forwarded-For is consulted as a proxy header (see TRUST_PROXY_HEADERS).
TRUST_PROXY_HEADERSfalseWhen true, the client IP used for the ALLOWED_IPS check is taken from the leftmost X-Forwarded-For entry (falling back to the socket peer if absent). Enable only behind a trusted reverse proxy that overwrites the header — otherwise it is client-spoofable.
SIP_BIND_IP127.0.0.1IPv4 address advertised in SDP, Contact, and Via headers (and used as the listen address when SIP_LISTEN_IP is empty)
SIP_LISTEN_IP(same as SIP_BIND_IP)UDP socket bind IP. Accepts 127.0.0.1, 0.0.0.0, ::, or any literal v4/v6 address
SIP_BIND_IPV6(empty = v4-only)IPv6 address advertised in SDP/Contact/Via for IPv6 calls. Set this for IPv6-only or dual-stack deployments
SIP_LISTEN_IPV6(same as SIP_BIND_IPV6)Optional separate IPv6 socket bind address (used when running with both 0.0.0.0 and a specific v6 literal)
SIP_EXTERNAL_IPPublic IPv4 address for NAT/Docker deployments. When set, used in SIP Contact headers and SDP media (c=) lines instead of the bind IP. IPv6 has no equivalent — set SIP_BIND_IPV6 to the address you want advertised.
SIP_PORT5060SIP listen port
SIP_TLS_PORT(disabled)SIP-over-TLS listen port (typically 5061). When set, SIP_TLS_CERT and SIP_TLS_KEY must also be provided. Required for WhatsApp Business Calling integration.
SIP_TLS_CERTPath to PEM-encoded TLS certificate (e.g. fullchain.pem). Meta rejects self-signed certs — use a CA-signed cert matching a public FQDN.
SIP_TLS_KEYPath to PEM-encoded TLS private key (e.g. privkey.pem).
SIP_DEBUGfalseWhen true, log the full RFC 3261 wire form of every inbound and outbound SIP request and response. Very verbose — use only for troubleshooting.
SIP_DOMAIN(falls back to advertised IP)FQDN advertised in From, Contact and Via on all outbound SIP signalling (classic trunks and WhatsApp). Should match the SAN on SIP_TLS_CERT and any allowlist your carrier or Meta keeps.
SIP_HOSTvoiceblenderSIP User-Agent name
SIP_CODECSPCMU,PCMAComma-separated, preference-ordered list of codecs the SIP engine offers on outbound INVITEs and accepts on inbound INVITEs. Recognized names (case-insensitive): PCMU, PCMA, G722, opus, AMR-WB, AMR-NB (bare token AMR resolves to AMR-NB per RFC 4867 §8.1). Unknown names and duplicates are dropped silently.
SIP_AUTO_RINGINGfalseWhen true, the server sends 180 Ringing automatically after 100 Trying. Default sends only 100 Trying; the API caller drives ringing via /ring, /early-media, or /answer.
SIP_USE_SOURCE_SOCKETfalseWhen true, route SIP responses and in-dialog requests (BYE, re-INVITE, UPDATE, INFO, NOTIFY, REFER) back to the request's source UDP socket instead of the peer's Contact / Via sent-by. Enable when peers advertise unroutable addresses (e.g. private IPs in Contact from behind NAT).
SIP_REGISTRATION_DEFAULT_EXPIRES_SECONDS3600Expiry used when an inbound REGISTER carries no Expires value.
SIP_REGISTRATION_MAX_EXPIRES_SECONDS7200Upper clamp on the granted REGISTER expiry. Requests above this value are honored at this maximum.
SIP_REGISTRATION_SWEEP_INTERVAL_MS1000Sweeper period (ms) for evicting expired AOR bindings.
SIP_REGISTRATION_ALLOW_MULTIPLE_CONTACTStrueWhen true, the same AOR may be bound from multiple Contacts simultaneously (and POST /v1/legs parallel-forks to every bound contact). When false, each REGISTER replaces any prior Contacts for the AOR.
ICE_SERVERSstun:stun.l.google.com:19302STUN/TURN URLs for WebRTC ICE, comma-separated
WEBRTC_EXTERNAL_IPS(empty)Comma-separated public IPs advertised as host ICE candidates (pion SetNAT1To1IPs). Required when VB runs behind NAT/Docker so peers behind firewalls can reach it; supports IPv4 and IPv6 literals. The literal value "auto" triggers STUN-based public-IP discovery at startup using the configured ICE_SERVERS; failure is non-fatal.
RTP_PORT_MIN10000Minimum UDP port for RTP/RTCP media
RTP_PORT_MAX20000Maximum UDP port for RTP/RTCP media
DEFAULT_SAMPLE_RATE16000Default mixer sample rate (Hz) for new rooms when sample_rate is not specified. Allowed: 8000, 16000, 48000.
SPEECH_DETECTION_ENABLEDfalseEmit speaking.started / speaking.stopped events for every connected leg by default. Per-call speech_detection on POST /v1/legs or POST /v1/legs/{id}/answer overrides this.
VSI_EVENT_BUFFER_SIZE256Per-client buffer (in events) on the /v1/vsi WebSocket. When the client falls behind, new events are dropped and the next delivered event carries an events_dropped notification. Clamped to [16, 1000000]. Memory: ~1 KB × buffer per connection at the default.
AMRWB_MODE2AMR-WB (G.722.2) encoder speech-mode ceiling 0..8: 0=6.60, 1=8.85, 2=12.65, 3=14.25, 4=15.85, 5=18.25, 6=19.85, 7=23.05, 8=23.85 kbit/s. The actual transmit mode is this ceiling clamped to the peer's negotiated mode-set. Default 2 matches GSMA IR.92 / VoLTE.
AMRWB_OCTET_ALIGNEDtrueOffer octet-aligned AMR-WB framing (RFC 4867) in outbound SDP. When false, offers bandwidth-efficient framing. On answers, VoiceBlender always echoes the framing the peer negotiated.
AMRNB_MODE7AMR-NB (RFC 4867) encoder speech-mode ceiling 0..7: 0=4.75, 1=5.15, 2=5.90, 3=6.70, 4=7.40, 5=7.95, 6=10.2, 7=12.2 kbit/s. The actual transmit mode is this ceiling clamped to the peer's negotiated mode-set. Default 7 is GSM-EFR-equivalent 12.2 kbit/s.
AMRNB_OCTET_ALIGNEDtrueOffer octet-aligned AMR-NB framing (RFC 4867) in outbound SDP. When false, offers bandwidth-efficient framing. On answers, VoiceBlender always echoes the framing the peer negotiated.
RECORDING_DIR/tmp/recordingsLocal directory for recording output files
LOG_LEVELinfoLog verbosity: debug, info, warn, error. Transcript text, DTMF digits and event payloads are logged only at debug.
WEBHOOK_URLGlobal webhook URL for event delivery (fallback when no per-leg or per-room webhook is set)
WEBHOOK_SECRETHMAC-SHA256 signing secret for the global webhook
ELEVENLABS_API_KEYAPI key for ElevenLabs TTS, STT, and Agent provider
VAPI_API_KEYAPI key for VAPI Agent provider
DEEPGRAM_API_KEYAPI key for Deepgram STT and TTS
AZURE_SPEECH_KEYSubscription key for Azure Cognitive Speech Services (TTS and STT)
AZURE_SPEECH_REGIONeastusAzure region for Speech Services (e.g. eastus, westeurope)
S3_BUCKETS3 bucket name for recording uploads
S3_REGIONus-east-1AWS region for S3
S3_ENDPOINTCustom S3-compatible endpoint (e.g. MinIO)
S3_PREFIXKey prefix applied to all S3 objects
S3_ALLOW_INSECURE_ENDPOINTfalseAllow a plaintext http:// S3_ENDPOINT on a non-local host, rather than refusing to ship recording audio in cleartext (startup: exit 1; per request: 400). Loopback, private and link-local addresses, single-label hostnames and .internal/.local names are exempt and need no opt-in.
S3_PREFLIGHT_TIMEOUT10sBudget for the HeadBucket probe run at startup. A bucket the store reports absent exits 1; any other probe failure (403 without s3:ListBucket, 5xx, unreachable) is warned about and startup continues. 0 disables the probe.
S3_REQUEST_PREFLIGHT_TIMEOUT2sBudget for the HeadBucket probe run when a request supplies s3_bucket. A bucket the store reports absent returns 400; any other probe failure is warned about and the recording proceeds. Kept short because it runs inside record-start, which on VSI occupies the connection's command loop. 0 disables the probe.
GCS_BUCKETGoogle Cloud Storage bucket for recording uploads via the native GCS API (storage=gcs). Uses Application Default Credentials / Workload Identity — preferred over S3_ENDPOINT=https://storage.googleapis.com on GKE.
GCS_OBJECT_NAME_PREFIXObject name prefix applied to all GCS uploads (e.g. recordings or a bare workspace id). A trailing slash is added automatically when missing.
AWS_ACCESS_KEY_ID[SDK-resolved, not read by VoiceBlender] AWS access key for S3 uploads and AWS Polly TTS. Consumed by the AWS SDK default credential chain alongside AWS_SECRET_ACCESS_KEY and the optional AWS_SESSION_TOKEN.
AWS_SECRET_ACCESS_KEY[SDK-resolved, not read by VoiceBlender] AWS secret key paired with AWS_ACCESS_KEY_ID.
AWS_SESSION_TOKEN[SDK-resolved, not read by VoiceBlender] Optional temporary-credential session token (STS / SSO) used together with AWS_ACCESS_KEY_ID/SECRET.
AWS_PROFILE[SDK-resolved, not read by VoiceBlender] Profile name in ~/.aws/credentials to use instead of static AWS_* env vars.
AWS_REGION[SDK-resolved, not read by VoiceBlender] AWS region used by S3 and Polly when S3_REGION is empty.
GOOGLE_APPLICATION_CREDENTIALS[SDK-resolved, not read by VoiceBlender] Path to a Google Cloud service-account JSON file used by Google Cloud TTS and by GCS recording uploads when no other credential source is available. Consumed by Google's Application Default Credentials chain (env var → gcloud ADC → GCE/GKE metadata / Workload Identity).
TTS_CACHE_ENABLEDfalseEnable disk-backed TTS audio cache; cached audio persists across restarts
TTS_CACHE_DIR/tmp/tts_cacheDirectory for cached TTS audio files (used when TTS_CACHE_ENABLED=true)
TTS_CACHE_INCLUDE_API_KEYfalseInclude API key in TTS cache key; set true if different keys map to different voice clones
SIP_JITTER_BUFFER_MS0SIP ingress jitter buffer target delay in ms (0 = disabled passthrough). Applies to every SIP leg.
SIP_JITTER_BUFFER_MAX_MS300Maximum depth of the SIP ingress jitter buffer in ms. Frames beyond this are dropped oldest-first to catch up after a stall.
SIP_SDP_STRICT_MLINE_ANSWERfalseEmit a port-0 placeholder for every offered m= section we do not accept, so answers carry the same m-line count and order as the offer (RFC 3264 §6). Gated separately from multi-stream because it changes the SDP single-stream calls emit whenever a peer offers a section we don't handle, such as video.
SIP_REFER_AUTO_DIALfalseWhen true, accept incoming SIP REFER requests and automatically originate the transferred call. Default-deny: stays off unless the SIP edge is locked down (IP allow-lists, digest auth) because auto-dialing arbitrary Refer-To URIs is a classic toll-fraud vector. Outbound transfers initiated via the REST API are unaffected by this flag.
SIP_TCP_ENABLEDfalseListen for SIP over TCP on SIP_PORT alongside the UDP listener. Recommended with SIPREC: a recording session's INVITE carries the metadata document alongside the SDP and is larger than RFC 3261 section 18.1.1 allows over UDP.
SIPREC_ENABLEDfalseAccept inbound SIPREC recording sessions (RFC 7866), where an SBC or PBX forks a call's media to this server. Off by default: when off, an INVITE carrying Require: siprec is rejected with 420 Bad Extension and one that only hints at SIPREC with 488.
SIPREC_AUTO_ANSWERtrueAnswer an inbound recording session immediately instead of parking it until POST /v1/legs/{id}/answer. A session recording client does not wait for an application decision, so leaving this on is usually correct; turn it off to gate sessions from a controller.
SIPREC_MAX_STREAMS8Maximum number of m=audio sections accepted on one recording session. A session offering more is rejected with 486, bounding the RTP ports and goroutines a single peer can claim.
SIPREC_METADATA_MAX_BYTES65536Maximum size of the rs-metadata XML document in a SIPREC INVITE. A larger document is rejected with 413 rather than parsed.
SIPREC_SRC_ENABLEDfalseAllow POST /v1/rooms/{id}/siprec to originate outbound recording sessions, forking a room's participants to an external session recording server. Off by default: it lets an API caller stream a room's audio to an arbitrary SIP destination.
SIPREC_AUTO_RECORDfalseStart multi-channel recording automatically when a SIPREC session is accepted, one channel per recorded participant. When false, recording is driven through the usual /v1/legs/{id}/record endpoint.
SIPREC_ROOM_MODEnoneWhere a recording session's audio streams are mixed. "none" attaches nothing and leaves placement to the stream API; "per_session" creates a room named siprec-<legID> and attaches every stream, which is what makes live STT and agents apply to a recorded call; "fixed" attaches every session's streams into SIPREC_ROOM_ID.
SIPREC_ROOM_IDRoom every recording session's streams join when SIPREC_ROOM_MODE=fixed. Ignored in the other modes.
MOQ_ENABLEDfalseEnable the experimental MoQ (Media over QUIC) inbound leg endpoint at CONNECT /v1/legs/moq over WebTransport/HTTP/3. PoC quality, tracks IETF draft-11. When enabled, both MOQ_TLS_CERT_FILE and MOQ_TLS_KEY_FILE must be set.
MOQ_LISTEN_ADDR:8443UDP address for the HTTP/3 listener that backs the MoQ leg. Independent of HTTP_ADDR — TCP/:8080 and UDP/:8443 can run side-by-side.
MOQ_TLS_CERT_FILEPath to the TLS certificate used by the HTTP/3 listener. Required when MOQ_ENABLED=true.
MOQ_TLS_KEY_FILEPath to the TLS private key used by the HTTP/3 listener. Required when MOQ_ENABLED=true.
MOQ_OPUS_BITRATE24000Target bitrate (bps) for the Opus encoder feeding the MoQ leg's mix track. Must be in 6000..510000.
LIVEKIT_ENABLEDfalseEnable the livekit_room leg type at POST /v1/legs (type=livekit_room). Lets VoiceBlender join a LiveKit room as a participant and bridge audio between SIP and LiveKit. Speaks the LiveKit signaling protocol directly via livekit/protocol protobufs over the existing pion stack — no LiveKit SDK is used.
LIVEKIT_URLDefault LiveKit server endpoint (wss://...). Required when LIVEKIT_ENABLED=true unless every request supplies livekit.url. Overridable per-request.
LIVEKIT_OPUS_BITRATE24000Target bitrate (bps) for the Opus encoder publishing audio into LiveKit. Must be in 6000..510000. Overridable per-request via livekit.opus_bitrate.
LIVEKIT_TOKEN_SIGNING_ENABLEDfalseOpt-in: when true, callers may omit livekit.token and instead pass {room,identity,permissions}; VoiceBlender mints the JWT itself. Security caveat: enabling this stores the LiveKit API secret (a high-privilege credential) in VoiceBlender. Keep off in multi-tenant deployments.
LIVEKIT_API_KEYLiveKit API key used to sign minted JWTs. Required only when LIVEKIT_TOKEN_SIGNING_ENABLED=true.
LIVEKIT_API_SECRETLiveKit API secret used to sign minted JWTs. Required only when LIVEKIT_TOKEN_SIGNING_ENABLED=true. Treat as a high-value secret; redact in logs.
LIVEKIT_DEFAULT_TOKEN_TTL6hDefault TTL applied to minted JWTs when the request omits livekit.token_ttl. Go duration string. LiveKit recommends ≤ 6 hours.