Skip to main content
Sayna keeps multi-tenant SIP traffic cleanly separated by preserving the caller’s original destination through the entire path: carrier ➝ proxy ➝ LiveKit ➝ Sayna ➝ customer webhook. This guide distills the architecture and the routing rules that keep each tenant’s events isolated.
Use this when you have one LiveKit SIP deployment serving many customers and need deterministic webhook delivery per domain.

Architecture

End-to-end flow

1

Capture the intended tenant

The PSTN carrier sends an INVITE to the customer-facing SIP domain (for example, customer-a.com). Kamailio terminates the INVITE so it can inspect headers before forwarding.
2

Preserve destination in transit

Kamailio extracts the host from the Request-URI or To header, writes it into X-To-IP, and then forwards the call to LiveKit SIP. This keeps the tenant hint intact even if the To header is rewritten.
3

Expose headers inside LiveKit

LiveKit creates the SIP participant and exposes every SIP header as a participant attribute (sip.h.<header-name>), making sip.h.x-to-ip available for routing. Standard attributes like sip.phoneNumber, sip.trunkPhoneNumber, and sip.callID travel alongside.
4

Resolve the tenant in Sayna

When LiveKit emits participant_joined, Sayna validates the webhook signature, checks that SIP attributes exist, and extracts the routing domain with a strict priority order.
5

Fan out to the right webhook

Sayna looks up the domain in the SIP hook table, signs the payload with HMAC-SHA256, and forwards asynchronously to the configured HTTPS endpoint for that tenant. Missing or malformed domains are logged but never block LiveKit acknowledgements.

Component responsibilities

Domain extraction rules

  • Priority: sip.h.x-to-ip takes precedence; sip.h.to is the fallback when the custom header is absent.
  • Parsing: hostnames are pulled from SIP URIs, display-name formats, or plain host strings; ports and parameters are discarded.
  • Normalization: domains are lowercased for case-insensitive matching against the hook table.
If neither sip.h.x-to-ip nor sip.h.to can be parsed into a domain, Sayna logs the condition and skips forwarding.

Webhook fan-out behavior

  • Signature verification: LiveKit webhooks must pass JWT validation before routing begins.
  • SIP-only enforcement: Events without SIP attributes are acknowledged but never forwarded.
  • Hook lookup: Domains are matched against the configured hook list (case-insensitive). Missing entries produce warnings rather than errors.
  • Payload signing: Forwarded requests include X-Sayna-Signature, X-Sayna-Timestamp, X-Sayna-Event-Id, and X-Sayna-Signature-Version for downstream verification.
  • Asynchronous delivery: Forwarding runs off the main webhook handler to keep LiveKit responses fast; retries depend on downstream HTTP results and your operational policies.

Payload shape (conceptual)

Hook model and lifecycle

  • Mapping: Each hook entry pairs a host (tenant domain) with an HTTPS webhook URL and an auth_id; hostnames must be unique.
  • Room ownership: The auth_id field determines who owns rooms created from inbound SIP calls. When a call arrives, Sayna sets room.metadata.auth_id to the hook’s auth_id value.
  • Sources of truth: Hooks load from configuration at startup and can be updated at runtime through the SIP hook APIs; runtime changes persist across restarts.
  • Security: HTTPS is required, and all forwarded calls are HMAC-signed so tenants can authenticate Sayna.
  • Operations: Adding, replacing, or removing a host takes effect immediately for new events; existing in-flight calls keep their resolved destination.
When AUTH_REQUIRED=true, the auth_id field is required for each SIP hook entry. This ensures that inbound SIP calls are properly associated with a tenant for room access control.

Reliability checklist

  • Confirm Kamailio always stamps X-To-IP so the strongest routing signal is present.
  • Monitor webhook forwarding logs for “no webhook configured for domain” and align hook entries with the domains your carriers use.
  • Validate sip.h.to parsing in LiveKit traces when troubleshooting unexpected skips.
  • Keep hook secrets long and rotate them with tenants periodically.
  • Track downstream failures (timeouts, TLS issues, DNS resolution) since Sayna will log but not retry indefinitely without operator action.