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

# Create SIP trunk API

> Create a SIP trunk to your own carrier via POST /trunk/trunks, with gateways for outbound calls and carrier IPs for inbound calls.

<Note>
  This endpoint returns `200`, not `201`. Send the carrier's credentials only if it challenges Bolna's outbound calls; a carrier that authenticates Bolna by IP needs Bolna's signalling IPs from [`GET /trunk/pop`](/docs/api-reference/trunks/connection_details) on its allowlist instead.
</Note>

## Next steps

* [Add your carrier's numbers](/docs/api-reference/trunks/add_number) to the trunk.
* Point your carrier at the values from [`GET /trunk/pop`](/docs/api-reference/trunks/connection_details).


## OpenAPI

````yaml POST /trunk/trunks
openapi: 3.1.0
info:
  title: Bolna API
  description: >-
    Use and leverage Bolna Voice AI using APIs through HTTP requests from any
    language in your applications and workflows.
  license:
    name: MIT
  version: 1.0.0
servers:
  - url: https://api.bolna.ai
    description: Production server
security:
  - bearerAuth: []
tags:
  - name: SIP trunks
    description: >-
      Bring-your-own-carrier SIP trunks, their gateways, carrier IPs and
      numbers.
paths:
  /trunk/trunks:
    post:
      tags:
        - SIP trunks
      description: >
        Creates a SIP trunk to your carrier. Gateways (where outbound calls are
        sent) and carrier IPs (the addresses inbound calls are accepted from)
        can be set here or added later. Numbers are added after the trunk
        exists, with the [Add number API](/api-reference/trunks/add_number).


        Carrier IPs are loaded into Bolna's SIP edge when the trunk is created,
        normally before the response returns. The carrier password is stored
        encrypted and is never returned.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TrunkCreateRequest'
            examples:
              credentials:
                summary: Inbound and outbound, carrier credentials
                value:
                  name: Telnyx production
                  preset: telnyx
                  channels: 20
                  transport: udp
                  codecs: PCMU,PCMA
                  outbound_leading_plus_enabled: true
                  auth_username: bolna-outbound
                  auth_password: <carrier-issued password>
                  gateways:
                    - host: sip.example.com
                      port: 5060
                      priority: 0
                  ip_acls:
                    - 203.0.113.10
                    - 198.51.100.0/28
              ip-authenticated:
                summary: Carrier that authenticates Bolna by IP
                value:
                  name: Custom carrier
                  preset: custom
                  gateways:
                    - host: 203.0.113.20
                      port: 5060
                      priority: 0
                    - host: 203.0.113.21
                      port: 5060
                      priority: 1
                  ip_acls:
                    - 203.0.113.20/31
      responses:
        '200':
          description: >-
            The created trunk. Bare IP addresses in `ip_acls` come back as `/32`
            networks.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Trunk'
        '400':
          description: >
            A value was refused, for example:

            - a gateway `host` is a private or non-public address (`gateway
            10.0.0.5 is not a public address`), or an IPv4 literal in a
            non-canonical form

            - a carrier IP is IPv6, not a public network, or broader than `/16`

            - `default_inbound_agent_id` is not one of your agents (`agent not
            in tenant`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrunkMessageError'
              example:
                message: 198.48.0.0/12 is broader than /16
        '409':
          description: >-
            A gateway host or address is already dialled by another trunk on
            Bolna, and this trunk or that one has no carrier credentials
            (`gateway_conflict`). Set `auth_username` and `auth_password` on
            both, or use a different gateway.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrunkCodedError'
              example:
                detail:
                  code: gateway_conflict
                  message: >-
                    gateway sip.example.com is used by another trunk; a shared
                    gateway needs carrier credentials on every trunk that uses
                    it
        '422':
          description: >
            The body failed validation, for example: `name` is empty or over 100
            characters, `channels` is outside 1–1000, `transport` is `tls`,
            `codecs` names something other than `PCMU`/`PCMA`, only one of
            `auth_username`/`auth_password` is set, a credential contains a
            quote, `|`, `{`, `}`, `\` or a control character, there are more
            than 8 gateways or 32 carrier IPs, a carrier IP is not an address or
            CIDR, or the body has an unknown field.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrunkValidationError'
              example:
                detail:
                  - loc:
                      - body
                      - channels
                    msg: Input should be less than or equal to 1000
                    type: less_than_equal
components:
  schemas:
    TrunkCreateRequest:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: Display name.
          example: Telnyx production
        description:
          type: string
          maxLength: 500
          description: Free-text note for your own reference.
        preset:
          type: string
          enum:
            - plivo
            - telnyx
            - twilio
            - exotel
            - vobiz
            - vonage
            - custom
          description: >-
            The carrier the trunk is for. Stored as a label; the API does not
            fill any other field from it.
        inbound_enabled:
          type: boolean
          default: true
          description: Accept inbound calls from the trunk's carrier IPs.
        outbound_enabled:
          type: boolean
          default: true
          description: Allow outbound calls from the trunk's numbers.
        channels:
          type: integer
          minimum: 1
          maximum: 1000
          default: 10
          description: >-
            Maximum concurrent calls on the trunk, inbound and outbound
            combined. An inbound call over the limit is refused with SIP `486
            Busy Here`; an outbound call over it waits in your call queue until
            a channel is free.
        default_inbound_agent_id:
          type: string
          description: >-
            Agent that answers inbound calls to any of the trunk's numbers that
            has no `agent_id` of its own. Must be one of your agents.
        auth_username:
          type: string
          minLength: 1
          description: >-
            Username your carrier issued for Bolna's outbound calls, sent when
            the carrier challenges the INVITE (SIP 401/407). Set together with
            `auth_password`, or leave both out when the carrier authenticates
            Bolna by IP. Must not contain a quote, `|`, `{`, `}`, `\` or a
            control character.
        auth_password:
          type: string
          minLength: 1
          format: password
          description: >-
            Password for `auth_username`. Stored encrypted; never returned. The
            same character rules apply.
        transport:
          type: string
          enum:
            - udp
            - tcp
          default: udp
          description: SIP transport towards your carrier's gateways. `tls` is rejected.
        outbound_leading_plus_enabled:
          type: boolean
          default: false
          description: >-
            Send the dialled number to the carrier with a leading `+`. When
            `false`, the number is sent as digits only.
        codecs:
          type: string
          default: PCMU,PCMA
          description: >-
            Comma-separated codecs offered to the carrier, in order of
            preference. Any non-empty subset of `PCMU` and `PCMA`; values are
            upper-cased.
        gateways:
          type: array
          maxItems: 8
          description: >-
            Carrier SIP hosts that outbound calls are sent to. Leave empty for
            an inbound-only trunk.
          items:
            $ref: '#/components/schemas/TrunkGatewayRequest'
        ip_acls:
          type: array
          maxItems: 32
          description: >-
            Addresses or CIDR ranges your carrier sends inbound calls from.
            Public IPv4, no broader than `/16`. A bare address is stored as a
            `/32`.
          items:
            type: string
          example:
            - 203.0.113.10
            - 198.51.100.0/28
    Trunk:
      description: A SIP trunk with its gateways, carrier IPs, numbers and live status.
      allOf:
        - $ref: '#/components/schemas/TrunkSettings'
        - type: object
          properties:
            health:
              type: string
              enum:
                - ready
                - waiting_for_carrier
                - degraded
                - disabled
              description: >
                - `ready`: calls have crossed the trunk and the carrier is not
                failing them.

                - `waiting_for_carrier`: no call has crossed the trunk yet.

                - `degraded`: the carrier failed three or more recent outbound
                calls with no answered call since.

                - `disabled`: `is_active` is `false`.
            active_calls:
              type: integer
              description: Calls holding one of the trunk's channels right now.
              example: 0
            gateways:
              type: array
              description: Carrier SIP hosts outbound calls are sent to.
              items:
                $ref: '#/components/schemas/TrunkGateway'
            ip_acls:
              type: array
              description: Carrier IPs inbound calls are accepted from.
              items:
                $ref: '#/components/schemas/TrunkCarrierIp'
            numbers:
              type: array
              description: The trunk's phone numbers, oldest first.
              items:
                $ref: '#/components/schemas/TrunkNumber'
      example:
        id: 3f2b8c1e-6d4a-4f7b-9e2c-1a5d7b9c0e21
        name: Telnyx production
        description: null
        preset: telnyx
        is_active: true
        inbound_enabled: true
        outbound_enabled: true
        channels: 20
        default_inbound_agent_id: null
        auth_username: bolna-outbound
        transport: udp
        codecs: PCMU,PCMA
        outbound_leading_plus_enabled: true
        last_inbound_at: null
        last_outbound_at: null
        converted_from_byot_trunk_id: null
        organization_id: 7a1c2e3f-4b5d-4c6e-8f90-a1b2c3d4e5f6
        sub_account_id: null
        user_id: null
        created_at: '2026-10-16T09:12:44.512381+00:00'
        updated_at: '2026-10-16T09:12:44.512381+00:00'
        is_deleted: false
        deleted_at: null
        health: waiting_for_carrier
        active_calls: 0
        gateways:
          - id: 8b1d4e2f-0a3c-4d5e-9f60-7182a3b4c5d6
            trunk_id: 3f2b8c1e-6d4a-4f7b-9e2c-1a5d7b9c0e21
            host: sip.example.com
            resolved_ips:
              - 203.0.113.30
            port: 5060
            priority: 0
            created_at: '2026-10-16T09:12:44.512381+00:00'
            updated_at: '2026-10-16T09:12:44.512381+00:00'
            is_deleted: false
            deleted_at: null
        ip_acls:
          - id: c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f
            trunk_id: 3f2b8c1e-6d4a-4f7b-9e2c-1a5d7b9c0e21
            cidr: 203.0.113.10/32
            created_at: '2026-10-16T09:12:44.512381+00:00'
            updated_at: '2026-10-16T09:12:44.512381+00:00'
            is_deleted: false
            deleted_at: null
          - id: d3e4f5a6-b7c8-4d9e-8f01-2b3c4d5e6f7a
            trunk_id: 3f2b8c1e-6d4a-4f7b-9e2c-1a5d7b9c0e21
            cidr: 198.51.100.0/28
            created_at: '2026-10-16T09:12:44.512381+00:00'
            updated_at: '2026-10-16T09:12:44.512381+00:00'
            is_deleted: false
            deleted_at: null
        numbers: []
    TrunkMessageError:
      type: object
      description: >-
        Error body for `400`, `403` and `404` responses from the SIP trunk
        endpoints.
      required:
        - message
      properties:
        message:
          type: string
          description: >-
            Human-readable reason. On a `404` it names the ID that was not
            found.
    TrunkCodedError:
      type: object
      description: >-
        Error body for `409` responses from the SIP trunk endpoints.
        `detail.code` is stable and machine-readable.
      required:
        - detail
      properties:
        detail:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - number_taken
                - gateway_conflict
                - convert_in_progress
                - conflict
              description: >
                - `number_taken`: the number is already live on a trunk.

                - `gateway_conflict`: a gateway is shared with another trunk
                without carrier credentials on both.

                - `convert_in_progress`: the trunk is mid-migration from a
                legacy SIP trunk.

                - `conflict`: the change clashes with an existing record.
            message:
              type: string
              description: Human-readable reason.
    TrunkValidationError:
      type: object
      description: >-
        Error body for `422` responses from the SIP trunk endpoints. The
        rejected input is never echoed back.
      properties:
        detail:
          type: array
          items:
            type: object
            properties:
              loc:
                type: array
                items:
                  oneOf:
                    - type: string
                    - type: integer
                description: Where the error is, such as `["body", "channels"]`.
              msg:
                type: string
                description: What is wrong.
              type:
                type: string
                description: >-
                  Pydantic error type, such as `value_error` or
                  `less_than_equal`.
    TrunkGatewayRequest:
      type: object
      required:
        - host
      properties:
        host:
          type: string
          description: >-
            The carrier's SIP host, as an IPv4 address or a DNS name. A literal
            address must be public.
          example: sip.example.com
        port:
          type: integer
          minimum: 1
          maximum: 65535
          default: 5060
          description: The carrier's SIP port.
        priority:
          type: integer
          minimum: 0
          maximum: 100
          default: 0
          description: Order in which gateways are tried; lowest first.
    TrunkSettings:
      type: object
      description: A SIP trunk's own settings.
      properties:
        id:
          type: string
          format: uuid
          description: Unique trunk ID.
          example: 3f2b8c1e-6d4a-4f7b-9e2c-1a5d7b9c0e21
        name:
          type: string
          description: Display name.
          example: Telnyx production
        description:
          type: string
          nullable: true
          description: Free-text note for your own reference.
        preset:
          type: string
          nullable: true
          enum:
            - plivo
            - telnyx
            - twilio
            - exotel
            - vobiz
            - vonage
            - custom
          description: The carrier the trunk was set up for.
          example: telnyx
        is_active:
          type: boolean
          description: >-
            When `false`, the trunk takes no inbound calls and places no
            outbound calls.
        inbound_enabled:
          type: boolean
          description: Whether the trunk accepts inbound calls from its carrier IPs.
        outbound_enabled:
          type: boolean
          description: Whether outbound calls can be placed from the trunk's numbers.
        channels:
          type: integer
          description: >-
            Maximum concurrent calls on the trunk, inbound and outbound
            combined.
          example: 20
        default_inbound_agent_id:
          type: string
          nullable: true
          description: >-
            Agent that answers inbound calls to any of the trunk's numbers that
            has no `agent_id` of its own.
        auth_username:
          type: string
          nullable: true
          description: >-
            Username your carrier issued, sent when the carrier challenges an
            outbound call (SIP 401/407). The password is never returned.
          example: bolna-outbound
        transport:
          type: string
          enum:
            - udp
            - tcp
          description: SIP transport used towards your carrier's gateways.
        codecs:
          type: string
          description: >-
            Codecs offered to the carrier, comma-separated, in order of
            preference.
          example: PCMU,PCMA
        outbound_leading_plus_enabled:
          type: boolean
          description: >-
            When `true`, the dialled number is sent to the carrier with a
            leading `+` (`+12025550178`); when `false`, as digits only
            (`12025550178`).
        last_inbound_at:
          type: string
          format: date-time
          nullable: true
          description: >-
            When the trunk last took an inbound call, updated at most once a
            minute. `null` until the first one.
        last_outbound_at:
          type: string
          format: date-time
          nullable: true
          description: >-
            When an outbound call was last placed through the trunk, updated at
            most once a minute. `null` until the first one.
        converted_from_byot_trunk_id:
          type: string
          nullable: true
          description: >-
            The legacy SIP trunk this trunk was migrated from, when Bolna
            migrated it for you. `null` otherwise.
        organization_id:
          type: string
          nullable: true
          description: >-
            The organisation that owns the trunk. `null` for an account outside
            an organisation.
        sub_account_id:
          type: string
          nullable: true
          description: >-
            The sub-account the trunk was created under. `null` for an
            organisation-level trunk.
        user_id:
          type: string
          nullable: true
          description: >-
            The owning user, for an account outside an organisation. `null` for
            an organisation's trunk.
        created_at:
          type: string
          format: date-time
          description: When the trunk was created.
        updated_at:
          type: string
          format: date-time
          description: When the trunk's settings last changed.
        is_deleted:
          type: boolean
          description: '`true` only in the response to a delete.'
        deleted_at:
          type: string
          format: date-time
          nullable: true
          description: When the trunk was deleted.
    TrunkGateway:
      type: object
      properties:
        id:
          type: string
          format: uuid
        trunk_id:
          type: string
          format: uuid
        host:
          type: string
          example: sip.example.com
        resolved_ips:
          type: array
          nullable: true
          items:
            type: string
          description: The public IPv4 addresses `host` resolved to when it was saved.
          example:
            - 203.0.113.30
        port:
          type: integer
          example: 5060
        priority:
          type: integer
          example: 1
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        is_deleted:
          type: boolean
        deleted_at:
          type: string
          format: date-time
          nullable: true
    TrunkCarrierIp:
      type: object
      properties:
        id:
          type: string
          format: uuid
        trunk_id:
          type: string
          format: uuid
        cidr:
          type: string
          description: The range as stored, always with a prefix length.
          example: 198.51.100.0/28
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        is_deleted:
          type: boolean
        deleted_at:
          type: string
          format: date-time
          nullable: true
    TrunkNumber:
      type: object
      properties:
        id:
          type: string
          format: uuid
          example: 5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9
        trunk_id:
          type: string
          format: uuid
          example: 3f2b8c1e-6d4a-4f7b-9e2c-1a5d7b9c0e21
        e164:
          type: string
          example: '+12025550143'
        destination_key:
          type: string
          example: '12025550143'
        agent_id:
          type: string
          nullable: true
          description: >-
            Agent that answers inbound calls to this number. When `null`, the
            trunk's `default_inbound_agent_id` answers.
          example: 123e4567-e89b-12d3-a456-426614174000
        last_inbound_at:
          type: string
          format: date-time
          nullable: true
          description: >-
            When this number last took an inbound call, updated at most once a
            minute.
        last_outbound_at:
          type: string
          format: date-time
          nullable: true
          description: >-
            When an outbound call was last placed from this number, updated at
            most once a minute.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        is_deleted:
          type: boolean
        deleted_at:
          type: string
          format: date-time
          nullable: true
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.