Codex skill
WhatsApp Skill for Codex
AGENTS.md-compatible skill that lets OpenAI Codex add WhatsApp messaging to any project via the Gavi WhatsApp API.
Install
- 1. Save the skill content below as
AGENTS.md (or ~/.codex/AGENTS.md)in your project (or user config) directory. - 2. Install the SDK:
npm install @gaviwhatsapp/whatsapp
- 3. Set the API key environment variable:
GAVIVENTURES_API_KEY=gv_...
Get a key → - 4. Restart Codex. The agent will now know how to use the Gavi WhatsApp API when you ask it to send messages.
GaviVentures WhatsApp API — Codex Instructions
When the user asks to add WhatsApp messaging to their project, use the GaviVentures WhatsApp API.
Canonical documentation (use these URLs, do not guess)
- API reference: https://www.gaviventures.com/docs/api
- Agent skills: https://www.gaviventures.com/docs/skills
- Get an API key: https://www.gaviventures.com/whatsapp/settings/api-keys
- Meta WhatsApp pricing: https://business.whatsapp.com/products/platform-pricing
Install
npm install @gaviwhatsapp/whatsapp
Set environment variable: GAVIVENTURES_API_KEY=gv_...
Get a key at https://www.gaviventures.com/whatsapp/settings/api-keys
Quick Reference
import { WhatsApp } from '@gaviwhatsapp/whatsapp'
const wa = new WhatsApp({ apiKey: process.env.GAVIVENTURES_API_KEY! })
// Text message
await wa.send({ to: '+919876543210', text: 'Hello!' })
// Template message (pre-approved by Meta)
await wa.sendTemplate({
to: '+919876543210',
template: 'order_confirmation',
language: 'en',
variables: { '1': 'ORD-1234' },
})
// Media message
await wa.sendMedia({
to: '+919876543210',
type: 'image',
url: 'https://example.com/photo.jpg',
})
// Broadcast to many
await wa.broadcast({
recipients: ['+919876543210', '+919876543211'],
template: 'promo_offer',
})
// List templates
const { templates } = await wa.getTemplates()
// Register webhook
const wh = await wa.registerWebhook({
url: 'https://myapp.com/api/webhook',
events: ['message.received'],
})
// Save wh.secret as-is (do NOT strip the `whsec_` prefix or base64-decode).
Receiving Webhooks (read carefully — most bugs come from breaking these rules)
Headers Gavi sends:
X-GaviVentures-Signature— HMAC-SHA256 hex of the raw bodyX-GaviVentures-Timestamp— Unix seconds, advisory only, NOT part of the HMAC payload
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 text"
}
fromhas NO+prefix — prepend+when replyingtextis a plain string, NOT{ body: "..." }(that's Meta's shape, Gavi normalizes it)- Other event types (
message.sent,message.delivered,message.read,message.failed) carryto/statusinstead offrom/text
HMAC verification rules:
- Use the secret as-is — full string including any
whsec_prefix; no base64/hex decoding. - Hash the raw bytes of the request body — never
JSON.stringify(req.body). - Payload is the raw body only — do NOT prefix with the timestamp.
Express handler:
import express from 'express'
import { createHmac, timingSafeEqual } from 'crypto'
const app = express()
app.use(express.json({
verify: (req, _res, buf) => {
(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 ok =
typeof received === 'string' &&
received.length === computed.length &&
timingSafeEqual(Buffer.from(received), Buffer.from(computed))
if (!ok) return res.sendStatus(401)
// event is at req.body; use '+' + event.from to reply
return res.sendStatus(200)
})
SDK helper:
import { verifyWebhookSignature } from '@gaviwhatsapp/whatsapp'
const ok = verifyWebhookSignature(rawBody, signature, secret)
REST API
Base: https://www.gaviventures.com
Header: Authorization: Bearer <api_key>
- POST /api/v1/messages/send —
{ to, text } - POST /api/v1/messages/template —
{ to, template, language?, variables? } - POST /api/v1/messages/media —
{ to, type, url, caption?, filename? } - GET /api/v1/messages — query: phone, limit
- GET /api/v1/templates — query: sync=true
- POST /api/v1/broadcasts —
{ recipients, template, language? } - POST /api/v1/webhooks —
{ url, events? }
Rules
- Phone numbers require country code (+919876543210)
- Templates must be approved by Meta first
- API key prefix: gv_
- Meta bills message costs to the user directly. Meta's pricing changes frequently — see current rates at https://business.whatsapp.com/products/platform-pricing
Other skills
Cursor
WhatsApp Skill for Cursor
Drop-in Cursor rule that teaches the AI to send WhatsApp messages, broadcasts, and webhooks via the Gavi WhatsApp API.
Claude Code
WhatsApp Skill for Claude Code
Agent skill that lets Claude Code send WhatsApp messages, manage templates, run broadcasts, and register webhooks via the Gavi WhatsApp API.
Ready to send your first message?
Get an API key and have Codex send a WhatsApp message in under 5 minutes.