WhatsApp API Reference

Complete API for sending WhatsApp messages, templates, media, and broadcasts.

Base URL: https://www.gaviventures.comAuth: Authorization: Bearer <api_key>

Quick Start

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!"
)

MCP Server (Cursor / Claude Code / Codex)

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 Endpoints

POST/api/v1/messages/send

Send 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!"}'
POST/api/v1/messages/template

Send 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"}'
POST/api/v1/messages/media

Send 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"}'
GET/api/v1/messages

Get 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"
GET/api/v1/templates

List 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"
POST/api/v1/broadcasts

Send 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"}'
POST/api/v1/webhooks

Register 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"]}'
GET/api/v1/webhooks

List 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"

Webhook Delivery Contract

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.

Headers Gavi sends

Event payload (top-level fields, NOT nested under 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"
}

HMAC verification rules

  1. Use the secret exactly as Gavi gave it to you. If your secret is 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.
  2. Hash the raw bytes of the request body, NOT a re-stringified version of the parsed JSON. In Express, capture the raw body via the verify callback in express.json(). In Next.js, call req.text() before any parsing.
  3. The HMAC payload is the raw body only. Do not prefix it with the timestamp. (Stripe uses `${timestamp}.${body}`; Gavi does NOT.)

Next.js App Router handler

// 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 })
}

Express handler

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

SDK helper

import { verifyWebhookSignature } from '@gaviwhatsapp/whatsapp'

const ok = verifyWebhookSignature(rawBody, signature, secret)

Error Responses

All errors return JSON with an error field.

StatusMeaning
400Bad request — missing or invalid parameters
401Invalid or missing API key
500Server error — check the error message for details
{
  "error": "Missing required fields: to, text"
}

Install

TypeScript SDK

npm install @gaviwhatsapp/whatsapp

Python SDK

pip install gaviwhatsapp

MCP Server

npx @gaviwhatsapp/mcp --api-key KEY