Connect a SIP trunk to the workspace
Connects the workspace's trunk. A workspace has at most one, so there is no trunk ID in the path.
Refuses with sip_trunk.already_exists when a trunk is already connected, rather than replacing it: swapping a live trunk would drop calls in flight, and on an ip_allowlist trunk would reset an approved connection back to pending_approval. Use PUT to change an existing trunk.
The resulting status depends on auth_mode, and the difference is a security one. A registration trunk starts pending and needs no operator approval — the carrier authenticates with a username and password on every REGISTER, so it proves itself on every call. An ip_allowlist trunk starts pending_approval and never self-activates: trusting a signalling IP is a standing grant that only an operator can make.
Authorization
bearerAuth Long-lived ES256 JWT minted from the dashboard (https://app.sautikit.com/developers/api-keys). Signed by the
platform keyring. Carries workspace_id and scopes claims;
revoked via the platform deny-list.
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
curl -X POST "https://example.com/v1/sip-trunk" \ -H "Content-Type: application/json" \ -d '{ "label": "Head office trunk", "auth_mode": "registration", "proxy": "sip.example-carrier.com", "transport": "udp", "username": "sautikit_100", "password": "…" }'{ "auth_mode": "registration", "label": "string", "realm": "string", "proxy": "string", "transport": "udp", "auth_username": "string", "has_password": true, "signaling_ips": [ "string" ], "media_ips": [ "string" ], "country_code": "string", "outbound_number_format": "string", "outbound_codec_prefs": "string", "outbound_media_security": "string", "status": "pending", "last_error": "string"}{ "error": { "code": "validation.bad_request", "message": "string", "request_id": "string", "details": [ "string" ] }}{ "error": { "code": "validation.bad_request", "message": "string", "request_id": "string", "details": [ "string" ] }}Get the workspace's SIP trunk GET
A workspace has at most one SIP trunk. Returns `sip_trunk.not_found` if none has been connected.
Update the workspace's SIP trunk PUT
Updates the workspace's single trunk — there is no trunk ID in the path because a workspace has only ever had, at most, one. It also creates one when none exists, for clients written before `POST` existed; `POST` is the clearer way to connect a new trunk, because it refuses to overwrite one that is already there. Refused with `workspace.locked_to_platform_numbers` on a workspace that already buys numbers from Sautikit. For a `registration` trunk, the password is pushed to Sautikit's box and a REGISTER is attempted synchronously, inside this same request (up to ~20s). The response's `status` reflects the outcome immediately: `active` on success, `failed` (with `last_error`) if the provider rejected the credentials or was unreachable — the workspace stays unlocked either way until it actually succeeds. Omit `password` on a later `PUT` to keep the password already on file (e.g. when only changing a label); a password rotation re-signals every active BYO number on the trunk automatically. For an `ip_allowlist` (signaling) trunk, this only submits the trunk for review — it lands at `pending_approval` and does not go live, or lock the workspace, until a person on Sautikit's operations team approves it and installs both the signalling and media IPs on the box.