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

# Register or update your SIP gateway

> Send inbound calls for your lines to your own SIP gateway — a PBX, contact-centre platform or SBC you run. Once registered, when someone calls one of your lines, Spenza's voice nodes send the SIP `INVITE` to `sipIngressIp:sipPort` over `transport` instead of ringing the SIM.

**Each call replaces the whole configuration.** Nothing is merged: an optional field you leave out goes back to its default, so send the full configuration every time.

**Which lines are routed**
- Every line on your account that has a phone number, narrowed by `didRanges`. Leave `didRanges` empty to route them all.
- A line bound to an AI voice agent stays with the agent.
- A number that two accounts both claim is never routed.
- Changes reach Spenza's voice nodes on their next routing sync, typically within seconds.

**Destination rules.** `sipIngressIp` must be a public IPv4 address — hostnames aren't accepted yet. Private, loopback, link-local, carrier-grade NAT, multicast, broadcast, reserved and documentation ranges (such as `203.0.113.0/24`) are refused, as are Spenza's own ranges. `sipPort` must be 1024–65535.

Spenza doesn't send SIP credentials to your gateway, so restrict it by source address instead — ask support@spenza.com for the addresses Spenza's voice nodes call from.

**Contract exceptions**
1. Needs an **Admin-tier** API key. A token from a read-only or read-write key gets `401` `Access to this endpoint is forbidden`.
2. **Not on the standard envelope.** Success is a flat `{ message, sipConfig, isSIPEnabled }`, and errors use the shapes shown below rather than `{ success: false, error }`.
3. A refused destination currently comes back as **`500`** with the reason in `message`, not `400`. Treat a `500` whose `message` starts with `SIP destination` or `SIP port` as a validation error and fix the request.




## OpenAPI

````yaml /openapi.yaml post /api/v1/customer/register-sip
openapi: 3.1.0
info:
  title: Spenza Partner API
  version: 3.0.0
  summary: >-
    Partner API for SIMs, eSIMs, plans, billing, users, groups and numbers —
    v1.1 surface.
  description: >
    The **Spenza Partner API** lets you manage SIMs, eSIMs, plans,
    subscriptions,

    billing, users, phone numbers, teams and device/SIM/user groups
    programmatically.


    All endpoints follow one consistent standard:


    - **Auth** — exchange your API `key` + `secret` for a bearer token, then
    send
      `Authorization: Bearer <token>` on every request.
    - **Envelopes** — success responses are `{ success, data, meta? }`; errors
    are
      `{ success: false, error: { code, message, details? } }`.
    - **Pagination** — list endpoints accept `page` (1-indexed) and `pageSize`
      (max 100); totals are returned in `meta`.
    - **Idempotency** — many write endpoints honor an optional `Idempotency-Key`
      request header so a retried call replays the original result instead of
      repeating the side effect.
    - **Async operations** — a handful of endpoints (eSIM provisioning, port-in,
      the `/api/v3/...` purchase variants, top-up) return `202` (`201` for
      top-up) with a `transactionId` + `statusEndpoint` instead of the finished
      resource. Poll
      `GET /api/v3/transactions/{transactionId}` (no auth required) until the
      transaction reaches a terminal status.

    Two resources are documented exceptions. **Port-in submission** lives at

    `POST /api/v3/port-in`, requires an Admin-tier account role rather than just
    a

    valid bearer token, and is **not** on the standard envelope (see that
    operation's

    description for its exact response shape). **SIP gateway configuration**
    lives at

    `/api/v1/customer/register-sip`, needs an Admin-tier API key, and is also
    **not**

    on the standard envelope.
  contact:
    name: Spenza API Support
    email: support@spenza.com
    url: https://spenza.com
  license:
    name: Proprietary
    url: https://spenza.com/terms
  x-logo:
    url: https://spenza.com/logo.png
    altText: Spenza
servers:
  - url: https://api.spenza.com
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Authentication
    description: Exchange API credentials for a bearer token.
  - name: SIMs
    description: List, inspect, assign and manage SIMs.
  - name: Subscriptions
    description: Plan subscriptions attached to SIMs.
  - name: Plans & Catalog
    description: Purchasable plans and SIM products.
  - name: eSIM
    description: Provision eSIMs and fetch activation QR / install status.
  - name: Billing
    description: Invoices, credit balance and top-ups.
  - name: Users
    description: End users (employees) on your account that SIMs/devices are assigned to.
  - name: Orders & Transactions
    description: Order history and financial transactions.
  - name: Numbers & Port-in
    description: Check port-in eligibility and port existing numbers into Spenza.
  - name: Messaging
    description: Send outbound SMS from a provisioned number.
  - name: Webhooks
    description: Register endpoints for operator SMS/voice events and inspect deliveries.
  - name: Voice Quality
    description: >-
      Test a line's call quality: call a Spenza test number from a line on your
      account and receive a scored report on your webhook.
  - name: SIP Gateway
    description: >-
      Deliver inbound calls for your lines to your own SIP gateway (PBX,
      contact-centre platform or SBC).
  - name: Notifications
    description: In-account notifications and delivery preferences.
  - name: Device Groups
    description: Organize devices into named groups with spend/data limits.
  - name: SIM Groups
    description: Organize SIMs into named groups with spend/data limits.
  - name: User Groups
    description: Organize users (departments) into named groups with spend/data limits.
  - name: Devices
    description: The device catalog (phones/tablets) and their assignment to users.
  - name: Team Members
    description: Admins who manage your Spenza account (distinct from Users/end-users).
  - name: Async Status
    description: Universal status poll for any asynchronous (v3) operation.
paths:
  /api/v1/customer/register-sip:
    post:
      tags:
        - SIP Gateway
      summary: Register or update your SIP gateway
      description: >
        Send inbound calls for your lines to your own SIP gateway — a PBX,
        contact-centre platform or SBC you run. Once registered, when someone
        calls one of your lines, Spenza's voice nodes send the SIP `INVITE` to
        `sipIngressIp:sipPort` over `transport` instead of ringing the SIM.


        **Each call replaces the whole configuration.** Nothing is merged: an
        optional field you leave out goes back to its default, so send the full
        configuration every time.


        **Which lines are routed**

        - Every line on your account that has a phone number, narrowed by
        `didRanges`. Leave `didRanges` empty to route them all.

        - A line bound to an AI voice agent stays with the agent.

        - A number that two accounts both claim is never routed.

        - Changes reach Spenza's voice nodes on their next routing sync,
        typically within seconds.


        **Destination rules.** `sipIngressIp` must be a public IPv4 address —
        hostnames aren't accepted yet. Private, loopback, link-local,
        carrier-grade NAT, multicast, broadcast, reserved and documentation
        ranges (such as `203.0.113.0/24`) are refused, as are Spenza's own
        ranges. `sipPort` must be 1024–65535.


        Spenza doesn't send SIP credentials to your gateway, so restrict it by
        source address instead — ask support@spenza.com for the addresses
        Spenza's voice nodes call from.


        **Contract exceptions**

        1. Needs an **Admin-tier** API key. A token from a read-only or
        read-write key gets `401` `Access to this endpoint is forbidden`.

        2. **Not on the standard envelope.** Success is a flat `{ message,
        sipConfig, isSIPEnabled }`, and errors use the shapes shown below rather
        than `{ success: false, error }`.

        3. A refused destination currently comes back as **`500`** with the
        reason in `message`, not `400`. Treat a `500` whose `message` starts
        with `SIP destination` or `SIP port` as a validation error and fix the
        request.
      operationId: registerSip
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - sipIngressIp
              properties:
                sipIngressIp:
                  type: string
                  description: >-
                    Public IPv4 address of your gateway. See the destination
                    rules above.
                  example: 44.205.10.20
                sipPort:
                  type: integer
                  minimum: 1024
                  maximum: 65535
                  default: 5060
                  example: 5060
                transport:
                  type: string
                  enum:
                    - udp
                    - tcp
                    - tls
                  default: udp
                codecs:
                  type: array
                  items:
                    type: string
                  default:
                    - ulaw
                    - alaw
                  description: >-
                    Stored and returned, but not applied yet — calls are offered
                    with the voice node's standard codec set.
                didRanges:
                  type: array
                  items:
                    type: string
                  default: []
                  description: >-
                    Which of your numbers to route. Each entry is an exact E.164
                    number (`+14155550123`) or an E.164 prefix ending in `*`
                    (`+1628555*`). Empty routes every line on your account.
                    Entries in any other form — such as `+1415000xxxx` — match
                    nothing.
                  example:
                    - '+14155550123'
                    - +1628555*
                allowedSourceIps:
                  type: array
                  items:
                    type: string
                  default: []
                  description: Stored and returned, but not enforced yet.
                  example:
                    - 44.205.10.20
                isSIPEnabled:
                  type: boolean
                  default: true
                  description: >-
                    Send `false` to save the configuration without routing any
                    calls. `GET` then returns `sipConfig: null`.
            example:
              sipIngressIp: 44.205.10.20
              sipPort: 5060
              transport: udp
              didRanges:
                - '+14155550123'
                - +1628555*
      responses:
        '200':
          description: Saved. `sipConfig` shows every field with defaults filled in.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: SIP configuration registered successfully
                  sipConfig:
                    $ref: '#/components/schemas/SipConfig'
                  isSIPEnabled:
                    type: boolean
                    example: true
              example:
                message: SIP configuration registered successfully
                sipConfig:
                  sipIngressIp: 44.205.10.20
                  sipPort: 5060
                  transport: udp
                  codecs:
                    - ulaw
                    - alaw
                  didRanges:
                    - '+14155550123'
                    - +1628555*
                  allowedSourceIps: []
                isSIPEnabled: true
        '400':
          description: >-
            A field failed type validation — `sipIngressIp` missing, `transport`
            not one of `udp`/`tcp`/`tls`, or a wrong type. `message` is an
            array, one entry per failure.
          content:
            application/json:
              examples:
                missingIp:
                  value:
                    statusCode: 400
                    message:
                      - sipIngressIp must be a string
                      - sipIngressIp should not be empty
                    error: Bad Request
                badTransport:
                  value:
                    statusCode: 400
                    message:
                      - >-
                        transport must be one of the following values: udp, tcp,
                        tls
                    error: Bad Request
        '401':
          $ref: '#/components/responses/SipUnauthorized'
        '500':
          description: >-
            The destination was refused (see the destination rules), or the save
            failed. `message` is a plain string.
          content:
            application/json:
              examples:
                privateAddress:
                  value:
                    message: SIP destination cannot be a private address
                notIpv4:
                  value:
                    message: >-
                      SIP destination must be an IPv4 address, for example
                      203.0.113.10
                badPort:
                  value:
                    message: SIP port must be between 1024 and 65535
components:
  schemas:
    SipConfig:
      type: object
      description: Where Spenza delivers inbound calls for your lines.
      properties:
        sipIngressIp:
          type: string
          description: Public IPv4 address of your SIP gateway.
          example: 44.205.10.20
        sipPort:
          type: integer
          minimum: 1024
          maximum: 65535
          example: 5060
        transport:
          type: string
          enum:
            - udp
            - tcp
            - tls
          example: udp
        codecs:
          type: array
          items:
            type: string
          description: Stored and returned, but not applied yet.
          example:
            - ulaw
            - alaw
        didRanges:
          type: array
          items:
            type: string
          description: Which of your numbers are routed. Empty routes every line.
          example:
            - '+14155550123'
            - +1628555*
        allowedSourceIps:
          type: array
          items:
            type: string
          description: Stored and returned, but not enforced yet.
          example:
            - 44.205.10.20
  responses:
    SipUnauthorized:
      description: >-
        Missing, malformed or expired bearer token (`errors` array), or a token
        from a read-only or read-write key (`Access to this endpoint is
        forbidden`). Neither is on the standard envelope.
      content:
        application/json:
          examples:
            badToken:
              value:
                errors:
                  - message: Token error
            keyTier:
              value:
                statusCode: 401
                message: Access to this endpoint is forbidden
                error: Unauthorized
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Bearer token obtained from `POST /api/v1.1/auth/token`.

````

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