WhatsApp Lite API Guide¶
Complete documentation for the WhatsApp Lite edge functions, with internal logic and sample curl commands for all use cases. WhatsApp Lite runs on Appgain (api.appgain.io) with QR-code pairing.
Table of Contents¶
- whatsapp-lite-send - Sending Messages
- Direct AppGain API - curl for all calling cases
- Curl Examples (Supabase edge function)
- 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 `organizationId` is provided
2. Phone Number Processing
├─ Cleans phone number (removes @s.whatsapp.net)
└─ Logs both original and cleaned versions
3. Organization Lookup
├─ Look up org by `organizationId`
4. Get AppGain Credentials
├─ Extract `appgain_api_key` from org settings
5. Send via AppGain API
├─ POST to https://notify.appgain.io/{organizationId}/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: true/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) |
organizationId |
string | ✅ Yes | Your ConnectGain organization UUID |
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/{organizationId}/send). Use your organizationId and AppGain API key from channel settings.
- Base:
POST https://notify.appgain.io/{organizationId}/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-org-uuid-here/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-org-uuid-here/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-org-uuid-here/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-org-uuid-here/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-org-uuid-here/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-org-uuid-here/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 organizationId¶
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",
"organizationId": "your-org-uuid-here"
}'
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",
"organizationId": "your-org-uuid-here"
}'
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!",
"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!",
"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!",
"contactName": "Mohamed Shaheen",
"from": "AIBot"
}'
Example 6: Using organizationId¶
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",
"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",
"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",
"conversationId": "existing-conversation-uuid",
"crossChannelResponse": {
"originalChannelId": "original-channel-account-uuid",
"originalChannelType": "WHATSAPP_CLOUD",
"responseChannelId": "whatsapp-lite-channel-account-uuid"
}
}'
Note: A cross-channel message is persisted in the conversation belonging to the
channel it was actually sent from (the WhatsApp Lite channel's own thread with that
contact — found or created atomically), not in the conversationId passed in the
request. The response's conversationId field returns the thread the message landed
in, and the dashboard uses it to switch the inbox to that conversation after sending.
The passed conversationId is still used to resolve the recipient when to is
omitted, and is recorded in the message's metadata.cross_channel for traceability.
appgain-whatsapp-webhook - Receiving Messages¶
Inbound WhatsApp Lite messages arrive through the appgain-whatsapp-webhook edge function. It acknowledges the provider quickly, then (in the background) looks up or creates the contact and conversation, deduplicates the message, re-hosts media in Supabase Storage, and triggers automations. See the webhook system overview for how inbound webhooks are processed.
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?",
"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.",
"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.",
"conversationId": "existing-conversation-uuid",
"from": "Agent Sarah"
}'
Error Handling¶
Failed requests return the error response format shown in the Response section above, with HTTP status 400 (validation), 404 (organization or credentials not found), or 500 (provider or internal error).
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
See also¶
- Appgain integration
- Channels overview
- Multi-channel messaging
- Webhook system overview
- API reference
- Appgain WhatsApp Business API — the platform powering WhatsApp Lite
- Appgain WhatsApp getting started — provider-side documentation
- WhatsApp Lite on connectgain.cloud — feature overview on the ConnectGain website
ConnectGain — omnichannel inbox, CRM & automation for WhatsApp, Messenger, Instagram, Telegram and more. Open the app · Docs home