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

# API v2

> Call Zavu API v2 from the TypeScript SDK as client.v2: send from an address, connect channels, and read flat errors, with the same client and API key as v1.

`@zavudev/sdk` 0.59.0 and later calls [API v2](/api-v2/overview) as `client.v2`,
on the same client, API key, retries and pagination as v1. v1 stays where it
was: `client.messages` is v1, `client.v2.messages` is v2, and the two can be
mixed in one codebase.

```bash theme={null}
npm add @zavudev/sdk@^0.59.0
```

```typescript theme={null}
import Zavudev from '@zavudev/sdk';

const zavu = new Zavudev(); // reads ZAVUDEV_API_KEY

const { message, routing } = await zavu.v2.messages.send({
  from: '+13125551212',
  to: '+56912345678',
  text: 'Your code is 123456.',
});
```

## Ids are positional

Every id in a v2 path is an argument, in the order the path has it. Body and
query fields go in the object after them.

```typescript theme={null}
await zavu.v2.channels.activate('+13125551212', 'whatsapp');
await zavu.v2.channels.disconnect('@acme_bot', 'telegram');
await zavu.v2.agents.channels.disconnect('agent_abc123', '+13125551212', 'sms');
await zavu.v2.templates.submit('tmpl_abc123', { from: '+13125551212' });
```

Pass an address as you read it from `GET /v2/channels`. The SDK puts it in the
path for you; you do not encode the `+` yourself.

## Types

v2's `Message`, `Contact` and `Template` are not v1's, so v2's types have their
own entry point:

```typescript theme={null}
import type { MessageSendParams, ChannelEntry } from '@zavudev/sdk/v2';
```

The root of `@zavudev/sdk` keeps exporting v1's types, as before.

## Lists

A list call pages for you. Iterate it to follow `nextCursor` to the end, or
read one page:

```typescript theme={null}
for await (const entry of zavu.v2.channels.list()) {
  console.log(entry.address, entry.type, entry.active);
}

const page = await zavu.v2.conversations.list({ limit: 20 });
```

## Errors

A failed call throws the same classes as v1, from the root of the package. A v2
error body is `{code, message, details}` at the root, and the SDK keeps it on
`error.error`:

```typescript theme={null}
try {
  await zavu.v2.messages.send({ to: '+56912345678', text: 'Hi' });
} catch (err) {
  if (!(err instanceof Zavudev.ConflictError)) throw err;
  const body = err.error as { code?: string; details?: { connect?: unknown[] } } | undefined;
  if (body?.code !== 'no_channel') throw err;
  console.log(body.details?.connect); // the channels you could connect to send this
}
```

A `409` is a `ConflictError` and is not retried: v2 answers it with
`x-should-retry: false`, and the SDK reads that header. See
[Errors](/api-v2/errors).

## Methods

| Method | Endpoint |
| - | - |
| `client.v2.messages.send(body)` | `POST /v2/messages` |
| `client.v2.messages.list(query)` | `GET /v2/messages` |
| `client.v2.messages.retrieve(messageID)` | `GET /v2/messages/{messageId}` |
| `client.v2.conversations.list(query)` | `GET /v2/conversations` |
| `client.v2.conversations.retrieve(conversationID)` | `GET /v2/conversations/{conversationId}` |
| `client.v2.conversations.listMessages(conversationID, query)` | `GET /v2/conversations/{conversationId}/messages` |
| `client.v2.conversations.markAsRead(conversationID)` | `POST /v2/conversations/{conversationId}/read` |
| `client.v2.contacts.list(query)` | `GET /v2/contacts` |
| `client.v2.contacts.create(body)` | `POST /v2/contacts` |
| `client.v2.contacts.retrieve(contactID)` | `GET /v2/contacts/{contactId}` |
| `client.v2.contacts.update(contactID, body)` | `PATCH /v2/contacts/{contactId}` |
| `client.v2.contacts.delete(contactID)` | `DELETE /v2/contacts/{contactId}` |
| `client.v2.channels.list(query)` | `GET /v2/channels` |
| `client.v2.channels.connect(body)` | `POST /v2/channels` |
| `client.v2.channels.retrieve(address)` | `GET /v2/channels/{address}` |
| `client.v2.channels.activate(address, type)` | `POST /v2/channels/{address}/{type}/activate` |
| `client.v2.channels.deactivate(address, type)` | `POST /v2/channels/{address}/{type}/deactivate` |
| `client.v2.channels.disconnect(address, type)` | `DELETE /v2/channels/{address}/{type}` |
| `client.v2.templates.list(query)` | `GET /v2/templates` |
| `client.v2.templates.create(body)` | `POST /v2/templates` |
| `client.v2.templates.retrieve(templateID)` | `GET /v2/templates/{templateId}` |
| `client.v2.templates.delete(templateID)` | `DELETE /v2/templates/{templateId}` |
| `client.v2.templates.submit(templateID, body)` | `POST /v2/templates/{templateId}/submit` |
| `client.v2.campaigns.list(query)` | `GET /v2/campaigns` |
| `client.v2.campaigns.create(body)` | `POST /v2/campaigns` |
| `client.v2.campaigns.retrieve(campaignID)` | `GET /v2/campaigns/{campaignId}` |
| `client.v2.campaigns.delete(campaignID)` | `DELETE /v2/campaigns/{campaignId}` |
| `client.v2.campaigns.recipients.add(campaignID, body)` | `POST /v2/campaigns/{campaignId}/recipients` |
| `client.v2.campaigns.send(campaignID, body)` | `POST /v2/campaigns/{campaignId}/send` |
| `client.v2.campaigns.progress(campaignID)` | `GET /v2/campaigns/{campaignId}/progress` |
| `client.v2.campaigns.cancel(campaignID)` | `POST /v2/campaigns/{campaignId}/cancel` |
| `client.v2.webhook.retrieve()` | `GET /v2/webhook` |
| `client.v2.webhook.set(body)` | `PUT /v2/webhook` |
| `client.v2.webhook.delete()` | `DELETE /v2/webhook` |
| `client.v2.webhook.rotateSecret()` | `POST /v2/webhook/secret` |
| `client.v2.agents.list(query)` | `GET /v2/agents` |
| `client.v2.agents.create(body)` | `POST /v2/agents` |
| `client.v2.agents.retrieve(agentID)` | `GET /v2/agents/{agentId}` |
| `client.v2.agents.update(agentID, body)` | `PATCH /v2/agents/{agentId}` |
| `client.v2.agents.delete(agentID)` | `DELETE /v2/agents/{agentId}` |
| `client.v2.agents.test(agentID, body)` | `POST /v2/agents/{agentId}/test` |
| `client.v2.agents.channels.connect(agentID, body)` | `POST /v2/agents/{agentId}/channels` |
| `client.v2.agents.channels.disconnect(agentID, address, type)` | `DELETE /v2/agents/{agentId}/channels/{address}/{type}` |
| `client.v2.numbers.searchAvailable(query)` | `GET /v2/numbers/available` |
| `client.v2.numbers.purchase(body)` | `POST /v2/numbers` |
| `client.v2.emailDomains.list()` | `GET /v2/email-domains` |
| `client.v2.emailDomains.create(body)` | `POST /v2/email-domains` |
| `client.v2.emailDomains.retrieve(emailDomainID)` | `GET /v2/email-domains/{emailDomainId}` |
| `client.v2.emailDomains.delete(emailDomainID)` | `DELETE /v2/email-domains/{emailDomainId}` |
| `client.v2.emailDomains.verify(emailDomainID)` | `POST /v2/email-domains/{emailDomainId}/verify` |
| `client.v2.functions.list(query)` | `GET /v2/functions` |
| `client.v2.functions.retrieve(functionID)` | `GET /v2/functions/{functionId}` |
| `client.v2.functions.update(functionID, body)` | `PATCH /v2/functions/{functionId}` |

What v2 does not cover (voice calls, 10DLC, memory, sub-accounts and the rest
listed in [Migrate from v1](/api-v2/migrate-from-v1)) stays on the v1 methods of
the same client.


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