Skip to main content

What v2 is

v2 is a second version of the Zavu REST API. It runs on the same engine as v1: the same API keys, the same sending limits, prices and reviews. v1 keeps working unchanged, and you can call both from the same code. What v2 changes is the shape:
  • No sender object. You send from an address, the way the recipient sees it: a phone number, an email address, a bot name.
  • No Zavu-Sender header. Pass from, or reply inside a thread with conversationId.
  • Every send says how it was routed. The 202 carries routing: the channel, the address it left from, and why.
  • One webhook for the project. Every event, at one URL.
  • One error shape. {code, message, details} at the root of every error.
Coming from v1? Read Migrate from v1.

Base URL and authentication

Authenticate with the same API key you use on v1, as a Bearer token:
The rate limit is per project and shared: v1 and v2 requests count against the same window. Sending limits apply to a message the same way whichever version sent it. A test-mode key sends from the WhatsApp sandbox on v2, as it does on v1. from and conversationId are not consulted.
The SDKs (@zavudev/sdk on npm, zavudev on PyPI, and the others) call v1 today. Call v2 over HTTP: curl, fetch, or any HTTP client.

Addresses

An address is where a message leaves from, written the way the recipient sees it. GET /v2/channels lists one entry per (address, type). A number that carries WhatsApp and SMS is two entries with the same address. active: false means the connection exists but is switched off.
More in Addresses and channels.

Send your first message

1

Connect a channel

POST /v2/channels connects what needs no purchase and no Meta signup. Pick one:
The answer lists the entries of the address you connected. Two-way SMS and voice come with a number of your own (POST /v2/numbers). WhatsApp, Instagram and Messenger connect through Meta, from Accounts in the dashboard.
2

Send

In a new project the first channel lands on the default, so from is not needed.
202 Accepted
3

Pick the sending address

With more than one address, name it in from:

Email domains

An email address can only be connected at a verified domain of the project:
  1. POST /v2/email-domains with {"domain": "yourdomain.com"}. The answer has the DNS records to publish.
  2. Publish them exactly as returned.
  3. POST /v2/email-domains/{emailDomainId}/verify.
  4. POST /v2/channels with {"type": "email", "address": "noreply@yourdomain.com"}.
Connecting an address at a domain that is missing or not verified answers 409 email_domain_missing or 409 email_domain_not_verified, with the call that fixes it in details.recommendation.

How a send is routed

The message leaves from conversationId if given, else from, else the project default. The transport is the first rule that applies, reported in routing.reason: The order does not compare price or check the WhatsApp 24-hour window. A connection that is switched off is skipped. A WhatsApp-to-SMS fallback happens after the 202, so it never appears in routing; read attemptedChannels on the message.

When nothing can send: 409 no_channel

Every “nothing can send this” is the same answer: no channel at all, a channel that is not connected, an email to without email, a WhatsApp-only messageType without WhatsApp. details.connect lists what this project can connect to reach to, with the exact request for each.
409 Conflict
request: null means the channel is connected from the dashboard. available: false says why in note, for example one-way SMS before business verification is approved.

Replying in a conversation

Pass conversationId instead of from. The reply leaves from the address the thread was last handled from and goes to whoever the thread is with, so to can be omitted.
A to that is not the thread’s contact is refused with 400, rather than starting another conversation.

Webhook

One webhook per project, configured with PUT /v2/webhook, receives every event you subscribe to. Payloads name addresses, never a sender id. See Webhook.

Errors

Every error is flat:
A known path called with the wrong method answers 405 method_not_allowed, with details.allowed and an Allow header. See Errors.

Money

Every amount in v2 is USD, rounded to 6 decimals. routing.estimatedCost is what an SMS took from your balance when it was accepted; it is null where the price is not known at send time (WhatsApp, email, voice). message.charged is what an SMS has taken so far.

Reference

Every operation, with its request and response schemas, is listed under API v2 reference in this tab.