---
title: "Create a SIP trunk"
method: POST
path: "/api/v1/trunks"
tags: ["SIP Trunks"]
---

# Create a SIP trunk

`POST /api/v1/trunks`

## Request

- Base URL: `https://api.callnode.io`
- URL: `https://api.callnode.io/api/v1/trunks`
- Auth: API key in header `X-Api-Key` and API key in header `X-Timestamp` and API key in header `X-Signature`

Creates a saved trunk. A trunk can be egress (outbound BYOC delivery to
your SIP host), ingress (accept inbound calls from your PBX/SBC), or
both — you must supply an `egressHost` and/or one or more `allowedIps`.

Requires a verified (KYC) account. Trunks beyond your plan's included
count incur your plan's additional-trunk monthly fee: the first attempt
returns `402 monthly_fee_confirmation_required`; re-submit with
`confirmMonthlyFee: true` to acknowledge the fee and create the trunk.

## Request body

- CreateTrunkRequest: Provide `name` plus at least one direction's config — an `egressHost` (egress), one or more `allowedIps` (IP-authenticated ingress), or `registrationEnabled: true` (registration ingress). A request with none of them is rejected with `no_config`.
  - `name` string, required
  - `egressHost` string: Hostname or IP of your SIP endpoint. Setting it enables egress delivery. Must be a public host.
  - `egressPort` integer: Defaults to 5060, or 5061 when `egressTransport` is `tls`.
  - `egressTransport` 'udp' | 'tcp' | 'tls'
  - `allowedIps` string[]: Public IPv4 source IPs for ingress.
  - `ingressEnabled` boolean
  - `registrationEnabled` boolean: Create a registration trunk for a PBX with no static public IP: your PBX registers to CallNode over TLS instead of being allow-listed by address, and calls we place to you go to whatever contact it last registered from. The response carries `sipPassword` once.
  - `defaultPhoneNumberId` string: One of your numbers' ids — the default ingress agent.
  - `outboundCliNumberId` string: One of your numbers' ids — caller ID presented on termination calls when the From is not an owned number.
  - `confirmMonthlyFee` boolean: Set true to acknowledge your plan's additional-trunk fee when you are beyond the included count.

## Response `201`

Trunk created.

- SipTrunk: A saved SIP trunk — egress (outbound BYOC), ingress (inbound from your PBX/SBC), or both.
  - `id` string
  - `name` string: 1–64 characters. Unique across your active trunks.
  - `status` 'active' | 'disabled'
  - `egressEnabled` boolean: Whether outbound (BYOC) delivery to `egressHost` is enabled.
  - `egressHost` string, nullable: Hostname or IP of your SIP endpoint for outbound delivery. Null for an ingress-only trunk.
  - `egressPort` integer
  - `egressTransport` 'udp' | 'tcp' | 'tls'
  - `ingressEnabled` boolean: Whether inbound calls from `allowedIps` are accepted.
  - `allowedIps` string[]: Public IPv4 addresses your PBX/SBC sends from — the accepted ingress source IPs.
  - `defaultPhoneNumberId` string, nullable: The id of one of your numbers — the default agent for ingress calls that do not match one of your own DIDs.
  - `outboundCliNumberId` string, nullable: The id of one of your numbers, presented as caller ID on outbound (termination) calls when the INVITE's From is not itself one of your numbers — the normal case for a registration trunk, whose From carries its auth username. A From that IS one of your live numbers always wins, so a multi-number PBX can still present per-call caller ID.
  - `monthlyFeeApplies` boolean: True when this trunk is beyond your plan's included count and is billed the additional-trunk fee.
  - `nextBillingDate` string, date-time, nullable
  - `registration` object: Registration-trunk settings, for a PBX with no static public IP. Never contains the password or its digest.
    - `enabled` boolean: Whether this trunk authenticates by registration rather than by source IP.
    - `username` string, nullable: The SIP username your PBX registers with.
    - `domain` string: SIP domain / digest realm.
    - `server` string: Host and port your PBX registers to.
    - `transport` 'tls': Registration trunks are TLS-only.
    - `passwordSetAt` string, date-time, nullable
    - `registered` boolean: True when your PBX is registered right now.
    - `lastRegisteredAt` string, date-time, nullable
    - `lastRegisteredIp` string, nullable: The address your PBX last registered from.
  - `sipPassword` string: Present ONLY on the response that mints it — creating a registration trunk, enabling registration, or rotating credentials. It cannot be retrieved afterwards; store it immediately.
  - `sipPasswordNotice` string: Human-readable reminder that accompanies `sipPassword`.
  - `createdAt` string, date-time
  - `updatedAt` string, date-time

## Other responses

- `400`: Failed validation. Codes: `invalid_name`, `invalid_port`, `unsupported_transport`, `blocked_destination` (egress host resolves to a private/reserved address), `invalid_allowed_ips`, `invalid_default_number`, or `no_config` (neither an egress host nor any ingress IPs were provided).
- `401`: Missing, unknown, or badly signed credentials — check the HMAC and the timestamp skew.
- `402`: `monthly_fee_confirmation_required` — you are beyond your plan's included trunks; re-submit with `confirmMonthlyFee: true`. Or `insufficient_balance` — not enough wallet balance for the fee.
- `403`: `account_not_verified` — trunk creation requires a verified (KYC) account.
- `409`: `duplicate_name` — you already have an active trunk with that name.

---

[API](https://skmtc.dev/callnode/apis/callnode-outbound-api.md) · [All operations](https://skmtc.dev/callnode/apis/callnode-outbound-api/llms.txt) · [OpenAPI document](https://skmtc.dev/callnode/apis/callnode-outbound-api/revisions/c7da6ad06509?raw)
