WhatsApp Lite API Guide¶
Complete documentation for both edge functions with internal logic and sample curl commands for all use cases.
Table of Contents¶
- whatsapp-lite-send - Sending Messages
- Direct AppGain API – curl for all calling cases
- appgain-whatsapp-webhook - Receiving Messages
- Common Use Cases
whatsapp-lite-send - Sending Messages¶
Internal Logic Flow¶
1. Request Validation
├─ Validates JSON payload
├─ Ensures required fields: `to`, `message`
├─ Validates message length (max 4096 chars)
└─ Ensures either `suitId` or `organizationId` is provided
2. Phone Number Processing
├─ Cleans phone number (removes @s.whatsapp.net)
└─ Logs both original and cleaned versions
3. Organization Lookup
├─ If `suitId` provided → Look up org by `settings->>appgain_suit_id`
└─ If `organizationId` provided → Look up org by ID
4. Get AppGain Credentials
├─ Extract `appgain_api_key` from org settings
└─ Extract `appgain_suit_id` from org settings
5. Send via AppGain API
├─ POST to https://notify.appgain.io/{suitId}/send
├─ Headers: appapikey, Content-Type
└─ Body: UOWHATSAPP payload with cleaned phone number
6. Contact Management
├─ Find existing contact by phone number (via shared utility)
├─ If not found, create with `contactName` or "Unknown Contact"
└─ Store multiple phone number variations for better matching
7. Channel Account Management
├─ Look up ALL active channel accounts for organization
├─ Use the FIRST (most recent) one found
└─ If none exist, create new channel account
8. Conversation Management
├─ If `conversationId` provided → use it directly
├─ If not provided:
│ ├─ Search for existing OPEN conversation for contact + channel
│ ├─ If found → use existing conversation ID
│ └─ If not found → create new conversation
└─ Update `last_message_at` timestamp
9. Message Storage
├─ Insert message with direction: "OUTBOUND"
├─ Set status: "SENT"
├─ Store `external_id` from AppGain response
├─ If `from` provided → add to metadata as `sender_name`
└─ Link to conversation and contact
10. Return Response
├─ success: |||/false
├─ result: AppGain API response
├─ conversationId
├─ contactId
└─ debug info
API Endpoint¶
Required Headers¶
Request Parameters¶
| Field | Type | Required | Description |
|---|---|---|---|
to |
string | ✅ Yes | Recipient phone number (supports 201001383533@s.whatsapp.net format) |
message |
string | ✅ Yes | Message content (max 4096 characters) |
suitId |
string | ⚠️ Either | AppGain suit ID (mutually exclusive with organizationId) |
organizationId |
string | ⚠️ Either | Supabase organization ID (mutually exclusive with suitId) |
conversationId |
string | ❌ No | Use existing conversation ID instead of creating/finding one |
contactName |
string | ❌ No | Contact name when creating new contact |
from |
string | ❌ No | Sender name for UI display (stored in metadata) |
Response¶
Success Response (200 OK):
{
"success": true,
"result": {
"status": "success",
"message": "successfully sent"
},
"conversationId": "uuid-of-conversation",
"contactId": "uuid-of-contact",
"debug": {
"hasChannelAccount": false,
"hasConversationId": true,
"hasContactId": true
}
}
Error Response (400/404/500):
Direct AppGain API – curl for all calling cases¶
These examples call the AppGain notify API directly (https://notify.appgain.io/{suitId}/send). Use your suitId and AppGain API key from channel settings.
Base: POST https://notify.appgain.io/{SUIT_ID}/send
Headers: appapikey: YOUR_APPGAIN_API_KEY, Content-Type: application/json
Body: JSON with top-level key UOWHATSAPP. Phone numbers must be cleaned (no @s.whatsapp.net).
AppGain case 1: Text only¶
curl -X POST "https://notify.appgain.io/YOUR_SUIT_ID/send" \
-H "appapikey: YOUR_APPGAIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"UOWHATSAPP": {
"random_string": 0,
"receivers": [{ "mobileNum": "201001383533" }],
"message": "Hello, this is a text-only message"
}
}'
AppGain case 2: Image only (no caption)¶
curl -X POST "https://notify.appgain.io/YOUR_SUIT_ID/send" \
-H "appapikey: YOUR_APPGAIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"UOWHATSAPP": {
"random_string": 0,
"receivers": [{ "mobileNum": "201001383533" }],
"imageUrl": "https://example.com/image.jpg"
}
}'
AppGain case 3: Image with caption¶
curl -X POST "https://notify.appgain.io/YOUR_SUIT_ID/send" \
-H "appapikey: YOUR_APPGAIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"UOWHATSAPP": {
"random_string": 0,
"receivers": [{ "mobileNum": "201001383533" }],
"imageUrl": "https://example.com/image.jpg",
"message": "Check out this image"
}
}'
AppGain case 4: Video¶
curl -X POST "https://notify.appgain.io/YOUR_SUIT_ID/send" \
-H "appapikey: YOUR_APPGAIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"UOWHATSAPP": {
"random_string": 0,
"receivers": [{ "mobileNum": "201001383533" }],
"message": "Optional caption for video",
"videoUrl": "https://example.com/video.mp4",
"imageUrl": ""
}
}'
AppGain case 5: Document¶
curl -X POST "https://notify.appgain.io/YOUR_SUIT_ID/send" \
-H "appapikey: YOUR_APPGAIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"UOWHATSAPP": {
"random_string": 0,
"receivers": [{ "mobileNum": "201001383533" }],
"message": "Optional caption for document",
"documentUrl": "https://example.com/document.pdf",
"imageUrl": ""
}
}'
AppGain case 6: Multiple receivers (broadcast)¶
curl -X POST "https://notify.appgain.io/YOUR_SUIT_ID/send" \
-H "appapikey: YOUR_APPGAIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"UOWHATSAPP": {
"random_string": 0,
"receivers": [
{ "mobileNum": "201001383533" },
{ "mobileNum": "2015551234567" }
],
"message": "Broadcast message to multiple numbers"
}
}'
Note: The whatsapp-lite-send edge function only uses AppGain cases 1–3 (text, image, image+caption). Video and document (cases 4–5) are used by the campaign scheduler; multiple receivers (case 6) are used by campaigns, not by the single-message edge function.
Multi-attachment delivery: When one message carries multiple attachments, the edge function sends one AppGain call per attachment. Two things are needed so all of them are delivered (not just the first):
- Unique
random_stringon calls 2..N only. AppGain treatsrandom_stringas a per-send de-duplication nonce, so reusing a constant value for a burst to the same receiver makes AppGain drop calls 2..N as duplicate retries. However, AppGain also reflects a non-zerorandom_stringin the delivered message content — sending every call with a unique nonce altered normal chat texts in production (recipients received the typed text with the nonce appended). The edge function therefore sendsrandom_string: 0on the first/only call (the one carrying the typed text or caption — matching the curl examples above) and a unique value only on the caption-less attachment calls 2..N. - Randomized pacing. A randomized ~0.8–2.5 s delay is inserted between consecutive sends so the WhatsApp session doesn't collapse a rapid burst.
Curl Examples (Supabase edge function)¶
Example 1: Basic Message Using suitId¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533",
"message": "Hello, this is a test message",
"suitId": "YOUR_SUIT_ID"
}'
Example 2: Message with Phone Number Including @s.whatsapp.net¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533@s.whatsapp.net",
"message": "Message with @s.whatsapp.net format",
"suitId": "YOUR_SUIT_ID"
}'
Example 3: Message with Custom Sender Name (from field)¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533",
"message": "Hello from AIBot!",
"suitId": "YOUR_SUIT_ID",
"from": "AIBot"
}'
Example 4: Message with Contact Name¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533",
"message": "Hello Mohamed!",
"suitId": "YOUR_SUIT_ID",
"contactName": "Mohamed Shaheen"
}'
Example 5: Complete Example with All Optional Fields¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533@s.whatsapp.net",
"message": "Hello Mohamed from AIBot!",
"suitId": "YOUR_SUIT_ID",
"contactName": "Mohamed Shaheen",
"from": "AIBot"
}'
Example 6: Using organizationId Instead of suitId¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533",
"message": "Message using organizationId",
"organizationId": "your-org-uuid-here"
}'
Example 7: Send to Existing Conversation¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533",
"message": "Follow-up message",
"suitId": "YOUR_SUIT_ID",
"conversationId": "existing-conversation-uuid"
}'
Example 8: Send Media (image URL with optional caption)¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533",
"message": "Check out this image",
"suitId": "YOUR_SUIT_ID",
"media_urls": ["https://example.com/image.jpg"]
}'
Example 9: Cross-Channel Response (e.g. reply via WhatsApp Lite when original was WhatsApp Cloud)¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533",
"message": "Reply from Lite channel",
"suitId": "YOUR_SUIT_ID",
"conversationId": "existing-conversation-uuid",
"crossChannelResponse": {
"originalChannelId": "original-channel-account-uuid",
"originalChannelType": "WHATSAPP_CLOUD",
"responseChannelId": "whatsapp-lite-channel-account-uuid"
}
}'
appgain-whatsapp-webhook - Receiving Messages¶
Complete webhook documentation is in the main WHATSAPP_LITE_API_GUIDE.md file.
Common Use Cases¶
Use Case 1: Send Welcome Message to New Customer¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533",
"message": "Welcome to our service! How can we help you today?",
"suitId": "YOUR_SUIT_ID",
"contactName": "New Customer",
"from": "Support Team"
}'
Use Case 2: Send Order Confirmation as AIBot¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533",
"message": "Your order #12345 has been confirmed. Expected delivery: 3 days.",
"suitId": "YOUR_SUIT_ID",
"from": "AIBot"
}'
Use Case 3: Follow-up Message in Existing Conversation¶
curl -X POST https://txpaxbxhnvnhsjwwaeoy.supabase.co/functions/v1/whatsapp-lite-send \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "201001383533",
"message": "I wanted to follow up on your inquiry.",
"suitId": "YOUR_SUIT_ID",
"conversationId": "existing-conversation-uuid",
"from": "Agent Sarah"
}'
Error Handling¶
See main documentation for complete error handling guide.
Notes¶
- Phone Number Formats: Both functions automatically handle phone numbers with or without
@s.whatsapp.netsuffix - Duplicate Prevention: Both functions use the same channel account lookup to prevent duplicate conversations
- Contact Matching: Uses multiple phone number variations to find existing contacts
- Sender Display: The
fromfield is stored in metadata and can be displayed in the UI as the message sender - Automatic Creation: Both contacts and conversations are automatically created if they don't exist