# 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}).

`POST https://api.zavu.dev/v2/channels`

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.

## Responses

### 200

Already connected; nothing changed.

- `items` (object[], required)
  - `address` (string, required): 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`).
  - `type` (string, required): A transport a message travels on. One of: `sms`, `sms_oneway`, `whatsapp`, `telegram`, `email`, `instagram`, `messenger`, `voice`.
  - `active` (boolean, required): 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` (string, required): 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`).

### 201

Connected. `items` are the entries of the connected address.

- `items` (object[], required)
  - `address` (string, required): 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`).
  - `type` (string, required): A transport a message travels on. One of: `sms`, `sms_oneway`, `whatsapp`, `telegram`, `email`, `instagram`, `messenger`, `voice`.
  - `active` (boolean, required): 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` (string, required): 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`).

### 400

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.

- `code` (string, required)
- `message` (string, required)
- `details` (object): 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.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

### 401

Unauthorized.

- `code` (string, required)
- `message` (string, required)
- `details` (object): 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.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

### 403

`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.

- `code` (string, required)
- `message` (string, required)
- `details` (object): 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.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

### 409

`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).

- `code` (string, required)
- `message` (string, required)
- `details` (object): 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.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

## Example

```bash
curl -X POST "https://api.zavu.dev/v2/channels" \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "sms_oneway"
}'
```

```typescript
import Zavudev from "@zavudev/sdk";

const zavu = new Zavudev({ apiKey: process.env.ZAVUDEV_API_KEY });

const result = await zavu.v2.channels.connect({
  type: "sms_oneway",
});

console.log(result);
```
