v1.0 Developer Reference

Voji Public REST API & Webhooks Documentation

Integrate your backend CRM, billing system, or e-commerce store with Voji's high-throughput WhatsApp Cloud API infrastructure.

1. Authentication

All Public REST API requests must include your workspace API key generated in Settings > API Keys. Pass the key in the X-API-Key request header.

curl -X GET "http://34.131.194.230:3000/public/v1/messages/msg_123/status" \
  -H "X-API-Key: waba_live_YOUR_API_KEY"
API keys are strictly scoped by permissions (messages:write, contacts:write, templates:read) and enforce Redis sliding-window rate limiting.

2. Send Message

Endpoint: POST /public/v1/messages

Supports TEXT, TEMPLATE, and MEDIA payloads. Note that customer window policies apply: non-template messages can only be sent within 24 hours of the customer’s last inbound message.

Example: Send Text Message
curl -X POST "http://34.131.194.230:3000/public/v1/messages" \
  -H "X-API-Key: waba_live_YOUR_API_KEY" \
  -H "Idempotency-Key: 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "919876543210",
    "type": "TEXT",
    "text": "Your order #1042 has shipped!"
  }'
Example: Send Approved WhatsApp Template
curl -X POST "http://34.131.194.230:3000/public/v1/messages" \
  -H "X-API-Key: waba_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "919876543210",
    "type": "TEMPLATE",
    "template": {
      "name": "shipping_update",
      "language": "en_US",
      "components": [
        {
          "type": "body",
          "parameters": [
            { "type": "text", "text": "Alice" },
            { "type": "text", "text": "TRK-998822" }
          ]
        }
      ]
    }
  }'

3. Idempotency Guarantees

To prevent duplicate message dispatches on network retries, supply a unique UUID in the Idempotency-Key header.

  • Concurrent requests with the same key are protected by a 15-second Redis concurrency lock.
  • Requests with the same key and identical payload return the previously dispatched message record.
  • Requests with the same key but a modified payload return an HTTP 422 Unprocessable Entity error.

4. Outbound Webhooks & HMAC Verification

Configure your webhook URL and signing secret in Settings > Outbound Webhooks to receive realtime events (message.received, message.status_updated).

Each webhook delivery includes an X-Signature header containing the HMAC-SHA256 hex digest of the raw request body computed with your webhook secret.

Node.js (Express / Fastify) Signature Verification
const crypto = require('crypto');

function verifyWebhookSignature(rawBody, signatureHeader, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader),
    Buffer.from(expectedSignature)
  );
}
Python (FastAPI / Flask) Signature Verification
import hmac
import hashlib

def verify_webhook_signature(raw_body_bytes, signature_header, secret):
    expected_sig = hmac.new(
        secret.encode('utf-8'),
        raw_body_bytes,
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature_header, expected_sig)