> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# SIP trunks

> Provision SIP trunks that connect your PBX to Lua, manage their IP allow list and digest credentials, and set the agent that answers calls

A SIP trunk is the SIP endpoint an [organization](/concepts/credentials) points its PBX or carrier at so calls reach a Lua [voice](/concepts/voice) agent. These routes create trunks, manage the IP allow list (ACL) and digest-authentication users that secure them, and set the default [agent](/concepts/agents) for a trunk. For the setup walkthrough, see [Voice calls](/channels/voice-calls).

*Verified against lua-cli 3.33.0.*

## Base URL and authentication

Every route sits under `https://api.heylua.ai/developer/orgs/:orgId/sip-trunks` and takes `Authorization: Bearer <<YOUR_API_KEY>>`; the [REST API overview](/reference/rest/overview) covers keys, scopes, and the error envelope. Every route checks a `telephony` scope on the organization in the path except agent linking, which checks `telephony:write` on the agent and `telephony:read` on the organization: `telephony:read` lists and inspects, `telephony:write` creates trunks, ACL entries, and credentials and rotates passwords, and `telephony:manage` deletes trunks, ACL entries, credentials, and the agent link. There is no CLI equivalent for this family. A `POST` answers `201`.

## SIP trunk object

<ResponseField name="id" type="string">Trunk ID; use it in the path of the other routes.</ResponseField>
<ResponseField name="domain" type="string">SIP domain assigned to the trunk.</ResponseField>
<ResponseField name="sipEndpoint" type="string">`sip:*@<domain>.sip-<region>.vonage.com`, the endpoint to send calls to.</ResponseField>
<ResponseField name="region" type="string">One of `us`, `eu`, `ap`.</ResponseField>
<ResponseField name="status" type="string">One of `provisioning`, `active`, `failed`, `suspended`, `deleted`.</ResponseField>
<ResponseField name="tls" type="string">TLS requirement: `optional`, `always`, or `never`.</ResponseField>
<ResponseField name="srtp" type="string">SRTP requirement: `optional`, `always`, or `never`.</ResponseField>
<ResponseField name="digestAuthEnabled" type="boolean">Whether digest-authentication users can be created.</ResponseField>
<ResponseField name="digestUsers" type="string[]">Digest usernames on the trunk. Passwords are never returned here.</ResponseField>

<ResponseField name="acl" type="object[]">
  Allowed source addresses.

  <Expandable title="properties">
    <ResponseField name="ip" type="string">CIDR, for example `203.0.113.0/24`.</ResponseField>
    <ResponseField name="description" type="string">Optional label.</ResponseField>
    <ResponseField name="createdAt" type="number">Unix time in milliseconds.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="defaultAgentId" type="string">The agent that answers calls on this trunk, once linked.</ResponseField>
<ResponseField name="createdAt" type="number">Unix time in milliseconds.</ResponseField>
<ResponseField name="createdBy" type="string">User ID of the creator.</ResponseField>
<ResponseField name="createdByEmail" type="string">Creator email, when known.</ResponseField>

<ResponseField name="connectionDetails" type="object">
  What to configure on your PBX.

  <Expandable title="properties">
    <ResponseField name="sipUri" type="string">The same host as `sipEndpoint` with a literal `{agent-id}` placeholder as the user part; substitute your own user part.</ResponseField>
    <ResponseField name="region" type="string">Region.</ResponseField>
    <ResponseField name="transport" type="string[]">`["TLS"]` when `tls` is `always`, otherwise `["UDP", "TCP", "TLS"]`.</ResponseField>
    <ResponseField name="port" type="number">`5061` when `tls` is `always`, otherwise `5060`.</ResponseField>
  </Expandable>
</ResponseField>

## Endpoints

### POST /developer/orgs/:orgId/sip-trunks

Provisions a trunk. Scope `telephony:write`. A trunk must have at least one ACL entry or digest authentication enabled; otherwise the trunk is recorded as `failed` and the call answers `400`. Answers `201` with the [SIP trunk object](#sip-trunk-object).

<ParamField body="region" type="string" default="us">One of `us`, `eu`, `ap`.</ParamField>
<ParamField body="acl" type="string[]">Source addresses in CIDR notation; not validated locally, the carrier rejects malformed entries.</ParamField>
<ParamField body="enableDigestAuth" type="boolean" default={false}>Allow digest-authentication users on this trunk.</ParamField>
<ParamField body="tls" type="string" default="optional">`optional`, `always`, or `never`.</ParamField>
<ParamField body="srtp" type="string" default="optional">`optional`, `always`, or `never`.</ParamField>

### GET /developer/orgs/:orgId/sip-trunks

Lists the organization's trunks, `failed` ones included and `deleted` ones excluded. Scope `telephony:read`.

**Response**

`200` with `{ trunks: SipTrunk[], count }`, where `count` is the page length.

### GET /developer/orgs/:orgId/sip-trunks/:trunkId

Returns one [SIP trunk object](#sip-trunk-object). Scope `telephony:read`. `404` when unknown or deleted.

### DELETE /developer/orgs/:orgId/sip-trunks/:trunkId

Deletes the trunk at the carrier and marks the record `deleted`, so it no longer appears in list or get; its credentials and ACL entries go with it. Scope `telephony:manage`.

**Response**

`200` with `{ "success": true }`.

### POST /developer/orgs/:orgId/sip-trunks/:trunkId/acl

Adds an address to the allow list. Scope `telephony:write`. A trunk holds at most 16 ACL entries. Answers `201` with the updated [SIP trunk object](#sip-trunk-object).

<ParamField body="ip" type="string" required>CIDR, for example `203.0.113.0/24`.</ParamField>
<ParamField body="description" type="string">Label for the entry.</ParamField>

### DELETE /developer/orgs/:orgId/sip-trunks/:trunkId/acl/:ip

Removes an address from the allow list. Scope `telephony:manage`. URL-encode the CIDR in the path (`/` becomes `%2F`). The last entry cannot be removed, even on a trunk with digest authentication; add its replacement first. Answers `200` with the updated object.

### POST /developer/orgs/:orgId/sip-trunks/:trunkId/credentials

Creates a digest-authentication user. Scope `telephony:write`. Requires `digestAuthEnabled`; a trunk holds at most 200 users. The password, 24 letters and digits, is returned once, in this response only.

<ParamField body="username" type="string" required>5 to 32 characters, alphanumeric and dashes, starting and ending with an alphanumeric character.</ParamField>

**Response**

`201` with `{ username, password }`.

### GET /developer/orgs/:orgId/sip-trunks/:trunkId/credentials

Lists digest usernames as `{ users: string[] }`. Scope `telephony:read`.

### DELETE /developer/orgs/:orgId/sip-trunks/:trunkId/credentials/:username

Deletes a digest user. Scope `telephony:manage`.

**Response**

`200` with `{ "success": true }`.

### POST /developer/orgs/:orgId/sip-trunks/:trunkId/credentials/:username/rotate

Generates a new password for a digest user, returned once. Scope `telephony:write`.

**Response**

`201` with `{ username, password }`.

### POST /developer/orgs/:orgId/sip-trunks/:trunkId/agents/:agentId

Sets the agent that answers calls on the trunk. Scope `telephony:write` on the agent, plus `telephony:read` on the organization. The agent must belong to the organization, and that is checked before the trunk lookup: a foreign agent answers `403` even for an unknown trunk. The route takes no body; per-URI-user and header routing are not available through this API. Answers `201` with the updated object and `defaultAgentId` set.

### DELETE /developer/orgs/:orgId/sip-trunks/:trunkId/agent

Removes the default agent. Scope `telephony:manage`. `400` when no agent is linked. Answers `200` with the updated object.

## Errors

| Status | Message                                                                    | Meaning                                                                                                                                  |
| ------ | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `At least one ACL entry or digest authentication is required for security` | Create body with no `acl` and `enableDigestAuth` false; pass an `acl` or enable digest auth, and delete the `failed` record that remains |
| `400`  | `Maximum 16 ACL entries allowed`                                           | ACL is full; remove an entry first                                                                                                       |
| `400`  | `IP already in ACL`                                                        | Duplicate CIDR; nothing to do                                                                                                            |
| `400`  | `Cannot remove last ACL entry. Add a replacement first.`                   | Removal would empty the ACL; add the new entry, then remove the old one                                                                  |
| `400`  | `Digest authentication is not enabled on this trunk`                       | Credential create on a trunk created without `enableDigestAuth`; create a trunk with it enabled                                          |
| `400`  | `Maximum 200 digest users allowed`                                         | User list is full; delete a user first                                                                                                   |
| `400`  | `Username must be 5-32 characters, alphanumeric and dashes only`           | Invalid username                                                                                                                         |
| `400`  | `Username already exists`                                                  | Duplicate username; rotate the existing user's password instead                                                                          |
| `400`  | `No agent is currently linked to this trunk`                               | Unlink with no link; nothing to do                                                                                                       |
| `403`  | `Agent does not belong to this organization`                               | Link across organizations; link an agent from the trunk's organization                                                                   |
| `403`  | `Insufficient permissions for this organization`                           | The key lacks `telephony:read` on the organization; grant the scope                                                                      |
| `404`  | `SIP trunk not found`, `IP not found in ACL`, `User not found`             | Unknown record; list and retry                                                                                                           |

## Example

Create a trunk that accepts calls from one address range, then read the endpoint to configure on the PBX.

<CodeGroup>
  ```ts TypeScript theme={null}
  const response = await fetch('https://api.heylua.ai/developer/orgs/<<YOUR_ORG_ID>>/sip-trunks', {
    method: 'POST',
    headers: { Authorization: 'Bearer <<YOUR_API_KEY>>', 'Content-Type': 'application/json' },
    body: JSON.stringify({ region: 'eu', acl: ['203.0.113.0/24'], tls: 'always', srtp: 'always' }),
  });
  if (!response.ok) throw new Error(`Trunk creation failed: ${response.status} ${await response.text()}`);
  const trunk: { id: string; status: string; connectionDetails: { sipUri: string; port: number; transport: string[] } } =
    await response.json();
  console.log(`Point your PBX at ${trunk.connectionDetails.sipUri}:${trunk.connectionDetails.port}`);
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.heylua.ai/developer/orgs/<<YOUR_ORG_ID>>/sip-trunks" \
    -H "Authorization: Bearer <<YOUR_API_KEY>>" \
    -H "Content-Type: application/json" \
    -d '{ "region": "eu", "acl": ["203.0.113.0/24"], "tls": "always", "srtp": "always" }'
  ```
</CodeGroup>

## See also

* [Voice calls](/channels/voice-calls) — connect a PBX or carrier to an agent
* [Phone numbers](/reference/rest/phone-numbers) — buy numbers instead of bringing your own
* [Voice](/reference/rest/voice) — voice agents, voice sessions, and outbound calls
* [REST API overview](/reference/rest/overview) — authentication, scopes, errors
