Complete API for sending WhatsApp messages, templates, media, and broadcasts.
https://www.gaviventures.comAuth: Authorization: Bearer <api_key>TypeScript
import { WhatsApp } from '@gaviwhatsapp/whatsapp'
const wa = new WhatsApp({
apiKey: process.env.GAVIVENTURES_API_KEY!
})
await wa.send({
to: '+919876543210',
text: 'Hello from my app!'
})Python
from gaviwhatsapp import WhatsApp import os wa = WhatsApp( api_key=os.environ["GAVIVENTURES_API_KEY"] ) wa.send( to="+919876543210", text="Hello from my app!" )
Add WhatsApp to your AI coding tool. Just ask: "Send a WhatsApp to +91... saying hello"
// Add to .cursor/mcp.json or .claude/mcp.json
{
"mcpServers": {
"gaviwhatsapp": {
"command": "npx",
"args": ["@gaviwhatsapp/mcp", "--api-key", "YOUR_API_KEY"]
}
}
}Prefer a knowledge-only skill (no extra process)? Drop-in agent skills for Cursor, Claude Code & Codex →
/api/v1/messages/sendSend a text message to a WhatsApp number. The recipient must have an active conversation window (messaged you in the last 24 hours) or use send template instead.
Request body
{
"to": "+919876543210",
"text": "Your order has shipped!"
}Response
{
"message_id": "wamid.abc123",
"status": "sent"
}curl
curl -X POST https://www.gaviventures.com/api/v1/messages/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to": "+919876543210", "text": "Hello!"}'/api/v1/messages/templateSend a pre-approved template message. Required for initiating conversations (first message to a user). Use GET /api/v1/templates to list available templates.
Request body
{
"to": "+919876543210",
"template": "order_confirmation",
"language": "en",
"variables": {
"1": "ORD-1234",
"2": "$49.99"
}
}Response
{
"message_id": "wamid.abc123",
"status": "sent"
}curl
curl -X POST https://www.gaviventures.com/api/v1/messages/template \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to": "+919876543210", "template": "order_confirmation", "language": "en"}'/api/v1/messages/mediaSend media (image, video, document, audio) via a public URL.
Request body
{
"to": "+919876543210",
"type": "image",
"url": "https://example.com/receipt.png",
"caption": "Your receipt"
}
// For documents, add filename:
{
"to": "+919876543210",
"type": "document",
"url": "https://example.com/invoice.pdf",
"filename": "invoice.pdf"
}Response
{
"message_id": "wamid.abc123",
"status": "sent"
}curl
curl -X POST https://www.gaviventures.com/api/v1/messages/media \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to": "+919876543210", "type": "image", "url": "https://example.com/photo.jpg"}'/api/v1/messagesGet sent message history. Filter by phone number. Returns delivery status for each message.
Query parameters
phone (optional) — Filter by recipient phone number limit (optional) — Max messages to return, 1-100, default 20
Response
{
"messages": [
{
"id": "wamid.abc123",
"to": "+919876543210",
"template": "order_confirmation",
"status": "delivered",
"error": null,
"sent_at": "2026-04-10T12:00:00Z",
"delivered_at": "2026-04-10T12:00:05Z",
"read_at": "2026-04-10T12:01:00Z"
}
]
}curl
curl https://www.gaviventures.com/api/v1/messages?phone=+919876543210&limit=10 \ -H "Authorization: Bearer YOUR_API_KEY"
/api/v1/templatesList available message templates. Add ?sync=true to refresh from Meta (slower but up-to-date).
Query parameters
sync (optional) — "true" to pull latest from Meta
Response
{
"templates": [
{
"id": "tmpl_abc",
"name": "order_confirmation",
"status": "APPROVED",
"category": "UTILITY",
"language": "en",
"body_text": "Hi {{1}}, your order {{2}} is confirmed.",
"variables": ["customer_name", "order_id"]
}
]
}curl
curl https://www.gaviventures.com/api/v1/templates \ -H "Authorization: Bearer YOUR_API_KEY"
/api/v1/broadcastsSend a template message to multiple recipients at once. The template must be approved. Use 'variables' for the same values to all recipients, or 'variables_per_recipient' to personalize each message.
Request body
// Same variables for all recipients:
{
"recipients": ["+919876543210", "+919876543211"],
"template": "promo_offer",
"language": "en",
"variables": { "1": "Alice", "2": "20%" }
}
// Different variables per recipient:
{
"recipients": ["+919876543210", "+919876543211"],
"template": "promo_offer",
"language": "en",
"variables_per_recipient": [
{ "1": "Alice", "2": "20%" },
{ "1": "Bob", "2": "15%" }
]
}Response
{
"broadcast_id": "bc_abc123",
"total": 2,
"sent": 2,
"failed": 0,
"results": [
{ "to": "+919876543210", "status": "sent", "message_id": "wamid.x" },
{ "to": "+919876543211", "status": "sent", "message_id": "wamid.y" }
]
}curl
curl -X POST https://www.gaviventures.com/api/v1/broadcasts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"recipients": ["+919876543210"], "template": "promo_offer", "language": "en"}'/api/v1/webhooksRegister a webhook URL to receive real-time events. Returns a secret for HMAC-SHA256 signature verification. Events are delivered with X-GaviVentures-Signature and X-GaviVentures-Timestamp headers. Valid events: message.received, message.sent, message.delivered, message.read, message.failed.
Request body
{
"url": "https://myapp.com/api/whatsapp-webhook",
"events": ["message.received", "message.sent", "message.delivered", "message.read", "message.failed"]
}Response
{
"id": "wh_abc123",
"url": "https://myapp.com/api/whatsapp-webhook",
"secret": "whsec_...",
"events": ["message.received", "message.sent", "message.delivered", "message.read", "message.failed"],
"is_active": true
}curl
curl -X POST https://www.gaviventures.com/api/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://myapp.com/api/webhook", "events": ["message.received"]}'/api/v1/webhooksList all registered webhook endpoints for your account.
Response
{
"webhooks": [
{
"id": "wh_abc123",
"url": "https://myapp.com/api/whatsapp-webhook",
"events": ["message.received", "message.delivered"],
"is_active": true,
"created_at": "2026-04-10T12:00:00Z"
}
]
}curl
curl https://www.gaviventures.com/api/v1/webhooks \ -H "Authorization: Bearer YOUR_API_KEY"
When you register a webhook, Gavi POSTs events to your URL with the exact shape and headers below. Following these rules is required — most webhook integration bugs come from breaking one of them.
X-GaviVentures-Signature — HMAC-SHA256 hex digest of the raw bodyX-GaviVentures-Timestamp — Unix timestamp (seconds). Advisory only — not part of the HMAC payload.data){
"event": "message.received",
"timestamp": "2026-04-16T22:51:33.273Z",
"message_id": "wamid.HBgM...",
"from": "91987654321",
"type": "text",
"text": "your plain message body here"
}from is the phone number without a + (e.g. 91987654321). Prepend + when you reply.text is a plain string — NOT { body: "..." }. (That nested shape is the Meta Cloud API format; Gavi normalizes it.)event values: message.received, message.sent, message.delivered, message.read, message.failed.message.received events, to, status, message_id are present; from and text may be absent.whsec_abc123..., pass the whole string as the HMAC key. Do not strip the whsec_ prefix. Do not base64-decode. Do not hex-decode. Pass it as a UTF-8 string, byte-for-byte.verify callback in express.json(). In Next.js, call req.text() before any parsing.`${timestamp}.${body}`; Gavi does NOT.)// app/api/whatsapp-webhook/route.ts
import { createHmac, timingSafeEqual } from 'crypto'
import { NextRequest, NextResponse } from 'next/server'
export async function POST(req: NextRequest) {
const rawBody = await req.text() // raw bytes BEFORE parsing
const received = req.headers.get('x-gaviventures-signature') ?? ''
const computed = createHmac('sha256', process.env.WEBHOOK_SECRET!)
.update(rawBody)
.digest('hex')
const sigOk =
received.length === computed.length &&
timingSafeEqual(Buffer.from(received), Buffer.from(computed))
if (!sigOk) return new NextResponse('invalid signature', { status: 401 })
const event = JSON.parse(rawBody)
if (event.event === 'message.received') {
const from = '+' + event.from // prepend + for replies
const text = event.text // plain string
console.log(`Received from ${from}: ${text}`)
}
return NextResponse.json({ ok: true })
}import express from 'express'
import { createHmac, timingSafeEqual } from 'crypto'
const app = express()
app.use(express.json({
verify: (req, _res, buf) => {
// Capture the raw body BEFORE JSON parsing — required for HMAC.
;(req as any).rawBody = buf.toString('utf8')
},
}))
app.post('/webhook', (req, res) => {
const received = req.headers['x-gaviventures-signature'] as string
const computed = createHmac('sha256', process.env.WEBHOOK_SECRET!)
.update((req as any).rawBody)
.digest('hex')
const sigOk =
typeof received === 'string' &&
received.length === computed.length &&
timingSafeEqual(Buffer.from(received), Buffer.from(computed))
if (!sigOk) return res.sendStatus(401)
const event = req.body
if (event.event === 'message.received') {
console.log('+' + event.from, event.text)
}
return res.sendStatus(200)
})import { verifyWebhookSignature } from '@gaviwhatsapp/whatsapp'
const ok = verifyWebhookSignature(rawBody, signature, secret)All errors return JSON with an error field.
| Status | Meaning |
|---|---|
| 400 | Bad request — missing or invalid parameters |
| 401 | Invalid or missing API key |
| 500 | Server error — check the error message for details |
{
"error": "Missing required fields: to, text"
}TypeScript SDK
npm install @gaviwhatsapp/whatsapp
Python SDK
pip install gaviwhatsapp
MCP Server
npx @gaviwhatsapp/mcp --api-key KEY