Connect a SIP trunk number

View as Markdown
Brings a number the business ALREADY OWNS into the workspace over its SIP trunk, so the agent answers on it (`inbound`) and calls from it (`outbound`). Nothing is bought. The number is registered with the voice provider as `provider: sip_trunk` and takes the same call path as a purchased number. For a plain line without a trunk, use call forwarding instead (`GET /api/phone-numbers/{id}/forwarding-instructions`). Give at least one of `inbound` / `outbound`; both is the full experience. `inbound` needs either `allowedAddresses` (an IP allow-list) or `credentials` (digest auth), because an open trunk answers to anyone. The agent must exist, be provisioned, and serve voice. **One number, two channels.** If the number is already this workspace's WhatsApp line, voice is ADDED to that same entry (whether the line is live, offline, or was disconnected) and, while the line has a WhatsApp status, the agent must also serve WhatsApp (400), because one number is answered by one agent. A number that is already a voice number here, or that lives in another workspace, is a 409. The plan's cap counts active voice numbers only, and connecting adds one (402 when full; no add-on arithmetic applies). **Side effects.** Registers the number and trunk configuration with the voice engine, binds the agent, stores the number (only the outbound trunk address is kept, as `sipHost`; trunk credentials go to the voice provider and are never stored by Jelliu), and writes an audit entry. If binding or storing fails, the registration is deleted before the error is returned. **Idempotency.** Not idempotent: once the number is connected, a repeat returns 409. Concurrent connects of the same number are serialized by a 2-minute lock (409 for the loser). **Access** - **Required scope:** `full`. Signed-in users need the admin or owner role. - **Rate limit:** General API — per plan: 120 to 600 requests/min per workspace. See [Rate limits](/rate-limits). - **Plan:** Requires an active plan; counts towards the plan's voice-number cap (Starter 1, Growth 3, Business 10).

Authentication

AuthorizationBearer
Workspace API key: `jl_` followed by 64 lowercase hex characters, created by the workspace owner in the dashboard (**Settings → API Keys**) and sent as `Authorization: Bearer jl_...`. The plaintext is shown once, at creation; Jelliu stores only a SHA-256 hash. A workspace can hold up to 25 active keys. | Scope | GET / HEAD | POST / PUT / PATCH / DELETE | Admin-only routes | | --- | --- | --- | --- | | `read` | Yes | No | No | | `write` | Yes | Yes | No | | `full` | Yes | Yes | Yes | Operations restricted to admins or owners reject keys without the `full` scope with `403`, and say so in their description. No key, whatever its scope, can mint or revoke API keys or rotate a webhook secret — that requires a signed-in owner session. A revoked key stops authenticating within about 10 seconds. See [Authentication](/authentication).

Request

This endpoint expects an object.
agentIdstringRequiredformat: "uuid"
A provisioned voice agent of this workspace. If the number is also the workspace's WhatsApp line, the agent must serve WhatsApp too.
phoneNumberstringRequiredformat: "^\+[1-9]\d{1,14}$"

The business’s own number, in E.164 format (phoneNumber must be E.164 (+…) otherwise).

labelstringOptional1-120 characters

Display name, trimmed. Defaults to <phoneNumber> (own number); when voice is added to the WhatsApp line, the line’s existing label is kept unless you send one.

inboundobjectOptional

What the business’s PBX or carrier sends to the agent, so the agent answers on the number. Needs a non-empty allowedAddresses or credentials (400 inbound needs either allowedAddresses (ACL) or credentials (digest auth) — an open trunk answers to anyone).

outboundobjectOptional
Where the agent dials out through, so outbound calls show the business's own number.

Response

The connected number (a new entry, or the WhatsApp line with voice added).

dataobject

One number the workspace holds. channels says what it answers on; provider says where it lives. Field names are camelCase. Returned by every /api/phone-numbers endpoint.

Errors

400
Bad Request Error
401
Unauthorized Error
402
Payment Required Error
403
Forbidden Error
404
Not Found Error
409
Conflict Error
429
Too Many Requests Error
502
Bad Gateway Error
503
Service Unavailable Error