WABA RabbitMQ Messaging Contract

WABA RabbitMQ Messaging Contract

Nexgate ↔ WABA Gateway Service

Exchange: whatsapp.exchange (topic)


Overview

Meta/WhatsApp
      │
      ▼
WABA Gateway Service          ← owns Meta API, webhook endpoint
      │  publishes inbound events
      ▼
whatsapp.exchange
      │
      ├── whatsapp.inbound.#   → q.whatsapp.inbound   → Nexgate consumes
      ├── whatsapp.outbound.#  → q.whatsapp.outbound  → WABA Gateway consumes
      └── whatsapp.outbound.auth → q.whatsapp.auth    → WABA Gateway consumes

Note: Inbound/outbound are from Nexgate's perspective.


Exchange & Queues

Name Type Purpose
whatsapp.exchange topic Single shared exchange
q.whatsapp.inbound durable Messages coming IN to Nexgate
q.whatsapp.outbound durable Messages going OUT from Nexgate (shop chatbot)
q.whatsapp.auth durable OTP delivery for registration and login

Routing Keys

Routing Key Direction Description
whatsapp.inbound.message WABA Gateway → Nexgate Customer sent a message (text, image, any media)
whatsapp.inbound.status WABA Gateway → Nexgate Delivery/read status update for Nexgate's outbound messages
whatsapp.outbound.message Nexgate → WABA Gateway Send any message to customer (template or freetext, determined by type field)
whatsapp.outbound.auth Nexgate → WABA Gateway Send OTP to customer via WhatsApp (nexgate_otp template only)

Status flows one way only — Meta → WABA Gateway → Nexgate. Nexgate never publishes status back.


Inbound Events (WABA Gateway publishes, Nexgate consumes)

Common Inbound Fields

Field Type Required Description
type WabaEventType MESSAGE_RECEIVED or STATUS_WEBHOOK
messageType WabaMessageType TEXT, IMAGE, DOCUMENT, AUDIO, VIDEO
wabaAccountId string WABA business account ID
wabaNumber string Shop's WhatsApp number (E.164)
wabaUserName string Customer's WhatsApp display name
messageId string Meta message ID
from string Customer's phone number (E.164)
text string ⚠️ Message text (null for media messages)
status string ⚠️ Delivery status (only for STATUS_WEBHOOK)
media object ⚠️ Media payload (null for text messages)
timestamp ISO-8601 Event timestamp

Media Object (inbound)

Field Type Description
type WabaMessageType IMAGE, DOCUMENT, AUDIO, VIDEO
url string Media URL from Meta
mimeType string e.g. image/jpeg
filename string Original filename
sha256 string Integrity checksum from Meta
caption string Optional caption from customer

Sample: TEXT message received

Routing key: whatsapp.inbound.message

{
  "type": "MESSAGE_RECEIVED",
  "messageType": "TEXT",
  "wabaAccountId": "waba-001",
  "wabaNumber": "+255700000000",
  "wabaUserName": "Josh",
  "messageId": "msg-123",
  "from": "+255712345678",
  "text": "Nataka kuku na chips",
  "media": null,
  "timestamp": "2026-04-12T11:00:00Z"
}

Sample: IMAGE message received

Routing key: whatsapp.inbound.message

{
  "type": "MESSAGE_RECEIVED",
  "messageType": "IMAGE",
  "wabaAccountId": "waba-001",
  "wabaNumber": "+255700000000",
  "wabaUserName": "Josh",
  "messageId": "msg-124",
  "from": "+255712345678",
  "text": null,
  "media": {
    "type": "IMAGE",
    "url": "https://example.com/image.jpg",
    "mimeType": "image/jpeg",
    "filename": "photo.jpg",
    "sha256": "abc123def456",
    "caption": "Hii ndiyo nataka"
  },
  "timestamp": "2026-04-12T11:01:00Z"
}

Sample: STATUS update

Routing key: whatsapp.inbound.status

{
  "type": "STATUS_WEBHOOK",
  "messageType": "TEXT",
  "wabaAccountId": "waba-001",
  "wabaNumber": "+255700000000",
  "wabaUserName": "Josh",
  "messageId": "msg-123",
  "from": "+255712345678",
  "status": "delivered",
  "timestamp": "2026-04-12T11:00:30Z"
}

Possible status values: sent, delivered, read, failed


Outbound Events (Nexgate publishes, WABA Gateway consumes)

Common Outbound Fields

Field Type Required Description
type WabaEventType SEND_TEMPLATE, SEND_FREETEXT, or SEND_AUTH_OTP
messageType WabaMessageType TEXT, TEMPLATE, IMAGE, DOCUMENT, AUDIO, VIDEO
wabaAccountId string WABA business account ID
wabaNumber string WhatsApp number to send from (E.164)
phoneNumberId string ⚠️ Meta phone number ID — present on SEND_AUTH_OTP events only, use directly without DB lookup
appId string ⚠️ Meta app ID — present on SEND_AUTH_OTP events only
to string Customer's phone number (E.164)
correlationId string ⚠️ Order/booking/session ID for tracing
text string ⚠️ Message text (for SEND_FREETEXT + TEXT)
template object ⚠️ Template payload (for SEND_TEMPLATE and SEND_AUTH_OTP)
media object ⚠️ Media payload (for image/document/audio/video)

For SEND_TEMPLATE and SEND_FREETEXT (shop chatbot), phoneNumberId and appId will be null — look them up from DB via wabaNumber as normal. For SEND_AUTH_OTP, phoneNumberId and appId are always populated — use them directly, no DB lookup needed.

Template Object (outbound)

Field Type Description
templateName WabaTemplateName Enum — see reference table below
languageCode string e.g. en, sw
params map Named params mapped to Meta {{1}} {{2}} by WABA Gateway

Media Object (outbound)

Field Type Description
url string Publicly accessible media URL
mimeType string e.g. image/jpeg
filename string Filename for documents
caption string Optional caption

Sample: SEND_FREETEXT (plain text)

Routing key: whatsapp.outbound.message

{
  "type": "SEND_FREETEXT",
  "messageType": "TEXT",
  "wabaAccountId": "waba-001",
  "wabaNumber": "+255700000000",
  "to": "+255712345678",
  "correlationId": "session-001",
  "text": "Habari! Tunayo kuku na chips. Bei ni 5,000 TZS."
}

Sample: SEND_TEMPLATE — shop_details

Routing key: whatsapp.outbound.message

{
  "type": "SEND_TEMPLATE",
  "messageType": "TEMPLATE",
  "wabaAccountId": "waba-001",
  "wabaNumber": "+255700000000",
  "to": "+255712345678",
  "correlationId": "session-002",
  "template": {
    "templateName": "SHOP_DETAILS",
    "languageCode": "en",
    "params": {
      "1": "Josh",
      "2": "Mode Bites"
    }
  }
}

Template body: Hello {{1}}! Welcome to *{{2}}*, browse our products and find what suits you.


Sample: SEND_TEMPLATE — product_results_found

Routing key: whatsapp.outbound.message

{
  "type": "SEND_TEMPLATE",
  "messageType": "TEMPLATE",
  "wabaAccountId": "waba-001",
  "wabaNumber": "+255700000000",
  "to": "+255712345678",
  "correlationId": "session-003",
  "template": {
    "templateName": "PRODUCT_RESULTS_FOUND",
    "languageCode": "en",
    "params": {
      "1": "Josh",
      "2": "2",
      "3": "kuku",
      "4": "Mode Bites",
      "5": "Kuku Choma"
    }
  }
}

Template body: Hello {{1}}! 🎉 We found {{2}} products matching *"{{3}}"* in {{4}}. Here are a few matches: *{{5}}*. Tap below to see all results!


Sample: SEND_TEMPLATE — products_not_found

Routing key: whatsapp.outbound.message

{
  "type": "SEND_TEMPLATE",
  "messageType": "TEMPLATE",
  "wabaAccountId": "waba-001",
  "wabaNumber": "+255700000000",
  "to": "+255712345678",
  "correlationId": "session-004",
  "template": {
    "templateName": "PRODUCTS_NOT_FOUND",
    "languageCode": "en",
    "params": {
      "1": "Josh",
      "2": "cake",
      "3": "Mode Bites",
      "4": "Kuku Choma"
    }
  }
}

Template body: Hello {{1}}, We couldn't find *"{{2}}"* in *{{3}}* right now. You might be interested in: *{{4}}*


Sample: SEND_TEMPLATE — fallback_text

Routing key: whatsapp.outbound.message

{
  "type": "SEND_TEMPLATE",
  "messageType": "TEMPLATE",
  "wabaAccountId": "waba-001",
  "wabaNumber": "+255700000000",
  "to": "+255712345678",
  "correlationId": "session-005",
  "template": {
    "templateName": "FALLBACK_TEXT",
    "languageCode": "en",
    "params": {
      "1": "Josh",
      "2": "sawa asante"
    }
  }
}

Template body: Hi {{1}}, thanks for your message: "{{2}}". We're not sure how to help with that — feel free to ask about our products or shop.

When is this sent?


Sample: SEND_AUTH_OTP

Routing key: whatsapp.outbound.auth

{
  "type": "SEND_AUTH_OTP",
  "messageType": "TEMPLATE",
  "wabaAccountId": "nexgate-waba-account-id",
  "wabaNumber": "+255700000001",
  "phoneNumberId": "nexgate-phone-number-id",
  "appId": "nexgate-app-id",
  "to": "+255712345678",
  "correlationId": "+255712345678",
  "template": {
    "templateName": "NEXGATE_OTP",
    "languageCode": "en",
    "params": {
      "1": "847291"
    }
  }
}

Template body: 847291 is your verification code. For your security, do not share this code. Expires in 10 minutes.

This event always uses the NextGate platform WABA account, not a shop account. phoneNumberId and appId are always present — use them directly without any DB lookup.


Sample: SEND_FREETEXT (image)

Routing key: whatsapp.outbound.message

{
  "type": "SEND_FREETEXT",
  "messageType": "IMAGE",
  "wabaAccountId": "waba-001",
  "wabaNumber": "+255700000000",
  "to": "+255712345678",
  "correlationId": "session-006",
  "media": {
    "url": "https://cdn.nexgate.co.tz/menu.jpg",
    "mimeType": "image/jpeg",
    "filename": "menu.jpg",
    "caption": "Menu ya leo"
  }
}

Enums Reference

WabaEventType

Value Routing Key Direction
MESSAGE_RECEIVED whatsapp.inbound.message WABA Gateway → Nexgate
STATUS_WEBHOOK whatsapp.inbound.status WABA Gateway → Nexgate
SEND_TEMPLATE whatsapp.outbound.message Nexgate → WABA Gateway
SEND_FREETEXT whatsapp.outbound.message Nexgate → WABA Gateway
SEND_AUTH_OTP whatsapp.outbound.auth Nexgate → WABA Gateway

WabaMessageType

Value Description
TEXT Plain text message
TEMPLATE Meta approved template
IMAGE Image (jpg, png)
DOCUMENT PDF, docx etc
AUDIO Voice/audio message
VIDEO Video message

WabaTemplateName

Value Meta Template Name Variables
SHOP_DETAILS shop_details {{1}} customerName, {{2}} shopName
PRODUCTS_NOT_FOUND products_not_found {{1}} customerName, {{2}} searchQuery, {{3}} shopName, {{4}} suggestedProducts
PRODUCT_RESULTS_FOUND product_results_found {{1}} customerName, {{2}} productCount, {{3}} searchQuery, {{4}} shopName, {{5}} topProducts
FALLBACK_TEXT fallback_text {{1}} customerName, {{2}} originalMessage
NEXGATE_OTP nexgate_otp {{1}} otp code

Notes