Chatzuri
Pricing
Guides
Guides
Introduction
  1. 1Getting Started
  2. 2Your Agent
  3. 3Knowledge & Sources
  4. 4Agent actions & Tools
  5. 5Agent Tasks
  6. 6WorkflowsBeta
  7. 7Channels
  8. 8Customers & Conversations
  9. 9Run Your Team
  10. 10Developer Tools
    • API keys
    • Webhooks
    • Send WhatsApp messages via the API
    • API overview
Developer reference →Showcase →
Guides10. Developer ToolsSend WhatsApp messages via the API
Chapter 10 · Developer Tools

Send WhatsApp messages via the API

Connect a number, send text and media, and receive replies through your webhook.

6 min read

This guide walks you through connecting a WhatsApp number and sending and receiving messages with the Chatzuri WhatsApp API. You need two things, both from your dashboard: your team API key and an agent ID. Every request is authenticated with the API key and lives under /api/v1/agents/{agentId}/whatsapp.

Before you start
Create an agent in your dashboard and copy its ID, and generate a team API key under Settings → API Keys. Each agent gets its own WhatsApp number.
1

Connect your number

Start a session, then poll the status endpoint. While pairing, the response includes a qr (a PNG data URL) — render it and scan it from WhatsApp on your phone (Linked devices → Link a device).

# Start
curl -X POST https://chatzuri.com/api/v1/agents/AGENT_ID/whatsapp/session \
  -H "Authorization: Bearer $CHATZURI_API_KEY"

# Poll until connected (render .qr while connected is false)
curl https://chatzuri.com/api/v1/agents/AGENT_ID/whatsapp/session \
  -H "Authorization: Bearer $CHATZURI_API_KEY"
# → { "connected": true, "linked": true, "state": "connected",
#     "needsScan": false, "phone": "2547...", "qr": null }

Later on, the same endpoint tells you whether anything is actually wrong. linked means the number is still paired with WhatsApp; live means we hold a connection right now. A deploy or a brief network problem drops the second and leaves the first alone, and you get state: "reconnecting" with needsScan: false — nothing to do, it comes back on its own. Branch on needsScan, not on "connected is false", or you will send people chasing QR codes they don't need.

2

Send your first message

Once connected, send a text message. to is an E.164 number or a full JID.

curl -X POST https://chatzuri.com/api/v1/agents/AGENT_ID/whatsapp/messages \
  -H "Authorization: Bearer $CHATZURI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "to": "254700000000", "text": "Your code is 123456" }'
# → { "status": "sent", "messageId": "BAE5F...", "to": "254700000000", ... }
3

Send media

Media is sent by public URL — no upload step. Fill in the field that matches what you want to send; text becomes the caption.

# Image with a caption
-d '{ "to": "254700000000", "imageUrl": "https://example.com/receipt.png", "text": "Your receipt" }'

# A PDF document
-d '{ "to": "254700000000", "documentUrl": "https://example.com/invoice.pdf", "documentFilename": "invoice.pdf" }'
4

Receive messages

Point a webhook at your app. Setting a URL switches the number into pass-through: incoming messages are delivered to you, and the response gives you a signing secret.

curl -X PUT https://chatzuri.com/api/v1/agents/AGENT_ID/whatsapp/webhook \
  -H "Authorization: Bearer $CHATZURI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://your-app.com/whatsapp/inbound" }'
# → { "mode": "passthrough", "url": "...", "secret": "whsec_...", ... }

Each incoming message is POSTed to your URL:

{
  "event": "message.received",
  "from": "254700000000",
  "message": { "id": "ABC", "type": "text", "text": "hi", "fromMe": false },
  "timestamp": "2026-07-19T10:30:00.000Z"
}

Verify it's genuinely from Chatzuri with the secret:

import crypto from "crypto";

// Use the RAW body. JSON.stringify(req.body) will NOT reproduce the signed
// bytes — key order and whitespace are part of them.
function verify(rawBody, header, secret) {
  const expected =
    "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(header ?? "");           // x-chatzuri-signature
  // timingSafeEqual throws on a length mismatch — check lengths first.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

We also send x-chatzuri-signature-v1 in the form t=<unix>,v1=<hex>, where the hash covers "<t>.<rawBody>". Prefer it: the timestamp is part of the hash, so reject anything outside a few minutes and a captured delivery can't be replayed at you forever.

5

Download inbound media

When someone sends a photo or file, the webhook includes a media.url. Fetch it with your API key to download the decrypted file.

curl -L -H "Authorization: Bearer $CHATZURI_API_KEY" \
  "https://chatzuri.com/api/v1/agents/AGENT_ID/whatsapp/media/ABC" \
  -o received.jpg
6

Check a number before sending

Confirm a number is on WhatsApp so you never waste a message.

curl -H "Authorization: Bearer $CHATZURI_API_KEY" \
  "https://chatzuri.com/api/v1/agents/AGENT_ID/whatsapp/contacts/254700000000"
# → { "phone": "254700000000", "exists": true, "jid": "254700000000@s.whatsapp.net" }

Tips for production

  • Always use E.164 numbers (for example 254700000000).
  • Keep your webhook secret private and verify every delivery.
  • Respond 2xx to webhooks quickly; do heavy work asynchronously.
  • Download inbound media promptly — links are short-lived.
  • Give each agent its own number to scale across teams and use cases.
  • Treat 503 CHANNEL_RECONNECTING as retryable and 409 CHANNEL_NOT_CONFIGURED as an alert. The first means the number is still linked and the connection is coming back; the second means someone has to scan a QR, and retrying will never fix it.
  • Turn on groups, read receipts or delivery receipts via PATCH /whatsapp/settings — all three ship off.
Tip
Sends are limited to 60 per minute per team and count against your plan's monthly quota. Every response carries X-RateLimit-Limit, so you can pace yourself rather than discover the ceiling as a 429.
Heads up
There is no bulk-send path here, by design. This is a real WhatsApp account, and blasting from one number is the fastest way to get it banned. Use it for OTPs, notifications and conversations; use SMS or a campaign for volume.
Previous · Developer ToolsWebhooksNext · Developer ToolsAPI overview
Chatzuri

AI-powered agents are transforming customer interactions by providing instant, intelligent responses around the clock. They help businesses reduce operational costs, improve response times, and scale support without compromising quality. These agents understand natural language, learn from conversations, and integrate with existing systems to offer personalized experiences that enhance customer satisfaction and loyalty.

Chatzuri

AI-powered agents are transforming customer interactions by providing instant, intelligent responses around the clock. They help businesses reduce operational costs, improve response times, and scale support without compromising quality. These agents understand natural language, learn from conversations, and integrate with existing systems to offer personalized experiences that enhance customer satisfaction and loyalty.

Product

  • Pricing
  • Security
  • Affiliates

Resources

  • API
  • Guides
  • Blog
  • Help

Company

  • About us
  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • DPA

About

  • Teams
  • Singapore, Nairobi

© 2026 Chatzuri. All rights reserved.

Chatzuri uses AI and can make mistakes.

Terms of ServicePrivacy PolicyCookie PolicyChatzuri