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

# Connect a channel

> Connect a channel without buying anything, so a new project can send its first message from /v2. Returns every entry of the connected address (`GET /v2/channels/{address}`).

**Where it lands.** Channels are grouped: the entries of `GET /v2/channels` that share a `v1SenderId` are one group, and a group holds one connection per transport. The new channel joins the project's default group when that group does not carry the transport yet; otherwise, or when the project has none, it starts a new group (the first group of a project becomes its default). So in a fresh project every channel connected here lands on the default and `POST /v2/messages` needs no `from`; a second email address or a second bot starts its own group and is used by passing its address as `from`.

**Idempotent** for `sms_oneway` and `email`: an address already connected answers `200` with its entries and changes nothing.

**Active or not.** One-way SMS is active at once. An email or Telegram connection the plan includes is switched on by itself; one that would cost money is listed with `active: false` and is switched on with `POST /v2/channels/{address}/{type}/activate` (same price and limits as on /v1).

A Telegram token that Telegram refuses leaves nothing behind.



## OpenAPI

````yaml /api-reference/v2/openapi.json post /v2/channels
openapi: 3.0.3
info:
  title: Zavu API v2
  version: 2.0.0
  description: >-
    The second version of the Zavu API. It runs on the same engine as /v1: the
    same API keys, the same per-project rate limit, the same sending limits,
    prices and reviews. /v1 keeps working unchanged, and you can mix calls to
    both.


    **Addresses, not senders.** /v2 has no sender object. Where a message leaves
    from is named the way the recipient sees it: an E.164 number, an email
    address, `@botname` for Telegram, `@username` for Instagram, the Facebook
    Page id for Messenger, and the literal `sms_oneway` for one-way SMS (sent
    from shared numbers, never a number of your own). `GET /v2/channels` lists
    one entry per (address, type); a number that carries WhatsApp and SMS is two
    entries with the same address.


    **What else changes.** No `Zavu-Sender` header (`from` or `conversationId`
    instead); every send reports how it was routed; one project webhook carries
    every event; every error is `{code, message, details}` at the root.


    **Wrong method.** A path under `/v2/messages`, `/v2/conversations`,
    `/v2/contacts`, `/v2/channels`, `/v2/templates`, `/v2/campaigns`,
    `/v2/agents`, `/v2/webhook` or `/v2/email-domains` called with a method it
    does not take answers `405 method_not_allowed`, with `details.allowed` and
    an `Allow` header.


    **Money.** Every amount is USD, rounded to 6 decimals.
servers:
  - url: https://api.zavu.dev
security:
  - bearerAuth: []
tags:
  - name: Messages
    description: Send, list and read messages.
  - name: Conversations
    description: Inbox threads, one per contact across channels.
  - name: Contacts
    description: Contacts and the identifiers they are reached at.
  - name: Channels
    description: 'Where the project can send from: one entry per (address, type).'
  - name: Templates
    description: WhatsApp message templates.
  - name: Campaigns
    description: One message to many recipients.
  - name: Webhook
    description: The one project webhook for every event.
  - name: Agents
    description: AI agents and the addresses they answer on.
  - name: Numbers
    description: Search and buy phone numbers.
  - name: Email Domains
    description: Domains to send email from.
  - name: Functions
    description: Configure Zavu Functions.
paths:
  /v2/channels:
    post:
      tags:
        - Channels
      summary: Connect a channel
      description: >-
        Connect a channel without buying anything, so a new project can send its
        first message from /v2. Returns every entry of the connected address
        (`GET /v2/channels/{address}`).


        **Where it lands.** Channels are grouped: the entries of `GET
        /v2/channels` that share a `v1SenderId` are one group, and a group holds
        one connection per transport. The new channel joins the project's
        default group when that group does not carry the transport yet;
        otherwise, or when the project has none, it starts a new group (the
        first group of a project becomes its default). So in a fresh project
        every channel connected here lands on the default and `POST
        /v2/messages` needs no `from`; a second email address or a second bot
        starts its own group and is used by passing its address as `from`.


        **Idempotent** for `sms_oneway` and `email`: an address already
        connected answers `200` with its entries and changes nothing.


        **Active or not.** One-way SMS is active at once. An email or Telegram
        connection the plan includes is switched on by itself; one that would
        cost money is listed with `active: false` and is switched on with `POST
        /v2/channels/{address}/{type}/activate` (same price and limits as on
        /v1).


        A Telegram token that Telegram refuses leaves nothing behind.
      operationId: connectChannelV2
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChannelConnectRequest'
            examples:
              sms_oneway:
                summary: One-way SMS
                value:
                  type: sms_oneway
              email:
                summary: Email
                value:
                  type: email
                  address: noreply@yourdomain.com
                  fromName: Acme
              telegram:
                summary: Telegram bot
                value:
                  type: telegram
                  botToken: 123456:ABC-DEF
      responses:
        '200':
          description: Already connected; nothing changed.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/ChannelEntry'
        '201':
          description: Connected. `items` are the entries of the connected address.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/ChannelEntry'
        '400':
          description: >-
            Invalid body (`details.errors`), a `type` that is connected
            elsewhere (the message says where), or a Telegram token Telegram
            refused (`details.errors[0].path` is `["botToken"]`).
            `invalid_bot_token`: Telegram rejected the bot token
            (`details.telegramSaid` has its words); no bot and no sender are
            left behind.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: >-
            `kyb_required`: one-way SMS is closed to this team until its
            business verification is approved. Only on an account required to
            verify its business first; one-way SMS needs no verification by
            default. `details.dashboardUrl` is `/kyb`; `details.recommendation`
            names the channels that are open.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                code: kyb_required
                message: >-
                  One-way SMS requires an approved business verification.
                  Complete it at /kyb, or send on a channel that does not need
                  it.
                details:
                  type: sms_oneway
                  dashboardUrl: /kyb
        '409':
          description: >-
            `email_domain_missing`: the address's domain is not an email domain
            of this project (`details.domain`; add it with `POST
            /v2/email-domains`). `email_domain_not_verified`: it is, but not
            verified yet (`details.emailDomainId`, `details.status`; verify with
            `POST /v2/email-domains/{emailDomainId}/verify`).
            `domain_setup_failed`: the domain has no DKIM records and will never
            verify, so publishing or verifying cannot help
            (`details.setupError`, the same text `GET
            /v2/email-domains/{emailDomainId}` shows).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                code: email_domain_not_verified
                message: >-
                  acme.com is not verified yet (status: pending), so
                  noreply@acme.com cannot send yet.
                details:
                  domain: acme.com
                  emailDomainId: emd_abc123
                  status: pending
                  recommendation: >-
                    Publish the DNS records from GET
                    /v2/email-domains/emd_abc123, then POST
                    /v2/email-domains/emd_abc123/verify and retry.
      security:
        - bearerAuth: []
components:
  schemas:
    ChannelConnectRequest:
      description: >-
        What to connect. Only channels that need no purchase and no third-party
        signup are connected here: one-way SMS, an email address at a verified
        domain, a Telegram bot. Two-way SMS and voice come with a number (`POST
        /v2/numbers`); WhatsApp, Instagram and Messenger connect through Meta,
        from Accounts in the dashboard. Any of those types here is a `400`
        saying so.
      oneOf:
        - type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - sms_oneway
          description: >-
            One-way SMS from shared system numbers. No number or credential;
            recipients cannot reply. Sends within the SMS daily ceiling. Sending
            to a US or Canadian (+1) number needs an approved 10DLC campaign
            first, and is refused with `403 ten_dlc_required` until then.
            Refused with `403 kyb_required` only on an account that must verify
            its business before using one-way SMS.
        - type: object
          required:
            - type
            - address
          properties:
            type:
              type: string
              enum:
                - email
            address:
              type: string
              format: email
              example: noreply@yourdomain.com
            fromName:
              type: string
              maxLength: 100
              description: Display name in the recipient's inbox.
          description: >-
            An address at an email domain of this project that is verified (`GET
            /v2/email-domains`).
        - type: object
          required:
            - type
            - botToken
          properties:
            type:
              type: string
              enum:
                - telegram
            botToken:
              type: string
              description: Bot token from @BotFather.
          description: A Telegram bot. Its address is `@botname`.
      discriminator:
        propertyName: type
    ChannelEntry:
      type: object
      required:
        - address
        - type
        - active
        - v1SenderId
      description: One place the project can send from, on one transport.
      properties:
        address:
          type: string
          description: >-
            The address the recipient sees: an E.164 number (`sms`, `voice`,
            `whatsapp`), an email address (lowercase), `@botname` (`telegram`),
            `@username` (`instagram`), the Facebook Page id (`messenger`), or
            the literal `sms_oneway` (one-way SMS goes out from shared system
            numbers, and messages on it store this same literal as `from`).
          example: '+13125551212'
        type:
          $ref: '#/components/schemas/ChannelType'
        active:
          type: boolean
          description: >-
            It can send right now. A connected transport that is switched off is
            listed with `active: false`; turn it on with `POST
            /v2/channels/{address}/{type}/activate`. `sms`, `sms_oneway` and
            `voice` have no switch and are always active.
        v1SenderId:
          type: string
          description: >-
            Read-only. The /v1 sender behind this entry, usable as the
            `Zavu-Sender` header on /v1. Entries with the same `v1SenderId` are
            one channel group. On /v2 it is accepted as `from` only when two
            entries of different groups share an address (see `409
            ambiguous_from`).
    Error:
      type: object
      required:
        - code
        - message
      description: >-
        Every v2 error has this shape at the root. There is no `{error: {...}}`
        wrapper anywhere in v2. A missing resource is `404 not_found` with the
        message `<Resource> not found`; an address the project does not have is
        `404 not_found`, `None of this project's channels has the address
        <address>.`, with the address in `details` under the name the request
        used (`from` or `address`). A `message` is written for the caller and
        never carries a stack trace or a source path. A body that is not JSON is
        `400 invalid_json`, with `details.errors` (`path: []`, the parse error
        in `message`).
      properties:
        code:
          type: string
          example: invalid_request
        message:
          type: string
        details:
          type: object
          additionalProperties: true
          description: >-
            Machine-readable context. Keys that mean the same thing everywhere:

            - `errors`: present on every `400 invalid_request` for an invalid
            body or query parameter, `[{path, message, ...}]`, `path` naming the
            field (`["to"]`, `["contacts", 2, "recipient"]`, `["limit"]`).

            - `recommendation`: one sentence saying what to do next.

            - `dashboardUrl`: the dashboard screen that fixes it.

            - `connect`: on `409 no_channel`, the channels this project can
            connect to reach the recipient, each `{type, request, note,
            available, dashboardUrl?}`; `request` is the exact call (`POST
            /v2/channels` with its body, or `POST /v2/numbers`), or null for a
            channel connected from the dashboard (WhatsApp, Instagram,
            Messenger). `available: false` says why in `note` (one-way SMS
            closed until business verification is approved).

            - `address` / `from`: the address the error is about, under the name
            the request used for it.
          properties:
            errors:
              type: array
              items:
                type: object
                required:
                  - path
                  - message
                properties:
                  path:
                    type: array
                    items:
                      oneOf:
                        - type: string
                        - type: integer
                  message:
                    type: string
                  code:
                    type: string
                additionalProperties: true
    ChannelType:
      type: string
      description: A transport a message travels on.
      enum:
        - sms
        - sms_oneway
        - whatsapp
        - telegram
        - email
        - instagram
        - messenger
        - voice
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````

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