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

# Change how a gateway is dialed

> For manually connected gateways: the port and transport it is dialed on,
the dial rules that rewrite a destination for it, and — for a SIP gateway —
the connection mode and everything behind a registration. Port, transport,
and inbound rules replace what is stored, and an omitted or blank one reverts
to the default; `dialRules` replace the stored rules only when sent, and `[]`
clears them; `connection` keeps its value when omitted. If the
port or transport changes on a direct gateway that already has numbers, or
the connection mode changes, the outbound trunk is replaced on the cell — new
one first, so a failure leaves the working trunk in place — and a mode change
also resets the inbound allow-list (the relay alone for `register`). A change
to a registered gateway republishes the cell's relay snapshot
(`configPublished`). Live calls are unaffected.

Vodex dials every destination in E.164. A PBX matches its outbound routes
on the digits its own phones dial (`00` for abroad, `0` for national) and
refuses anything else, so `dialRules` rewrite a leading prefix: the
longest `match` wins, an unmatched number is dialed as is, and records
and identities stay E.164. The caller ID is rewritten only by `callerIdRules`.




## OpenAPI

````yaml /openapi/api-public-v1.yaml post /integrations/{id}/gateway
openapi: 3.0.3
info:
  title: Vodex API (v1)
  description: >
    The **public Vodex `/v1` REST API** — everything a customer's own systems

    integrate with: assistants, telephony (numbers, trunks, carrier

    integrations), calls and recordings, batch dialing, the SMS channel, and

    outbound webhooks.


    This is deliberately a **subset**. Account and console-only routes

    (`/v1/auth/*`, `/v1/api-keys`, `/v1/usage`), platform provisioning

    (`/v1/platform/*`), Vodex support operations, and endpoints other systems

    call inward (carrier delivery receipts, the agent worker, cron) are not

    part of the published surface and are not documented here.


    The **data plane** (LiveKit SFU + SIP + the agent worker) is not an HTTP

    API and is not described here: a call reaches the worker over LiveKit, with

    its config carried in participant metadata or read from the config snapshot

    this API publishes.


    ## Authentication


    | Credential | Sent as | Scope |

    |---|---|---|

    | **Tenant API key** | `Authorization: Bearer vdx_live_…` | One customer,
    full access — the credential for everything in this document |


    Mint an API key in the console (**Settings → API keys**). The plaintext is

    shown **once**, by the call that creates it, and no endpoint reads one back

    — a lost key is replaced, not recovered.


    Every route here also answers a signed-in console session, because the

    console is built on this same API. That is **not** an integration

    credential and is deliberately not described as one: it is the console's

    own cookie, it carries a role an API key never has, and it is free to

    change. Integrate with `vdx_live_`.


    ## Customers and tenants


    One thing, two words, and they are not interchangeable:


    - **Customer** is the product word — what a human calls the account, and
      what the console shows you.
    - **Tenant** is the same thing's identity on the wire and in storage:
      `tenantId` on every response.

    **A *workspace* is a third thing, and not a synonym for either.** Tenancy

    is mapped at the customer level deliberately: integrations (carrier, SMS,

    provider credentials) live above the workspaces that use them, so mapping

    workspaces to tenants would fragment one customer's telephony across

    tenants that then have to share credentials. A workspace is therefore a

    **label inside the tenant** (`externalWorkspaceRef`) — for filtering and

    attribution, never isolation. Anything that must not be shared between one

    customer's workspaces needs its own tenant.


    ## Tenancy


    There is **no tenant parameter**. Every tenant-scoped route derives its

    tenant from the credential and binds it for the whole request, so a caller

    cannot ask for another tenant's data by changing a query string. An id that

    belongs to somebody else answers `404`, never `403` — "no such call" is the

    only thing worth confirming.


    ## Response shapes


    Resources are returned **flat**, at the top level (`{ "id": "…", … }`) —

    there is no envelope object. Collections vary by age of the endpoint:


    - Newer lists are `{ "<plural>": [ … ], "total": n, "limit": n, "offset": n
    }`
      (`calls`, `batches`, `rows`, `attempts`, `messages`).
    - Older lists return a **bare JSON array** (`/v1/phone-numbers`,
      `/v1/sip-trunks`, `/v1/integrations`, `/v1/carriers`,
      `/v1/console/assistants`, `/v1/sms/configurations`).

    Deletes and side-effecting no-content operations return `{ "ok": true }`.


    ## Errors


    ```json

    { "error": "human-readable message" }

    ```


    A flat string, not a coded object — HTTP status is the machine signal.

    The one exception is quota exhaustion (`402`), which adds a stable

    `code: "quota_exhausted"` plus the counters the console renders.


    ## Pagination


    `?limit=` and `?offset=`, with `total` in the body. Defaults and ceilings

    differ per endpoint (calls 25/100, batch rows 50/500, attempts 100/500) and

    are stated on each operation.


    ## Conventions


    - **Phone numbers** are E.164 strings (`+14155551234`); `+91…` numbers are
      served from Mumbai, `+1…` from us-central1.
    - **Money** is a JSON number of **USD** (`costUsd`), not a decimal string.

    - **Timestamps** are ISO‑8601 UTC strings, except the calls list, which
      reports `mtime` / `startedAt` as epoch **milliseconds** and durations as
      **milliseconds**.
    - **Ids** are unprefixed: batches, rows, attempts and SMS rows are UUIDs;
      a call id is its LiveKit room name (`call-…`, or `console-…` for web
      calls); a phone number's id is `num-<digits>`; a trunk's is `trunk-…`.
    - **Secrets** (`vdx_live_…`, `whsec_…`) are returned exactly once, by the
      call that mints them. No endpoint reads one back — carrier and provider
      credentials are stored in Secret Manager and echoed only as a
      non-reversible fingerprint.
    - **Idempotency**: `POST /v1/sms` and `POST /v1/batches` **require** an
      `Idempotency-Key` header. Only successful (2xx) responses are replayed;
      a failed attempt releases the key so a corrected retry can reuse it.
  version: 1.0.0
  contact:
    name: Vodex Platform
    url: https://vodex.ai
  license:
    name: Proprietary
    url: https://vodex.ai/terms
servers:
  - url: https://apiv2.vodex.ai/v1
    description: Production
security:
  - ApiKeyBearer: []
tags:
  - name: Payments
    description: >-
      Card payments taken by keypad on a live call. The caller types the card on
      the phone; Vodex never receives it.
  - name: Assistants
    description: Assistant configurations (the agent's engine, prompt, slots, tools).
  - name: Console
    description: Reference data (model catalog, voices) and the web-call token.
  - name: Usage
    description: Minute usage against the plan.
  - name: Calls
    description: Place calls, read the call log, fetch traces and recordings.
  - name: Phone Numbers
    description: Number inventory and assistant mapping.
  - name: SIP Trunks
    description: Customer-owned (BYO) SIP trunks.
  - name: Carriers
    description: Carrier registry — what can be connected, and how.
  - name: Integrations
    description: Connected carrier accounts and their numbers.
  - name: Batches
    description: Batch dialing — lists in, calls out.
  - name: Number Rotation
    description: Per-number spacing, daily caps, and cooldown.
  - name: Callbacks
    description: >-
      "Call me Tuesday afternoon" — appointments booked mid-call, and dialed
      later.
  - name: SMS
    description: SMS configurations, credentials, preview, send, and message log.
  - name: Webhooks
    description: Outbound webhook configuration and delivery records.
  - name: Tenants
    description: How long this customer's calls are kept, and what that costs.
  - name: Contacts
    description: >-
      The people a tenant calls — records, channels, custom attributes, files,
      and CSV import.
  - name: Audiences
    description: >-
      Saved filters over contacts. Stored as a filter tree, never a frozen id
      list.
  - name: Campaigns
    description: An audience plus an assistant, executed as a batch.
  - name: Do Not Contact
    description: The address suppression list, and the person-level decision beside it.
paths:
  /integrations/{id}/gateway:
    post:
      tags:
        - Integrations
      summary: Change how a gateway is dialed
      description: >
        For manually connected gateways: the port and transport it is dialed on,

        the dial rules that rewrite a destination for it, and — for a SIP
        gateway —

        the connection mode and everything behind a registration. Port,
        transport,

        and inbound rules replace what is stored, and an omitted or blank one
        reverts

        to the default; `dialRules` replace the stored rules only when sent, and
        `[]`

        clears them; `connection` keeps its value when omitted. If the

        port or transport changes on a direct gateway that already has numbers,
        or

        the connection mode changes, the outbound trunk is replaced on the cell
        — new

        one first, so a failure leaves the working trunk in place — and a mode
        change

        also resets the inbound allow-list (the relay alone for `register`). A
        change

        to a registered gateway republishes the cell's relay snapshot

        (`configPublished`). Live calls are unaffected.


        Vodex dials every destination in E.164. A PBX matches its outbound
        routes

        on the digits its own phones dial (`00` for abroad, `0` for national)
        and

        refuses anything else, so `dialRules` rewrite a leading prefix: the

        longest `match` wins, an unmatched number is dialed as is, and records

        and identities stay E.164. The caller ID is rewritten only by
        `callerIdRules`.
      operationId: updateIntegrationGateway
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                port:
                  type: integer
                  minimum: 1
                  maximum: 65535
                transport:
                  type: string
                  enum:
                    - udp
                    - tcp
                    - tls
                dialRules:
                  type: array
                  maxItems: 10
                  items:
                    $ref: '#/components/schemas/DialRule'
                  example:
                    - match: '+971'
                      replace: '0'
                    - match: +
                      replace: '00'
                callerIdRules:
                  type: array
                  maxItems: 10
                  description: >
                    Rewrites the caller ID the gateway is sent, by the same
                    rules as `dialRules`. Omitted keeps the

                    current rules; an empty list clears them. Direct gateways
                    only: a registered gateway presents its

                    registered identity, and two of the gateway's numbers may
                    not end up presented identically (400).

                    The outbound trunk holds the rewritten numbers, so a change
                    updates it on the cell.
                  items:
                    $ref: '#/components/schemas/DialRule'
                  example:
                    - match: '+971'
                      replace: '0'
                connection:
                  type: string
                  enum:
                    - direct
                    - register
                    - registrar
                  description: SIP gateway only; omitted keeps the current mode.
                preset:
                  type: string
                  description: register only; omitted keeps the current preset.
                authId:
                  type: string
                  description: register only, with the one-identity form.
                outboundProxy:
                  type: string
                  description: register only.
                register:
                  allOf:
                    - $ref: '#/components/schemas/RegisterBlock'
                  description: 'register only: overrides to the preset''s values.'
                identities:
                  type: array
                  maxItems: 20
                  items:
                    $ref: '#/components/schemas/RelayIdentityInput'
                  description: >-
                    register only. A password is required for a new identity and
                    optional for one that exists; omitted, the current
                    identities are kept.
                inboundNumber:
                  type: string
                  description: >-
                    register only: must be one of the gateway's numbers once it
                    has any. Omitted keeps the current one; an empty string
                    clears it.
                inboundRules:
                  type: array
                  maxItems: 10
                  description: >-
                    register only. Omitted keeps the current rules; an empty
                    list clears them.
                  items:
                    $ref: '#/components/schemas/InboundRule'
                  example:
                    - match: '0'
                      replace: '+971'
                    - match: '00'
                      replace: +
      responses:
        '200':
          description: Updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Integration'
        '400':
          description: Not a manually connected gateway, or an invalid port or transport.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Another integration already uses this endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    DialRule:
      type: object
      description: >
        How a gateway wants a destination written. A leading `match` on the
        E.164

        number becomes `replace`; the longest match wins; an unmatched number is

        dialed as is. `+971 → 0` dials a UAE number nationally, `+ → 00`
        everything

        else internationally. `replace` is digits, optionally ending in `+`: a
        PBX

        that seizes a line with a digit and then takes E.164 is `+ → 6+`.
        Records

        stay E.164. A gateway's `callerIdRules` apply the same rewrite to our
        caller ID.
      required:
        - match
        - replace
      properties:
        match:
          type: string
          pattern: ^\+[0-9]{0,15}$
          example: '+971'
        replace:
          type: string
          pattern: ^[0-9]{0,10}\+?$
          example: '0'
    RegisterBlock:
      type: object
      description: >
        The registration the cell's relay holds for a `register` gateway,
        resolved

        from the vendor preset with the admin's overrides applied. Stored on the

        trunk, so the relay renders from the trunk alone.
      properties:
        server:
          type: string
          description: The registrar host; the gateway address.
        port:
          type: integer
          minimum: 1
          maximum: 65535
        transport:
          type: string
          enum:
            - udp
            - tcp
            - tls
        outboundProxy:
          type: string
          description: The vendor's regional proxy, where it routes through one.
        srtp:
          type: string
          enum:
            - none
            - optional
            - required
        expiry:
          type: integer
          minimum: 60
          maximum: 300
          description: >-
            Registration expiry in seconds — the shortest the vendor allows,
            since a dead relay keeps receiving inbound until it lapses.
        qualify:
          type: integer
          enum:
            - 0
            - 60
          description: >-
            OPTIONS keepalive interval; 0 for a vendor relay that refuses
            OPTIONS.
        line:
          type: boolean
          description: >-
            Add `;line=` to the Contact so inbound INVITEs match the
            registration.
        pinHost:
          type: boolean
          description: >-
            Resolve the registrar once and pin it, so every INVITE rides the
            connection the REGISTER opened. Defeats SRV failover; off unless the
            vendor needs it.
        tlsVerify:
          type: object
          properties:
            verify:
              type: boolean
            wildcard:
              type: boolean
            fingerprint:
              type: string
              description: >-
                SHA-256 fingerprint of a self-signed certificate to accept
                instead of the CA bundle.
    RelayIdentityInput:
      type: object
      description: >-
        One identity the relay registers as. The password is write-only and goes
        to Secret Manager.
      required:
        - username
      properties:
        username:
          type: string
          example: '1910'
          pattern: ^[A-Za-z0-9._+@-]{1,128}$
          description: Letters, digits and . _ + @ - only.
        password:
          type: string
          format: password
          maxLength: 256
          description: >-
            Required for a new identity; optional when editing one that exists.
            No line breaks or control characters, and no leading or trailing
            space.
        authUsername:
          type: string
          pattern: ^[A-Za-z0-9._+@-]{1,128}$
          description: >-
            The authorization id, where the vendor issues one distinct from the
            user name.
        fromUser:
          type: string
          pattern: ^[A-Za-z0-9._+@-]{0,128}$
          description: >-
            The From user on outbound INVITEs; defaults to `username`. Omit to
            keep the stored value; send an empty string to clear it.
        maxConcurrent:
          type: integer
          minimum: 1
          maximum: 100
          default: 1
          description: Calls this identity can carry at once, as the far side enforces it.
    InboundRule:
      type: object
      description: |
        How the caller a registered system delivers is normalized, applied with
        the same longest-match rule as `DialRule` in the other direction: a
        leading `match` (digits) becomes `replace` (`+` and digits). `0 → +971`
        and `00 → +` turn 0586582538 into +971586582538. A caller already in
        E.164, and a caller no rule matches (a desk-phone extension dialling the
        assistant), pass through unchanged — so a call record's caller is not
        always E.164 on a registered gateway.
      required:
        - match
        - replace
      properties:
        match:
          type: string
          pattern: ^[0-9]{1,15}$
          example: '0'
        replace:
          type: string
          pattern: ^\+?[0-9]{0,15}$
          example: '+971'
    Integration:
      type: object
      properties:
        id:
          type: string
        carrier:
          type: string
        name:
          type: string
        accountRef:
          type: string
          description: Twilio Account SID or carrier SIP host — safe to show.
        accountName:
          type: string
        status:
          type: string
          enum:
            - connected
            - error
        lastError:
          type: string
        trunkId:
          type: string
        carrierTrunkSid:
          type: string
          description: >-
            The trunk id inside the carrier's account (API-provisioned
            carriers).
        terminationUri:
          type: string
        credentialFingerprint:
          type: string
          description: Non-reversible; for display and audit only.
        hasCredentials:
          type: boolean
        secretsBackend:
          type: string
          example: secret-manager
        provisioning:
          type: string
          enum:
            - api
            - manual
        displayName:
          type: string
        transferSupport:
          type: string
          enum:
            - supported
            - verify
            - unsupported
        channel:
          type: string
          enum:
            - voice
            - sms
          description: An `sms` integration has no trunk and no numbers.
        sipUri:
          type: string
        gateway:
          type: object
          description: >-
            How a manually connected gateway is dialed; port and transport are
            null where unset. The register fields are empty on a direct gateway.
          properties:
            port:
              type: integer
              nullable: true
            transport:
              type: string
              nullable: true
              enum:
                - udp
                - tcp
                - tls
                - null
            dialRules:
              type: array
              items:
                $ref: '#/components/schemas/DialRule'
            callerIdRules:
              type: array
              items:
                $ref: '#/components/schemas/DialRule'
              description: >-
                The same rules applied to our caller ID; empty means it is sent
                as E.164.
            connection:
              type: string
              enum:
                - direct
                - register
                - registrar
            preset:
              type: string
              nullable: true
            register:
              allOf:
                - $ref: '#/components/schemas/RegisterBlock'
              description: Null on a direct gateway.
            identities:
              type: array
              items:
                $ref: '#/components/schemas/RelayIdentity'
            inboundNumber:
              type: string
              nullable: true
              description: >-
                The Vodex number an inbound call through the identity is
                presented as.
            inboundRules:
              type: array
              items:
                $ref: '#/components/schemas/InboundRule'
            maxConcurrent:
              type: integer
              nullable: true
              description: >-
                The identities' caps summed — the batch runner's hard limit on
                in-flight calls through this gateway.
        configPublished:
          type: boolean
          description: >-
            Present after a change to a registered gateway; whether the cell's
            relay snapshot reached the config bucket.
        connectedAt:
          type: integer
          format: int64
    Error:
      type: object
      properties:
        error:
          type: string
          example: not found
    RelayIdentity:
      type: object
      description: >-
        An identity as the console reads it, with what the cell's relay last
        reported about its registration.
      properties:
        username:
          type: string
        authUsername:
          type: string
          nullable: true
        fromUser:
          type: string
          nullable: true
        maxConcurrent:
          type: integer
        registration:
          type: object
          properties:
            status:
              type: string
              nullable: true
              description: >-
                Asterisk's word: Registered, Rejected, Unregistered; null before
                the relay has reported.
            nextRefreshS:
              type: integer
              nullable: true
            summary:
              type: string
              example: Registered, next refresh in 4 min
  responses:
    NotFound:
      description: >-
        No such resource — or it belongs to another tenant, which looks the
        same.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    ApiKeyBearer:
      type: http
      scheme: bearer
      description: 'Tenant API key (`vdx_live_…`) as `Authorization: Bearer`.'

````

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