πŸ“š Chatyug WhatsApp Business API - Complete Documentation

Version 1.7 | Last Updated: September 28, 2026

πŸ“‹ About This Documentation:
This comprehensive guide explains how to integrate Chatyug's WhatsApp Business API with your CRM, software applications, or any external system. Whether you're a developer, business owner, or technical integrator, this documentation will help you understand and implement the API effectively.

πŸ“‘ Table of Contents

1. Overview & Introduction

What is Chatyug API?

Chatyug API is a RESTful web service that allows you to send WhatsApp messages programmatically from your external software applications. It provides a simple, secure, and reliable way to integrate WhatsApp messaging capabilities into your CRM, ERP, e-commerce platform, or any custom software.

Why Use Chatyug API?

Base URL

https://chatyug.com/api

API Response Format

All API responses follow a consistent JSON format:

{ "success": true, "message": "Operation completed successfully", "data": { ... } }
πŸ’‘ Key Concept: The API uses standard HTTP methods (GET, POST) and returns JSON responses. All requests require authentication via API key in the Authorization header.

2. Authentication & API Keys

How Authentication Works

All API requests require an API key for authentication. The API key is passed in the Authorization header using Bearer token format.

Authorization: Bearer YOUR_API_KEY

Getting Your API Key

  1. Login to Chatyug Portal at https://chatyug.com
  2. Navigate to API Configuration page
  3. Click "Generate New API Key" button
  4. Enter a descriptive name (e.g., "My CRM Integration")
  5. Save the API key securely - you cannot view it again!
⚠️ Important Security Note:
API keys are like passwords - keep them secret! Never commit API keys to version control, share them in emails, or expose them in client-side code. Store them in environment variables or secure configuration files.

API Key Format

API keys follow this format:

ck_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0

API Key Scoping

Each API key is associated with:

πŸ’‘ Understanding API Keys:
Think of an API key as a "key to your house" - it gives access to send messages from your WhatsApp Business Account. Each key can be named and tracked separately, making it easy to manage multiple integrations (e.g., one for CRM, one for e-commerce).

3. API Endpoints Reference

3.1 Send Message Endpoint

Endpoint: POST https://chatyug.com/api/external/send-message

Description: Send a text, template, or session media (image/video/audio/document) WhatsApp message.

Request Headers

HeaderValueRequired
AuthorizationBearer YOUR_API_KEYYes
Content-Typeapplication/jsonYes

Request Body Parameters

ParameterTypeRequiredDescription
phoneNumberIdstringYesYour WhatsApp phone number ID
tostringYesRecipient phone number (with country code, e.g., 919876543210)
typestringYestext | template | image | video | audio | document
textstringIf type=textText message content (also used as caption for media if caption omitted)
templateNamestringIf type=templateName of approved WhatsApp template
templateLanguagestringIf type=templateTemplate language code (e.g., "en", "en_US")
templateVariablesarrayNoArray of variable values for template
mediaIdstringFor session media*Meta media ID from POST /messages/upload-media (preferred)
mediaUrlstringFor session media*Public HTTPS media link, or template header media URL
captionstringNoCaption for image/video/document (max 1024)
filenamestringNoFilename for document type

* For image/video/audio/document, provide mediaId or mediaUrl.

Do not send type: "text" with mediaUrl expecting a photo β€” that only delivers text. Use type: "image" (or video/audio/document). Session media requires an open 24-hour window.

Example: Send Text Message

POST https://chatyug.com/api/external/send-message Authorization: Bearer ck_your_api_key_here Content-Type: application/json { "phoneNumberId": "719142414620783", "to": "919876543210", "type": "text", "text": "Hello! This is a test message from my CRM." }

Example: Send Image (session media)

{ "phoneNumberId": "719142414620783", "to": "919876543210", "type": "image", "mediaId": "123456789012345", "caption": "Product photo" }

Upload first: POST https://chatyug.com/api/external/messages/upload-media (multipart mediaFile + phoneNumberId) β†’ use returned data.mediaId.

Example: Send Template Message

POST https://chatyug.com/api/external/send-message Authorization: Bearer ck_your_api_key_here Content-Type: application/json { "phoneNumberId": "719142414620783", "to": "919876543210", "type": "template", "templateName": "order_confirmation", "templateLanguage": "en", "templateVariables": ["John Doe", "ORD-12345", "β‚Ή1,299"] }

Response Format

{ "success": true, "message": "Message sent successfully", "data": { "messageId": "wamid.HBgMOTE5ODc2NTQzMjEw...", "conversationId": 12345, "status": "sent", "to": "919876543210", "type": "image", "timestamp": "2025-12-25T12:00:00.000Z" } }

Inbound media URLs

Customer photos are downloaded to ChatYug at /received_media/.... Call GET https://chatyug.com/api/external/messages/:messageId/media for a stable absolute URL. Meta lookaside CDN links expire in minutes and cannot be recovered after expiry if only the CDN URL was stored.

3.2 Check Delivery Status Endpoint

Endpoint: GET https://chatyug.com/api/external/delivery-status/:messageId

Description: Check the delivery status of a previously sent message.

Request Headers

HeaderValueRequired
AuthorizationBearer YOUR_API_KEYYes

URL Parameters

ParameterTypeRequiredDescription
messageIdstringYesThe message ID returned from send-message endpoint

Example Request

GET https://chatyug.com/api/external/delivery-status/wamid.HBgMOTE5ODc2NTQzMjEw... Authorization: Bearer ck_your_api_key_here

Response Format

{ "success": true, "data": { "messageId": "wamid.HBgMOTE5ODc2NTQzMjEw...", "status": "delivered", "to": "919876543210", "customerName": "John Doe", "messageType": "template", "content": "Template: order_confirmation", "sentAt": "2025-12-25T12:00:00.000Z", "deliveredAt": "2025-12-25T12:00:05.000Z", "readAt": "2025-12-25T12:00:15.000Z", "error": null } }

Status Values

StatusMeaningWhen It Occurs
sentMessage sent to WhatsAppImmediately after sending
deliveredMessage delivered to phoneWhen phone receives message
readMessage read by recipientWhen user opens WhatsApp
failedMessage failed to sendIf delivery fails
πŸ“Š Understanding Message Status:
Message status is updated automatically via webhooks from WhatsApp. You don't need to poll continuously - just check the status when needed. Status updates typically arrive within seconds of the event occurring.

3.3 Carousel Template API

Send approved media card carousel templates via API. Register templates in the ChatYug portal; use the API to list and send only.

Prerequisites: Template must be APPROVED in Meta. Each card needs a headerMediaId from portal media upload (external upload planned Phase 1b).

3.3.1 Upload Header Media

Endpoint: POST https://chatyug.com/api/external/messages/upload-media

Auth: Bearer API key. Content-Type: multipart/form-data

FieldRequiredDescription
mediaFileYesImage or video file (field name matches portal; max 15MB)
phoneNumberIdYesWhatsApp phone number ID for this API key WABA
mediaTypeNoimage (default) or video β€” local storage subfolder
curl -X POST https://chatyug.com/api/external/messages/upload-media \ -H "Authorization: Bearer YOUR_API_KEY" \ -F "mediaFile=@header.jpg" \ -F "phoneNumberId=YOUR_PHONE_NUMBER_ID" \ -F "mediaType=image"
{ "success": true, "message": "Media uploaded successfully", "data": { "mediaId": "1234567890", "mediaUrl": "https://chatyug.com/chat_media/images/chat_....jpg", "filename": "chat_123.jpg", "size": 102400, "mimetype": "image/jpeg" } }

Use data.mediaId as cards[].headerMediaId when sending carousel messages.

3.3.2 List Carousel Templates

Endpoint: GET https://chatyug.com/api/external/templates/carousel

Query: status (optional, default APPROVED) β€” APPROVED, PENDING, REJECTED

GET https://chatyug.com/api/external/templates/carousel?status=APPROVED Authorization: Bearer YOUR_API_KEY
{ "success": true, "templates": [ { "templateId": 1217, "templateName": "summer_sale_carousel", "status": "APPROVED", "languageCode": "en_US", "carouselConfig": { "cards": [], "source": "catalog" }, "createdAt": "2026-06-03T10:30:00.000Z" } ] }

3.3.3 Get Single Carousel Template

Endpoint: GET https://chatyug.com/api/external/templates/carousel/:templateId

3.3.4 Send Carousel Message

Endpoint: POST https://chatyug.com/api/external/messages/carousel

{ "phoneNumberId": "YOUR_PHONE_NUMBER_ID", "to": "919876543210", "templateName": "summer_sale_carousel", "templateId": 1217, "languageCode": "en_US", "bodyParams": [{ "type": "text", "text": "Rahul" }], "cards": [ { "card_index": 0, "headerMediaId": "META_MEDIA_ID", "headerMediaType": "image", "bodyParams": [], "buttons": [ { "sub_type": "quick_reply", "index": "0", "parameters": [{ "type": "payload", "payload": "shop" }] } ] }, { "card_index": 1, "headerMediaId": "META_MEDIA_ID_2", "headerMediaType": "image", "bodyParams": [], "buttons": [ { "sub_type": "quick_reply", "index": "0", "parameters": [{ "type": "payload", "payload": "buy" }] } ] } ] }

Responses: 200 success; 403 TEMPLATE_NOT_APPROVED; 400 validation; 404 phone/template not found.

Note: Bulk carousel (Phase 2) and scheduled carousel are portal-only. Register/sync/delete templates via portal (Phase 3).

Carousel error codes

CodeHTTPDescription
TEMPLATE_NOT_APPROVED403Template exists but Status β‰  APPROVED
TEMPLATE_NOT_FOUND404templateId not in this account
PHONE_NOT_FOUND404phoneNumberId not in this account
VALIDATION_ERROR400Invalid cards count, missing fields
META_ERROR502Meta API rejected the request

Feature availability

FeaturePortalExternal API
Register templateYesPhase 3
Sync Meta statusYesPhase 3
Send approved carouselYesYes (Phase 1)
Upload header mediaYesYes (Phase 1b)
Bulk carouselYesPhase 2
Scheduled carouselYesNot planned

Note: Create and approve templates in the portal. Bulk carousel (Phase 2) is portal-only at this time.

Integrator workflow

  1. Create or register a carousel template in the portal (Carousel Templates page)
  2. Wait for Meta approval; Sync from Meta in portal
  3. GET templates/carousel?status=APPROVED β€” inspect carouselConfig
  4. POST messages/upload-media per card β†’ collect mediaId as headerMediaId
  5. POST messages/carousel
  6. GET delivery-status/:messageId

3.4 Catalog API

Manage WhatsApp product catalogs and send catalog messages. Base path: https://chatyug.com/api/external. One API key is scoped to one WABA; catalog data is stored in Meta and mirrored in ChatYug.

Third-party / ERP control: Create, update, list, and delete products plus sale/coupon offers using only your Bearer API key β€” no ChatYug portal required. Path :catalogId accepts the Meta Catalog ID or the ChatYug internal Catalog_Id.
Required fields by catalog vertical:
  • commerce / generic (typical shop catalog): required = productName, price, imageUrl, productUrl. Optional = retailerId, currency, description, availability, category, brand, inventory. Address / lat / long are not required and are ignored for Meta.
  • local_service_businesses / offline_commerce: same core fields plus locality β€” full address, or lat+long, or postal code alone. ChatYug returns LOCALITY_REQUIRED if missing.
Product images: Pass any public HTTPS imageUrl on create/update, or upload a JPEG/PNG/WebP file via POST /catalogs/upload-image and use the returned imageUrl. Meta requires a crawlable public URL (max 5MB).
Categories: Not separate Meta objects β€” they are labels on each product (category field). GET /categories aggregates distinct labels. Manage via assign / rename / clear endpoints, or set category on create/update. Empty categories are unsupported (a label appears only after at least one product uses it).
Integration pattern: Sync products from your ERP via POST /catalogs/:id/products, then send product-list or single-product messages inside the 24-hour session, or use carousel templates for marketing outside the session.
Category browse (SHOP keyword): ChatYug does not auto-reply to SHOP. Your integrator app receives inbound webhooks and calls GET /catalogs/:id/categories β†’ POST /messages/interactive β†’ on list_reply, GET /catalogs/:id/product-list-payload?category={slug} β†’ POST /messages/product-list.

GET /catalogs β€” List catalogs

Returns active catalogs linked to your WABA.

curl -X GET https://chatyug.com/api/external/catalogs \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "success": true,
  "total": 1,
  "catalogs": [
    {
      "Catalog_Id": 1,
      "Meta_Catalog_Id": "980328057290841",
      "Catalog_Name": "My Shop Catalog",
      "Product_Count": 45,
      "Is_Active": true,
      "Created_At": "2025-01-15T10:30:00.000Z"
    }
  ]
}

POST /catalogs β€” Create catalog

FieldRequiredDescription
catalogNameYesDisplay name
verticalYescommerce (recommended), generic, local_service_businesses, offline_commerce, or other Meta-supported verticals
curl -X POST https://chatyug.com/api/external/catalogs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"catalogName": "My Shop Catalog", "vertical": "commerce"}'

GET /catalogs/:catalogId/products β€” List products

Query: page, limit (max 200), search (name/SKU/description), availability (in stock / out of stock), category (category name or slug from GET /categories).

curl -X GET "https://chatyug.com/api/external/catalogs/980328057290841/products?page=1&limit=50" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "success": true,
  "products": [
    {
      "Product_Id": 101,
      "Product_Name": "Premium Cotton T-Shirt",
      "Retailer_ID": "TSHIRT-WHITE-M",
      "Price": 599.00,
      "Currency": "INR",
      "Image_URL": "https://example.com/tshirt.jpg",
      "Product_URL": "https://example.com/products/tshirt",
      "Availability": "in stock",
      "Category": "Apparel",
      "Brand": "MyBrand",
      "Inventory": 150,
      "sale_price": 499.00
    }
  ],
  "pagination": { "page": 1, "limit": 50, "total": 1, "pages": 1 }
}

GET /catalogs/:catalogId/categories β€” List categories

Returns distinct browse categories with product counts. Includes interactiveListSections ready for POST /messages/interactive (category picker). Products without a category are grouped as Uncategorized.

QueryDescription
inStockOnlyDefault true β€” only count in-stock products
curl -X GET "https://chatyug.com/api/external/catalogs/980328057290841/categories" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "success": true,
  "catalogId": "980328057290841",
  "categories": [
    { "name": "Kitchen Appliances", "slug": "cat_kitchen_appliances", "productCount": 5 },
    { "name": "Dairy", "slug": "cat_dairy", "productCount": 3 }
  ],
  "interactiveListSections": [
    {
      "title": "Categories",
      "rows": [
        { "id": "cat_kitchen_appliances", "title": "Kitchen Appliances", "description": "5 products" },
        { "id": "cat_dairy", "title": "Dairy", "description": "3 products" }
      ]
    }
  ]
}

Row id values are category slug strings (e.g. cat_dairy). Pass the slug back as ?category= on product-list-payload when the customer taps a row.

POST /catalogs/:catalogId/categories/assign β€” Assign category to products

Set the category label on one or more SKUs. Empty categories are not supported β€” assign to products so they appear in GET /categories.

FieldRequiredDescription
nameYesCategory label (e.g. Dairy)
retailerIdsYesArray of product SKUs
curl -X POST https://chatyug.com/api/external/catalogs/980328057290841/categories/assign \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Dairy", "retailerIds": ["MILK-1L", "YOGURT-500G"] }'

PUT /catalogs/:catalogId/categories β€” Rename category

Bulk-rename a label across all products. Cannot rename Uncategorized.

curl -X PUT https://chatyug.com/api/external/catalogs/980328057290841/categories \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "from": "Dairy", "to": "Milk & Yogurt" }'

DELETE /catalogs/:catalogId/categories/:slugOrName β€” Clear category

Clears Category on matching products (they become Uncategorized in GET). Path may be a slug (cat_dairy) or URL-encoded name.

curl -X DELETE https://chatyug.com/api/external/catalogs/980328057290841/categories/cat_dairy \
  -H "Authorization: Bearer YOUR_API_KEY"

GET /catalogs/:catalogId/product-list-payload β€” Build product_list sections

Builds Meta-ready sections and a flat productRetailerIds array dynamically from the catalog β€” no hardcoded product lists.

QueryDescription
categoryCategory slug from GET /categories (e.g. cat_dairy)
groupByCategoryIf true and no category, one section per category (max 30 products total)
curl -X GET "https://chatyug.com/api/external/catalogs/980328057290841/product-list-payload?category=cat_dairy" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "success": true,
  "catalogId": "980328057290841",
  "sections": [
    {
      "title": "Dairy",
      "product_items": [
        { "product_retailer_id": "MILK-1L" },
        { "product_retailer_id": "YOGURT-500G" }
      ]
    }
  ],
  "productRetailerIds": ["MILK-1L", "YOGURT-500G"],
  "totalProducts": 2,
  "truncated": false
}

Meta limits: max 30 products per product_list message. If truncated: true, send follow-up messages with remaining retailer IDs.

POST /catalogs/upload-image β€” Upload product image

Multipart upload (field name image). JPEG, PNG, or WebP β€” max 5MB. Returns a public HTTPS imageUrl for create/update. You may also pass any existing public CDN URL as imageUrl without uploading.

curl -X POST https://chatyug.com/api/external/catalogs/upload-image \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "image=@/path/to/product.jpg"
{
  "success": true,
  "message": "Image uploaded successfully",
  "data": {
    "imageUrl": "https://chatyug.com/uploads/product-images/product-1720856400000-123.jpg",
    "imagePath": "/uploads/product-images/product-1720856400000-123.jpg",
    "filename": "product-1720856400000-123.jpg",
    "size": 84210,
    "mimeType": "image/jpeg"
  }
}

POST /catalogs/:catalogId/products β€” Create product

FieldRequiredDescription
productNameYesProduct title
priceYesNumeric price in major units (e.g. 599 for β‚Ή599)
currencyNoDefault INR
retailerIdNoSKU; auto-generated if omitted
descriptionNoProduct description
imageUrlYesPublic HTTPS image URL (required for Meta). Upload via POST /catalogs/upload-image or use your CDN
productUrlYesFull http(s) product/shop URL β€” Meta link under WhatsApp description (never ChatYug.com default)
availabilityNoin stock or out of stock
categoryNoBrowse category label (e.g. Dairy) β€” drives category picker flow
brandNoBrand name (optional for Meta)
inventoryNoStock count integer (optional)

Locality (only when catalog vertical is local_service_businesses or offline_commerce): provide a full address (availabilityAddressStreet, City, State, PostalCode, Country), or availabilityLatitude+availabilityLongitude, or postal code alone. Not required for commerce catalogs.

curl -X POST https://chatyug.com/api/external/catalogs/980328057290841/products \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "productName": "Premium Cotton T-Shirt",
    "description": "100% cotton, comfortable fit",
    "price": 599,
    "currency": "INR",
    "retailerId": "TSHIRT-WHITE-M",
    "imageUrl": "https://example.com/tshirt.jpg",
    "productUrl": "https://example.com/products/tshirt",
    "availability": "in stock",
    "category": "Apparel",
    "brand": "MyBrand",
    "inventory": 150
  }'
{
  "success": true,
  "message": "Product created successfully",
  "data": {
    "productId": 101,
    "metaProductId": "5678901234567",
    "retailerId": "TSHIRT-WHITE-M",
    "productName": "Premium Cotton T-Shirt",
    "price": 599,
    "currency": "INR"
  }
}

PUT /catalogs/:catalogId/products/:retailerId β€” Update product

Partial update β€” send only fields to change. Same body fields as create (all optional).

curl -X PUT https://chatyug.com/api/external/catalogs/980328057290841/products/TSHIRT-WHITE-M \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "price": 499, "availability": "in stock", "inventory": 200 }'

DELETE /catalogs/:catalogId/products/:retailerId β€” Remove product

Deletes the product from Meta and ChatYug by SKU (retailerId). If Meta delete fails, ChatYug still removes the local row and may return metaWarning.

curl -X DELETE https://chatyug.com/api/external/catalogs/980328057290841/products/TSHIRT-WHITE-M \
  -H "Authorization: Bearer YOUR_API_KEY"
{ "success": true, "message": "Product deleted successfully", "data": { "productId": 101, "retailerId": "TSHIRT-WHITE-M" } }

GET /catalogs/:catalogId/offers β€” List offers

Returns sale / coupon / auto offers for the catalogue (scoped to your API key WABA).

curl -X GET https://chatyug.com/api/external/catalogs/980328057290841/offers \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "success": true,
  "offers": [
    {
      "offerId": 1,
      "applicationType": "SALE",
      "valueType": "EXACT_SALE_PRICE",
      "salePrice": 225.90,
      "targetSelection": "SPECIFIC_PRODUCTS",
      "targetRetailerIds": ["TSHIRT-WHITE-M"],
      "status": "ACTIVE"
    }
  ]
}

POST /catalogs/:catalogId/offers β€” Create sale / offer

applicationType: SALE sets Meta sale_price (WhatsApp strikethrough). Coupon/auto use Meta Offers API (FB/IG). Works for every WABA.

FieldRequiredDescription
applicationTypeYesSALE | AUTOMATIC_AT_CHECKOUT | BUYER_APPLIED
valueTypeYesEXACT_SALE_PRICE | PERCENTAGE | FIXED_AMOUNT
salePrice*Exact sale price (major units) when EXACT_SALE_PRICE
percentOff*1–100 for PERCENTAGE
fixedAmountOff*Amount off for FIXED_AMOUNT
targetSelectionNoSPECIFIC_PRODUCTS (default) or ALL_CATALOG_PRODUCTS
targetRetailerIds*SKU array for specific products
couponCode*Required for BUYER_APPLIED
startAtNoOptional ISO start datetime (API field; alias startTime not used)
endAtNoOptional ISO end datetime

Offer date windows use startAt / endAt on this API. If your app UI omits them, that is an integrator UI gap β€” ChatYug accepts the fields.

curl -X POST https://chatyug.com/api/external/catalogs/980328057290841/offers \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "applicationType": "SALE",
    "valueType": "EXACT_SALE_PRICE",
    "salePrice": 225.90,
    "targetSelection": "SPECIFIC_PRODUCTS",
    "targetRetailerIds": ["TSHIRT-WHITE-M"]
  }'

DELETE /catalogs/offers/:offerId β€” Deactivate offer

For SALE offers, clears Meta sale_price on targeted products.

curl -X DELETE https://chatyug.com/api/external/catalogs/offers/1 \
  -H "Authorization: Bearer YOUR_API_KEY"

POST /messages/product-list β€” Send product list

FieldRequiredDescription
phoneNumberIdYesWhatsApp phone number ID
toYesRecipient (e.g. 919876543210 or +91 98765 43210)
catalogIdYesMeta catalog ID
headerTextNoMessage header
bodyTextYesMessage body
sectionsOne of*Meta format: [{ title, product_items: [{ product_retailer_id }] }]
productRetailerIdsOne of*Flat SKU array β€” single section (legacy / flat browse)
sectionTitleNoTitle when using productRetailerIds only (default: Products)

*Either sections[] or productRetailerIds[] is required.

curl -X POST https://chatyug.com/api/external/messages/product-list \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumberId": "YOUR_PHONE_NUMBER_ID",
    "to": "919876543210",
    "catalogId": "980328057290841",
    "sections": [{
      "title": "Dairy",
      "product_items": [
        { "product_retailer_id": "MILK-1L" },
        { "product_retailer_id": "YOGURT-500G" }
      ]
    }],
    "bodyText": "Browse Dairy products"
  }'

Category browse β€” end-to-end integrator flow

  1. Customer sends text (e.g. SHOP) β†’ your webhook receives eventType: message
  2. GET /catalogs/{metaCatalogId}/categories
  3. POST /messages/interactive with interactiveType: "list" and sections from interactiveListSections
  4. Customer taps a category β†’ webhook interactive.list_reply.id = category slug (e.g. cat_dairy)
  5. GET /catalogs/{id}/product-list-payload?category={slug}
  6. POST /messages/product-list with returned sections or productRetailerIds

See also docs/INTEGRATOR_WHATSAPP_COMMERCE_REFERENCE.md Β§4.3 for full curl examples and webhook payloads.

POST /messages/single-product β€” Send single product card

FieldRequiredDescription
phoneNumberIdYes
toYes
catalogIdYes
productRetailerIdYesSKU from catalog
bodyTextYesCaption text

Catalog errors

HTTPWhen
400Validation (missing fields, invalid price)
403Catalog not owned by this API key WABA
404Catalog or product not found
502Meta Graph API error

3.5 Orders API

WhatsApp Commerce orders (native cart or flow checkout). Orders arrive via the order webhook and are queryable here.

Integrator journey (cart β†’ fulfil):
  1. Receive webhook eventType: order (raw Meta in data.order, enriched lines in data.chatyug)
  2. GET /orders/:id β€” items, flow_token, and checkout_submission (answers + media) once the customer completes checkout Flow
  3. Checkout Flow: enable auto-send in PUT /commerce/settings, or POST /messages/flow with flowToken = order flow_token
  4. Push: webhook nfm_reply; pull: analytics media APIs or order.checkout_submission
  5. PUT /orders/:id/status (+ optional POST .../notify)
Status pipeline: pending β†’ confirmed β†’ shipped β†’ delivered (or cancelled). PUT /orders/:id/status can auto-notify the customer on WhatsApp unless notifyCustomer: false.
Order numbers: id is the internal PK (use in path params). order_number is the per-WABA display number (prefix + sequence). Customer messages use the display number. Older rows may have order_number: null β€” fall back to id.

GET /commerce/settings

Read auto-ack and order numbering for the API key WABA. Catalog link / RSA / Meta cart toggles remain portal-only.

curl -X GET "https://chatyug.com/api/external/commerce/settings" \
  -H "Authorization: Bearer YOUR_API_KEY"

PUT /commerce/settings

Update auto-ack and/or numbering. When autoAckEnabled: true, require autoAckBody (type text) or autoAckTemplateName (type template). Placeholders: {{order_number}}, {{items}}, {{total}}, {{currency}}, {{customer_name}}.

FieldRequiredDescription
autoAckEnabledNo*boolean β€” *required content when true
autoAckTypeNotext | template
autoAckBodyCond.Free-text ack with placeholders
autoAckTemplateNameCond.APPROVED Meta template name
autoAckTemplateLanguageNoDefault en
autoAckTemplateVarsNoJSON array of placeholder names in body-param order
orderNumberPrefixNoe.g. ORD-
orderNumberPadNo0–10 zero-pad width
orderNumberNextNoNext sequence (β‰₯1); does not rewrite old orders
autoSendCheckoutFlowNoAuto-send checkout Flow after cart
checkoutFlowIdNoMeta Flow ID
curl -X PUT "https://chatyug.com/api/external/commerce/settings" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "autoAckEnabled": true, "autoAckType": "text", "autoAckBody": "βœ… Order #{{order_number}}\n{{items}}\nTotal: {{currency}} {{total}}", "orderNumberPrefix": "ORD-", "orderNumberPad": 4, "orderNumberNext": 1 }'

GET /orders

Query: status, page, pageSize, dateFrom/dateTo (YYYY-MM-DD), dateFromUtc/dateToUtc (ISO).

curl -X GET "https://chatyug.com/api/external/orders?status=pending&page=1&pageSize=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "success": true,
  "total": 42,
  "page": 1,
  "pageSize": 20,
  "orders": [
    {
      "id": 101,
      "order_number": "ORD-0007",
      "customer_phone": "919876543210",
      "customer_name": "Rahul Sharma",
      "status": "pending",
      "total_amount": "4999.00",
      "currency": "INR",
      "items_count": 2,
      "items": [{ "product_retailer_id": "SKU-001", "quantity": 1, "item_price": "599", "currency": "INR" }]
    }
  ]
}

GET /orders/:orderId

Full order with id, order_number, items, shipping_address, flow_token, timestamps, and checkout_submission (latest Flow Analytics submit for that flow_token, or null if the customer has not completed the form yet).

{
  "success": true,
  "order": {
    "id": 101,
    "order_number": "ORD-0007",
    "flow_token": "order_1744012345_abc12",
    "shipping_address": { "delivery_address": "12 MG Road", "city": "Bharuch", "pincode": "392001" },
    "items": [{ "product_retailer_id": "SKU-001", "product_name": "White Tee", "quantity": 1, "item_price": 599, "currency": "INR" }],
    "checkout_submission": {
      "eventId": 69,
      "submittedAt": "2026-06-23T10:20:00.000Z",
      "payload": { "customer_name": "Rahul", "delivery_address": "12 MG Road", "photo_id": { "id": "META_MEDIA_ID", "chatyug_url": "https://chatyug.com/received_media/..." } },
      "attachments": [
        {
          "field": "photo_id",
          "mediaType": "image",
          "downloadUrl": "https://chatyug.com/api/external/commerce/flows/analytics/events/69/media/photo_id",
          "metadataUrl": "https://chatyug.com/api/external/commerce/flows/analytics/events/69/media/photo_id?format=json"
        }
      ],
      "hasMedia": true
    }
  }
}

Download Flow uploads with Bearer auth on attachments[].downloadUrl (same as GET /commerce/flows/analytics/events/:eventId/media/:fieldName).

PUT /orders/:orderId/status

FieldRequiredDescription
statusYespending | confirmed | shipped | delivered | cancelled
noteNoInternal note; included in customer notification
notifyCustomerNoDefault true β€” sends WhatsApp text update (shows display order number)
{ "success": true, "message": "Order status updated to shipped", "notifySent": true }

POST /orders/:orderId/note

Body: { "note": "AWB: DTDC123456789" } β€” internal note only, no status change.

POST /orders/:orderId/notify

Body: { "message": "Custom WhatsApp text..." } β€” omit message for default status-based text (includes display order number).

3.6 Commerce Flows API

WhatsApp Flows for checkout / customer-details forms. Author via portal Flow Design or External API flowJson (full node reference, multi-screen recipes, POST /commerce/flows/media for Image src). Publish to Meta, then send. Deprecate / delete / Meta sync remain portal-only. Deep guide: Commerce Flows API β€” flowJson design.

Push vs pull: Customer completions arrive on your webhook as message / interactive.type: nfm_reply (raw response_json; PhotoPicker fields often contain Meta media IDs only). For downloadable ChatYug media URLs use Flow Analytics (attachments[]) or GET /orders/:id β†’ checkout_submission when the send used the order flow_token. Recipe: fetch design + analytics + media.

GET /commerce/flows

List flows for your WABA.

GET /commerce/flows/:flowId

Single flow detail (local id or as returned by list) β€” includes flow_json design.

POST /commerce/flows/media β€” Upload Image for flowJson

Multipart field mediaFile (JPEG/PNG/WebP, max 5MB). Returns public HTTPS url for Image.src / ImageCarousel. Portal designer stays paste-URL only.

curl -X POST "https://chatyug.com/api/external/commerce/flows/media" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "mediaFile=@./banner.jpg"

POST /commerce/flows β€” Create flow on Meta

FieldRequiredDescription
nameYesInternal flow name
categoryNoMeta category string, e.g. OTHER (default OTHER)
flowJsonNoWhatsApp Flow JSON (screens, fields). Per-node examples: commerce-flows.html#flow-nodes
curl -X POST "https://chatyug.com/api/external/commerce/flows" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Checkout", "category": "OTHER", "flowJson": { "version": "7.3", "screens": [] } }'

PUT /commerce/flows/:flowId

Update draft flow name and/or flowJson before publish.

POST /commerce/flows/:flowId/publish

Publishes draft flow to Meta. Required before sending to customers.

POST /messages/flow β€” Send Flow message

Send a published Flow to a customer (24h session or as allowed by Meta). To bind answers to a cart order, pass flowToken = that order’s flow_token (from GET /orders/:id or auto-send).

FieldRequiredDescription
phoneNumberIdYes
toYesCustomer phone
flowIdYesMeta flow ID from create/list
flowTokenNoCorrelation token returned in nfm_reply and stored on analytics submit
headerTextNo
bodyTextYes
ctaTextNoButton label (default "Continue")
curl -X POST https://chatyug.com/api/external/messages/flow \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumberId": "YOUR_PHONE_NUMBER_ID",
    "to": "919876543210",
    "flowId": "META_FLOW_ID",
    "flowToken": "order_1744012345_abc12",
    "bodyText": "Please fill in your delivery details",
    "ctaText": "Proceed"
  }'
{
  "success": true,
  "message": "Flow message sent",
  "flowToken": "order_1744012345_abc12",
  "messageId": "wamid.HBgM..."
}

GET /commerce/flows/analytics

Query: flowId (local), metaFlowId, eventType (send|submit), phone, page, pageSize, date filters. Each event includes parsed payload and flat attachments[] with downloadUrl / metadataUrl.

GET /commerce/flows/analytics/summary

Aggregated send/submit counts for filters above.

GET /commerce/flows/analytics/export

Query: flowId, format=xlsx|json. Customer submissions export (Excel columns from form fields).

GET /commerce/flows/analytics/events/:eventId/media/:fieldName

Stream a PhotoPicker/DocumentPicker file for one submit event (Bearer API key). Add ?format=json for metadata only. Same URLs appear on attachments[].downloadUrl and on order.checkout_submission.attachments.

curl -OJ "https://chatyug.com/api/external/commerce/flows/analytics/events/69/media/photo_id" \
  -H "Authorization: Bearer YOUR_API_KEY"

3.7 Chatbot Flows API

Keyword-triggered automation inside ChatYug. Requires automation permission on the WABA. Alternative to building your own keyword engine on inbound webhooks.

When to use: Simple keyword β†’ journey flows managed in ChatYug. For distributor-by-pincode routing or custom COD logic, use inbound message webhooks and your own engine (hybrid architecture).

GET /chatbot/flows

Query: phoneNumberId, published=true, active=true.

POST /chatbot/flows

FieldRequiredDescription
flowNameYes
triggerKeywordsYesComma-separated, e.g. SHOP,CATALOGUE
flowDataYesJSON graph: { nodes: [], edges: [] }
phoneNumberIdNoScope to a phone number
sessionTimeoutMinutesNoDefault session timeout

GET /chatbot/flows/:flowId

PUT /chatbot/flows/:flowId

Update name, keywords, flowData, isActive.

403 if automation permission not enabled for WABA.

3.8 Connectivity Test

GET /test

No API key. Use for load-balancer / integration health checks.

GET https://chatyug.com/api/external/test
{ "success": true, "message": "External API is working!", "timestamp": "2026-06-23T10:00:00.000Z" }

GET /me

Validates API key and returns account context. Use in your integration settings screen (wf-17).

curl -X GET https://chatyug.com/api/external/me -H "Authorization: Bearer YOUR_API_KEY"
{
  "success": true,
  "userId": 9,
  "wabaId": "815098678039367",
  "businessName": "My Business",
  "phoneNumbers": [
    {
      "Phone_Number_Id": "719142414620783",
      "Phone_Number": "919876543210",
      "Display_Phone_Number": "+91 98765 43210"
    }
  ]
}

3.9 Integrator API (Inbound + Session)

Read conversations, send interactive replies, and fetch media β€” for chat consoles and commerce bots driven by webhooks.

POST /messages/interactive β€” Button or list message

Within 24 hours of the customer's last inbound message. Use for distributor selection, project pickers, COD confirm, etc.

FieldRequiredDescription
phoneNumberIdYes
toYesRecipient phone
interactiveTypeYesbutton (1–3 buttons) or list
bodyTextYesMain message (max 1024 chars)
headerTextNoMax 60 chars
footerTextNoMax 60 chars
buttonsIf button[{ id, title }] β€” id max 256, title max 20
listButtonTextIf listOpen list label (max 20)
sectionsIf list[{ title, rows: [{ id, title, description? }] }] β€” max 10 rows per section
contextMessageIdNoReply in thread to a specific message
curl -X POST https://chatyug.com/api/external/messages/interactive \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumberId": "YOUR_PHONE_NUMBER_ID",
    "to": "919876543210",
    "interactiveType": "list",
    "bodyText": "Select your distributor",
    "listButtonText": "View distributors",
    "sections": [{
      "title": "Available",
      "rows": [
        { "id": "DIST_01", "title": "Distributor A", "description": "North zone" },
        { "id": "DIST_02", "title": "Distributor B", "description": "South zone" }
      ]
    }]
  }'
{
  "success": true,
  "message": "Interactive message sent",
  "data": {
    "messageId": "wamid.HBgM...",
    "conversationId": 12345,
    "interactiveType": "list"
  }
}

Customer replies arrive on your webhook as message with interactive.button_reply or interactive.list_reply.

GET /conversations

Query: phoneNumberId (required), page, pageSize (max 200), search (phone/name).

{
  "success": true,
  "page": 1,
  "pageSize": 50,
  "total": 90,
  "conversations": [
    {
      "Conversation_Id": 12345,
      "Customer_Phone": "919876543210",
      "Customer_Name": "Rahul",
      "Last_Message_Time": "2026-06-23T09:00:00.000Z",
      "Unread_Count": 1,
      "session": {
        "sessionOpen": true,
        "lastCustomerMessageAt": "2026-06-23T08:30:00.000Z",
        "expiresAt": "2026-06-24T08:30:00.000Z"
      }
    }
  ]
}
24-hour window: Each conversation includes session.sessionOpen, lastCustomerMessageAt, and expiresAt (ISO timestamp). When sessionOpen is false, use template messages via POST /send-message with type: "template".

GET /conversations/:conversationId/messages

Query: limit (max 500), offset. Returns messages, conversation, session, total.

Each message includes semantic fields for WhatsApp-like rendering:

{
  "success": true,
  "messages": [
    {
      "messageId": 5001,
      "direction": "received",
      "messageType": "order",
      "contentType": "cart_order",
      "content": "1 item Β· β‚Ή251.00",
      "text": "1 item Β· β‚Ή251.00",
      "cart": {
        "catalog_id": "980328057290841",
        "items_count": 1,
        "total_amount": 251,
        "currency": "INR",
        "thumbnail_url": "https://cdn.example.com/amul.jpg",
        "order_id": 5,
        "order_number": "5",
        "items": [
          {
            "product_retailer_id": "SKU-001",
            "product_name": "Amul Protein Water (250ML)",
            "quantity": 1,
            "item_price": 251,
            "currency": "INR",
            "image_url": "https://cdn.example.com/amul.jpg"
          }
        ]
      },
      "templateVariables": { "kind": "cart_order" }
    },
    {
      "messageId": 5002,
      "direction": "sent",
      "messageType": "text",
      "contentType": "commerce_auto_ack",
      "content": "βœ… Order Received!\n\nOrder #5\nβ€’ Amul Protein Water (250ML) Γ— 1  INR 251.00\n\nTotal: INR 251.00\n\nWe'll confirm your order shortly. Thank you! πŸ™",
      "ack": { "ack_type": "text", "order_id": 5, "order_number": "5" }
    }
  ]
}

Also use outbound webhook eventType: order β†’ data.chatyug for realtime cart push (see Β§4.5).

GET /messages/:messageId/media

Returns mediaUrl for messages with attachments. Relative paths (e.g. /received_media/...) are served from your ChatYug host; persist a copy in your system for long-term audit.

{
  "success": true,
  "messageId": 98765,
  "mediaType": "image",
  "mediaUrl": "/received_media/919876543210/image_123.jpg",
  "direction": "received",
  "note": "Relative URLs are served from chatyug.com. Meta-hosted media may require portal download."
}
Media URL lifetime: Files under /received_media/ are stored on ChatYug servers and remain available while the message exists in ChatYug. Meta CDN / lookaside URLs expire in minutes β€” never cache them. Call GET /messages/:messageId/media; if only a Meta media ID is stored, ChatYug downloads and persists the file on demand.

Integrator API errors

HTTPMeaning
400Missing/invalid body (e.g. wrong button count, missing phoneNumberId)
401Invalid or missing API key
403Phone number not in this WABA
404Conversation or message not found
502Meta rejected interactive/flow send (session closed, policy, etc.)

3.10 Standard Templates API

List approved WhatsApp message templates (non-carousel) registered in ChatYug, including variable placeholders for POST /send-message. Carousel templates use GET /templates/carousel (Β§3.3).

GET /templates β€” List templates

Query: status (APPROVED default; PENDING, REJECTED, ALL), search, category, language

curl -X GET "https://chatyug.com/api/external/templates?status=APPROVED" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "success": true,
  "total": 2,
  "statistics": { "total": 2, "approved": 2, "pending": 0, "rejected": 0 },
  "templates": [
    {
      "templateId": 42,
      "templateName": "order_confirmation",
      "language": "en",
      "category": "UTILITY",
      "status": "APPROVED",
      "bodyText": "Hello {{1}}, your order {{2}} is confirmed for {{3}}.",
      "headerText": null,
      "footerText": "Thank you for shopping with us",
      "mediaType": "None",
      "variableCount": 3,
      "variables": [
        { "index": 1, "component": "body", "placeholder": "{{1}}", "example": "Rahul", "type": "text" },
        { "index": 2, "component": "body", "placeholder": "{{2}}", "example": "ORD-12345", "type": "text" },
        { "index": 3, "component": "body", "placeholder": "{{3}}", "example": "β‚Ή1,299", "type": "text" }
      ],
      "buttons": [],
      "sampleSendRequest": {
        "phoneNumberId": "YOUR_PHONE_NUMBER_ID",
        "to": "919876543210",
        "type": "template",
        "templateName": "order_confirmation",
        "templateLanguage": "en",
        "templateVariables": ["Rahul", "ORD-12345", "β‚Ή1,299"]
      }
    }
  ],
  "usage": {
    "sendEndpoint": "POST /api/external/send-message",
    "note": "Pass templateVariables as array in {{1}}, {{2}} order"
  }
}

GET /templates/:templateId β€” Single template

curl -X GET https://chatyug.com/api/external/templates/42 -H "Authorization: Bearer YOUR_API_KEY"

Returns template (full detail + components) and sendMessageExample ready to POST.

Integration workflow

  1. GET /templates?status=APPROVED β€” populate template picker in your portal
  2. User selects template β†’ read variables array for form fields
  3. Build templateVariables array from user input in placeholder order
  4. POST /send-message with type: "template", templateName, templateLanguage
  5. Track delivery via status webhook or GET /delivery-status/:messageId
Variable format: templateVariables accepts an array ["Rahul", "ORD-123"] or object {"1":"Rahul","2":"ORD-123"}. Values map to {{1}}, {{2}} in template body/header. Templates with media headers require mediaUrl on send-message.

Template API errors

HTTPCodeWhen
404TEMPLATE_NOT_FOUNDtemplateId not in your WABA
400β€”Invalid templateId

3.11 Scheduled Messages API

Schedule template-based WhatsApp sends from your CRM, ERP, or custom platform. Requires API key + automation permission on the WABA for write endpoints.

POST /scheduled-messages

curl -X POST https://chatyug.com/api/external/scheduled-messages \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phoneNumberId":"YOUR_PHONE_NUMBER_ID","scheduleName":"Monday promo","scheduleType":"ONE_TIME","templateName":"order_update","templateLanguage":"en","scheduledDate":"2026-07-25","scheduledTime":"10:30","timezone":"Asia/Kolkata","recipients":[{"phoneNumber":"919876543210","variables":{"1":"Rahul","2":"ORD-991"}}]}'

Also: GET /scheduled-messages, GET /scheduled-messages/:id, GET /scheduled-messages/:id/report, DELETE /scheduled-messages/:id, POST /scheduled-messages/upload-csv, POST /scheduled-messages/:id/run-now.

3.12 Template Creation API

Create and submit templates to Meta via API. Always returns PENDING. Requires automation permission.

POST /templates

curl -X POST https://chatyug.com/api/external/templates \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"templateName":"order_update_api","templateContent":"Hi {{1}}, your order {{2}} is confirmed.","category":"UTILITY","language":"en"}'

GET /templates/:templateId/status, POST /templates/:templateId/sync, POST /templates/carousel.

4. Integration Theory & Concepts

4.1 Understanding REST APIs

REST (Representational State Transfer) is an architectural style for designing web services. Our API follows REST principles, making it easy to understand and integrate.

Key Concepts:

4.2 How API Integration Works

Here's the complete flow of how your software integrates with Chatyug API:

Step 1: Your Software β†’ Prepare Message Data Step 2: Your Software β†’ Send HTTP POST Request to Chatyug API Step 3: Chatyug API β†’ Validates API Key & Request Step 4: Chatyug API β†’ Sends Message to WhatsApp Step 5: WhatsApp β†’ Delivers Message to Recipient Step 6: WhatsApp β†’ Sends Status Update to Chatyug (via Webhook) Step 7: Chatyug β†’ Updates Database with Status Step 8: Your Software β†’ Can Check Status Anytime via GET Request

4.3 Understanding Message Types

Text Messages

Template Messages

πŸ’‘ Choosing the Right Message Type:
Use Text Messages when you're in an active conversation (within 24-hour window).
Use Template Messages for automated notifications, order confirmations, or when starting a new conversation outside the 24-hour window.

4.4 Understanding Webhooks (Status Updates)

WhatsApp doesn't provide a "check status" API endpoint. Instead, status updates are delivered automatically via webhooks - this is how WhatsApp designed their system.

How It Works:

  1. You send a message via our API
  2. We save the message to our database
  3. WhatsApp sends status updates to our webhook automatically
  4. We update the message status in our database
  5. You can check the status anytime by querying our database via the delivery-status endpoint
βœ… Why This Approach Works:
This is the official and only way to track message status according to Meta's WhatsApp Business API. We handle all the webhook complexity for you - you just need to check the status when needed.

4.5 Outbound Webhooks (Push to Your System)

Configure your HTTPS webhook in ChatYug portal β†’ API Configuration β†’ Webhook Settings. ChatYug POSTs JSON events to your URL. This is the primary way to receive inbound customer messages, button/list replies, Flow submissions, delivery receipts, and cart orders.

Webhook registration: URL and secret are configured only in the portal β€” there is no External API to register or rotate webhooks programmatically today. Use the portal UI and store the secret in your integration settings.

Delivery guarantees

AspectBehaviour
MethodHTTP POST, Content-Type: application/json
HeadersX-Chatyug-Event-Id (idempotency key), X-Chatyug-Signature (if secret configured)
RetriesAutomatic for up to 48 hours; backoff ~1, 2, 10, 60, 360 minutes (~12–13 attempts)
Rate limit~300 events/minute per WABA
AcknowledgementReturn HTTP 2xx quickly; process async in your app

Signature verification (confirmed)

X-Chatyug-Signature is HMAC-SHA256 of the raw JSON request body (UTF-8), prefixed with sha256=, using the secret from Webhook Settings. There is no separate timestamp header β€” use eventId for idempotency. Verify before parsing if possible (read raw body first).

const crypto = require('crypto');
function verifyChatyugSignature(rawBody, secret, signatureHeader) {
  if (!secret || !signatureHeader) return false;
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody, 'utf8')
    .digest('hex');
  try {
    return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
  } catch { return false; }
}
// Express: use express.raw({ type: 'application/json' }) on webhook route

Envelope (all events)

Every outbound webhook uses this top-level shape:

{
  "eventType": "message | status | order | test",
  "userId": 34,
  "wabaId": "1957290828491918",
  "phoneNumberId": "1079034885291122",
  "eventId": "unique-id-for-idempotency",
  "occurredAt": "2026-06-23T10:10:00.000Z",
  "data": { }
}

eventType: message β€” inbound text

{
  "eventType": "message",
  "userId": 34,
  "wabaId": "1957290828491918",
  "phoneNumberId": "1079034885291122",
  "eventId": "wamid.HBgMOTE5ODc2NTQzMjEw...",
  "occurredAt": "2026-06-23T10:10:00.000Z",
  "data": {
    "metadata": { "phone_number_id": "1079034885291122", "display_phone_number": "919876543210" },
    "contacts": [{ "profile": { "name": "Rahul" }, "wa_id": "919876543210" }],
    "message": {
      "from": "919876543210",
      "id": "wamid.HBgM...",
      "timestamp": "1719156600",
      "type": "text",
      "text": { "body": "SHOP" }
    }
  }
}

eventType: message β€” inbound media

{
  "eventType": "message",
  "data": {
    "message": {
      "type": "image",
      "image": { "id": "META_MEDIA_ID", "mime_type": "image/jpeg", "caption": "Invoice" }
    }
  }
}

Fetch stored file URL via GET /messages/:messageId/media after correlating with ChatYug message ID, or download from Meta using your own media pipeline.

eventType: message β€” button reply

{
  "eventType": "message",
  "data": {
    "message": {
      "type": "interactive",
      "interactive": {
        "type": "button_reply",
        "button_reply": { "id": "DIST_01", "title": "Distributor A" }
      }
    }
  }
}

eventType: message β€” list reply

{
  "eventType": "message",
  "data": {
    "message": {
      "type": "interactive",
      "interactive": {
        "type": "list_reply",
        "list_reply": { "id": "PROJ_12", "title": "Project Alpha", "description": "Phase 2" }
      }
    }
  }
}

eventType: message β€” Flow submission (nfm_reply)

{
  "eventType": "message",
  "data": {
    "message": {
      "from": "919876543210",
      "id": "wamid.HBgM...",
      "timestamp": "1719156600",
      "type": "interactive",
      "interactive": {
        "type": "nfm_reply",
        "nfm_reply": {
          "response_json": "{\"flow_token\":\"order_1744012345_abc12\",\"customer_name\":\"Rahul\",\"delivery_address\":\"12 MG Road\",\"photo_id\":{\"id\":\"META_MEDIA_ID\"}}",
          "body": "Sent",
          "name": "flow"
        }
      }
    }
  }
}

Parse response_json as JSON. Correlate with flow_token (same value you passed as flowToken on send, or the cart order’s flow_token). PhotoPicker/DocumentPicker fields often contain Meta media IDs only on the webhook β€” use GET /commerce/flows/analytics (attachments[]) or GET /orders/:id β†’ checkout_submission.attachments for ChatYug downloadUrls.

eventType: status β€” delivery receipt

{
  "eventType": "status",
  "eventId": "wamid.HBgM...",
  "data": {
    "status": {
      "id": "wamid.HBgM...",
      "status": "delivered",
      "timestamp": "1719156600",
      "recipient_id": "919876543210",
      "conversation": { "id": "...", "origin": { "type": "business_initiated" } }
    }
  }
}

Values: sent, delivered, read, failed. Alternative: poll GET /delivery-status/:messageId.

eventType: order β€” WhatsApp cart

{
  "eventType": "order",
  "data": {
    "metadata": { "phone_number_id": "1079034885291122" },
    "contacts": [{ "wa_id": "919876543210" }],
    "order": {
      "catalog_id": "980328057290841",
      "text": "Order note from customer",
      "product_items": [
        {
          "product_retailer_id": "SKU-001",
          "quantity": 2,
          "item_price": 599,
          "currency": "INR"
        }
      ]
    },
    "chatyug": {
      "order_id": 135,
      "order_number": "135",
      "items_count": 1,
      "total_amount": 251,
      "currency": "INR",
      "thumbnail_url": "https://cdn.example.com/amul-protein.jpg",
      "items": [
        {
          "product_retailer_id": "SKU-001",
          "product_name": "Amul Protein Water (250ML)",
          "quantity": 1,
          "item_price": 251,
          "currency": "INR",
          "image_url": "https://cdn.example.com/amul-protein.jpg"
        }
      ]
    }
  }
}

Raw Meta cart is in data.order. Additive data.chatyug includes ChatYug order_id / order_number, enriched product_name, image_url, totals, and thumbnail_url for native-like cart UI. Also creates a record queryable via GET /orders. Conversation history stores the same cart summary in Template_Variables with kind: "cart_order" (see GET /conversations/:id/messages).

eventType: test

Sent from Webhook Settings "Send test event". Use to validate URL, TLS, and signature handling.

Recommended webhook handler flow

  1. Read raw body β†’ verify X-Chatyug-Signature
  2. Parse JSON β†’ check eventId not already processed
  3. Route on eventType β†’ update your DB / trigger bot logic
  4. Return 200 OK within a few seconds

5. CRM Integration Guide

5.1 Common CRM Integration Scenarios

Scenario 1: Order Confirmation

When: Customer places an order in your e-commerce system

Action: Send template message with order details

// Pseudo-code example function onOrderPlaced(order) { sendWhatsAppMessage({ to: order.customerPhone, type: "template", templateName: "order_confirmation", templateVariables: [ order.customerName, order.orderNumber, order.totalAmount ] }); }

Scenario 2: Follow-up After Support Ticket

When: Support ticket is resolved

Action: Send text message asking for feedback

// Pseudo-code example function onTicketResolved(ticket) { if (isWithin24Hours(ticket.lastCustomerMessage)) { sendWhatsAppMessage({ to: ticket.customerPhone, type: "text", text: "Hi! Your support ticket #" + ticket.id + " has been resolved. " + "Please let us know if you need any further assistance." }); } }

Scenario 3: Payment Reminder

When: Invoice is due in 3 days

Action: Send template message with payment link

// Pseudo-code example function sendPaymentReminder(invoice) { sendWhatsAppMessage({ to: invoice.customerPhone, type: "template", templateName: "payment_reminder", templateVariables: [ invoice.customerName, invoice.invoiceNumber, invoice.amount, invoice.paymentLink ] }); }

5.2 Integration Architecture

Here's how to structure your CRM integration:

Layer 1: CRM Database/System ↓ (triggers event) Layer 2: Integration Service/Webhook Handler ↓ (calls API) Layer 3: Chatyug API ↓ (sends message) Layer 4: WhatsApp ↓ (delivers to customer) Layer 5: Customer's Phone

5.3 Best Practices for CRM Integration

1. Store API Keys Securely

2. Handle Errors Gracefully

3. Track Message Status

4. Respect Rate Limits

5. Personalize Messages

⚠️ Important:
Always test your integration in a development/staging environment before deploying to production. Test with your own phone number first to ensure everything works correctly.

6. Complete Code Examples

πŸ“š How to Use These Examples:
Each example below is a complete, working implementation. Copy the code, replace the API key and phone number IDs with your own values, and you're ready to go! All examples include detailed comments explaining each step.

6.1 Node.js / JavaScript Example

This example shows how to integrate Chatyug API in a Node.js application. Perfect for server-side applications, web backends, or Node.js-based systems.

Step 1: Install Required Package

# Install axios for making HTTP requests npm install axios

Step 2: Complete Implementation

// ============================================
// CHATYUG API INTEGRATION - NODE.JS EXAMPLE
// ============================================

// Step 1: Import required library
const axios = require('axios');

// Step 2: Configure API credentials
// Replace these with your actual values from Chatyug Portal
const API_BASE_URL = 'https://chatyug.com/api';
const API_KEY = 'ck_your_api_key_here';  // Get this from API Configuration page
const PHONE_NUMBER_ID = '719142414620783'; // Your WhatsApp Business Phone Number ID

// ============================================
// FUNCTION: Send WhatsApp Message
// ============================================
/**
 * Sends a WhatsApp message (text, template, or session media) to a recipient
 * @param {string} to - Recipient phone number (with country code, no + sign)
 * @param {string} messageType - 'text' | 'template' | 'image' | 'video' | 'audio' | 'document'
 * @param {object} content - Message content (text string or template object)
 * @returns {Promise<string>} - Message ID from WhatsApp
 */
async function sendWhatsAppMessage(to, messageType, content) {
    try {
        // Step 1: Prepare the request URL
        const url = `${API_BASE_URL}/external/send-message`;
        
        // Step 2: Build request body based on message type
        const requestBody = {
            phoneNumberId: PHONE_NUMBER_ID,  // Your WhatsApp phone number ID
            to: to,                           // Recipient phone number
            type: messageType                // 'text' | 'template' | 'image' | 'video' | 'audio' | 'document'
        };
        
        // Step 3: Add message-specific fields
        if (messageType === 'text') {
            // For text messages, add the text content
            requestBody.text = content;
        } else {
            // For template messages, add template details
            requestBody.templateName = content.templateName;
            requestBody.templateLanguage = content.templateLanguage;
            requestBody.templateVariables = content.templateVariables || [];
        }
        
        // Step 4: Make HTTP POST request with authentication
        const response = await axios.post(url, requestBody, {
            headers: {
                'Authorization': `Bearer ${API_KEY}`,  // API key authentication
                'Content-Type': 'application/json'        // JSON content type
            }
        });
        
        // Step 5: Check if request was successful
        if (response.data.success) {
            console.log('βœ… Message sent successfully!');
            console.log('Message ID:', response.data.data.messageId);
            console.log('Status:', response.data.data.status);
            
            // Return the message ID for tracking
            return response.data.data.messageId;
        } else {
            throw new Error('API returned success=false: ' + response.data.message);
        }
        
    } catch (error) {
        // Handle errors gracefully
        if (error.response) {
            // API returned an error response
            console.error('❌ API Error:', error.response.status);
            console.error('Error details:', error.response.data);
        } else if (error.request) {
            // Request was made but no response received
            console.error('❌ Network Error: No response from server');
        } else {
            // Something else happened
            console.error('❌ Error:', error.message);
        }
        throw error; // Re-throw to allow caller to handle
    }
}

// ============================================
// FUNCTION: Check Delivery Status
// ============================================
/**
 * Checks the delivery status of a previously sent message
 * @param {string} messageId - The message ID returned from sendWhatsAppMessage
 * @returns {Promise<object>} - Status information (status, deliveredAt, readAt, etc.)
 */
async function checkDeliveryStatus(messageId) {
    try {
        // Step 1: Prepare the request URL with message ID
        // Note: messageId must be URL-encoded for safety
        const url = `${API_BASE_URL}/external/delivery-status/${encodeURIComponent(messageId)}`;
        
        // Step 2: Make HTTP GET request with authentication
        const response = await axios.get(url, {
            headers: {
                'Authorization': `Bearer ${API_KEY}`
            }
        });
        
        // Step 3: Return the status data
        return response.data.data;
        
    } catch (error) {
        // Handle errors
        if (error.response && error.response.status === 404) {
            console.error('❌ Message not found. It may not have been saved yet.');
        } else {
            console.error('❌ Error checking status:', error.message);
        }
        throw error;
    }
}

// ============================================
// USAGE EXAMPLES
// ============================================

// Example 1: Send a Text Message
async function exampleSendTextMessage() {
    try {
        const messageId = await sendWhatsAppMessage(
            '919876543210',  // Recipient phone number (India: 91 + 9876543210)
            'text',          // Message type
            'Hello! This is a test message from my CRM system.'  // Message text
        );
        
        console.log('Text message sent with ID:', messageId);
        
        // Wait a few seconds for delivery, then check status
        setTimeout(async () => {
            const status = await checkDeliveryStatus(messageId);
            console.log('Delivery status:', status.status);
            console.log('Delivered at:', status.deliveredAt);
        }, 5000);
        
    } catch (error) {
        console.error('Failed to send text message:', error.message);
    }
}

// Example 2: Send a Template Message
async function exampleSendTemplateMessage() {
    try {
        const messageId = await sendWhatsAppMessage(
            '919876543210',  // Recipient phone number
            'template',      // Message type
            {
                templateName: 'order_confirmation',     // Your approved template name
                templateLanguage: 'en',                // Template language code
                templateVariables: [                   // Variables to fill in template
                    'John Doe',      // Customer name
                    'ORD-12345',     // Order number
                    'β‚Ή1,299'         // Order amount
                ]
            }
        );
        
        console.log('Template message sent with ID:', messageId);
        
        // Check status after a delay
        setTimeout(async () => {
            const status = await checkDeliveryStatus(messageId);
            console.log('Message Status:', status.status);
            if (status.readAt) {
                console.log('Message was read at:', status.readAt);
            }
        }, 10000);
        
    } catch (error) {
        console.error('Failed to send template message:', error.message);
    }
}

// Example 3: Send Message from CRM Event (Order Placed)
async function onOrderPlaced(order) {
    try {
        // Send order confirmation via WhatsApp
        const messageId = await sendWhatsAppMessage(
            order.customerPhone.replace(/[^0-9]/g, ''),  // Remove any formatting
            'template',
            {
                templateName: 'order_confirmation',
                templateLanguage: 'en',
                templateVariables: [
                    order.customerName,
                    order.orderNumber,
                    order.totalAmount
                ]
            }
        );
        
        // Store message ID in your database for tracking
        console.log(`Order confirmation sent to ${order.customerName}: ${messageId}`);
        
        return messageId;
    } catch (error) {
        console.error('Failed to send order confirmation:', error);
        // Log error but don't fail the order process
    }
}

// Run example (uncomment to test)
// exampleSendTextMessage();
// exampleSendTemplateMessage();
πŸ’‘ Pro Tips:
β€’ Store your API key in environment variables: process.env.CHATYUG_API_KEY
β€’ Always handle errors gracefully - WhatsApp delivery can fail for various reasons
β€’ Store message IDs in your database to track delivery status later
β€’ Use template messages for automated notifications (order confirmations, etc.)
β€’ Use text messages for customer support conversations

6.2 PHP Example

This example shows how to integrate Chatyug API in a PHP application. Perfect for WordPress plugins, Laravel applications, or any PHP-based system.

Step 1: Complete Implementation

<?php
// ============================================
// CHATYUG API INTEGRATION - PHP EXAMPLE
// ============================================

// Step 1: Configure API credentials
$apiBaseUrl = 'https://chatyug.com/api';
$apiKey = 'ck_your_api_key_here';
$phoneNumberId = '719142414620783';

// ============================================
// FUNCTION: Send WhatsApp Message
// ============================================
function sendWhatsAppMessage($to, $messageType, $content) {
    global $apiBaseUrl, $apiKey, $phoneNumberId;
    
    $url = $apiBaseUrl . '/external/send-message';
    $data = [
        'phoneNumberId' => $phoneNumberId,
        'to' => $to,
        'type' => $messageType
    ];
    
    if ($messageType === 'text') {
        $data['text'] = $content;
    } else {
        $data['templateName'] = $content['templateName'];
        $data['templateLanguage'] = $content['templateLanguage'];
        $data['templateVariables'] = $content['templateVariables'] ?? [];
    }
    
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json'
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($httpCode === 200) {
        $result = json_decode($response, true);
        return $result['data']['messageId'];
    } else {
        throw new Exception('Failed: ' . $response);
    }
}

// Step 3: Check Delivery Status Function
function checkDeliveryStatus($messageId) {
    global $apiBaseUrl, $apiKey;
    
    $url = $apiBaseUrl . '/external/delivery-status/' . urlencode($messageId);
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Authorization: Bearer ' . $apiKey
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($httpCode === 200) {
        return json_decode($response, true)['data'];
    } else {
        throw new Exception('Failed: ' . $response);
    }
}
?>

Step 2: Usage Examples

<?php
// Example 1: Send Text Message
try {
    $messageId = sendWhatsAppMessage(
        '919876543210',
        'text',
        'Hello! This is a test message.'
    );
    echo "Message sent: " . $messageId . "\n";
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

// Example 2: Send Template Message
try {
    $messageId = sendWhatsAppMessage(
        '919876543210',
        'template',
        [
            'templateName' => 'order_confirmation',
            'templateLanguage' => 'en',
            'templateVariables' => ['John Doe', 'ORD-12345', 'β‚Ή1,299']
        ]
    );
    echo "Message sent: " . $messageId . "\n";
    
    sleep(5);
    $status = checkDeliveryStatus($messageId);
    echo "Status: " . $status['status'] . "\n";
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}
?>

6.3 Python Example

This example shows how to integrate Chatyug API in a Python application. Perfect for Django, Flask, or any Python-based system.

Step 1: Install Required Package

# Install requests library pip install requests

Step 2: Complete Implementation

# ============================================
# CHATYUG API INTEGRATION - PYTHON EXAMPLE
# ============================================

import requests
import time

# Step 1: Configure API credentials
API_BASE_URL = 'https://chatyug.com/api'
API_KEY = 'ck_your_api_key_here'
PHONE_NUMBER_ID = '719142414620783'

# Step 2: Send WhatsApp Message Function
def send_whatsapp_message(to, message_type, content):
    """
    Sends a WhatsApp message (text or template) to a recipient
    Args:
        to: Recipient phone number (with country code)
        message_type: 'text' or 'template'
        content: Message content (text string or template dict)
    Returns:
        str: Message ID from WhatsApp
    """
    url = f"{API_BASE_URL}/external/send-message"
    
    headers = {
        'Authorization': f'Bearer {API_KEY}',
        'Content-Type': 'application/json'
    }
    
    data = {
        'phoneNumberId': PHONE_NUMBER_ID,
        'to': to,
        'type': message_type
    }
    
    if message_type == 'text':
        data['text'] = content
    else:
        data['templateName'] = content['templateName']
        data['templateLanguage'] = content['templateLanguage']
        data['templateVariables'] = content.get('templateVariables', [])
    
    response = requests.post(url, json=data, headers=headers)
    response.raise_for_status()
    
    result = response.json()
    if result.get('success'):
        print(f"βœ… Message sent! ID: {result['data']['messageId']}")
        return result['data']['messageId']
    else:
        raise Exception(f"API error: {result.get('message', 'Unknown error')}")

# Step 3: Check Delivery Status Function
def check_delivery_status(message_id):
    """
    Checks the delivery status of a previously sent message
    Args:
        message_id: The message ID returned from send_whatsapp_message
    Returns:
        dict: Status information
    """
    url = f"{API_BASE_URL}/external/delivery-status/{requests.utils.quote(message_id)}"
    
    headers = {
        'Authorization': f'Bearer {API_KEY}'
    }
    
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    
    result = response.json()
    return result['data']

# Step 4: Usage Examples
if __name__ == '__main__':
    # Example 1: Send Text Message
    try:
        message_id = send_whatsapp_message(
            '919876543210',
            'text',
            'Hello! This is a test message from Python.'
        )
        print(f"Text message sent: {message_id}")
    except Exception as e:
        print(f"❌ Error: {e}")
    
    # Example 2: Send Template Message
    try:
        message_id = send_whatsapp_message(
            '919876543210',
            'template',
            {
                'templateName': 'order_confirmation',
                'templateLanguage': 'en',
                'templateVariables': ['John Doe', 'ORD-12345', 'β‚Ή1,299']
            }
        )
        print(f"Template message sent: {message_id}")
        
        time.sleep(5)
        status = check_delivery_status(message_id)
        print(f"Status: {status['status']}")
    except Exception as e:
        print(f"❌ Error: {e}")

6.4 C# / .NET Example

This example shows how to integrate Chatyug API in a C# / .NET application.

Step 1: Complete Implementation

using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

// ============================================
// CHATYUG API INTEGRATION - C# EXAMPLE
// ============================================

public class ChatyugAPI
{
    // Step 1: Define API configuration
    private readonly string apiBaseUrl = "https://chatyug.com/api";
    private readonly string apiKey = "ck_your_api_key_here";
    private readonly string phoneNumberId = "719142414620783";
    private readonly HttpClient httpClient;

    // Step 2: Initialize HttpClient with authentication
    public ChatyugAPI()
    {
        httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");
    }

    // ============================================
    // METHOD: Send WhatsApp Message
    // ============================================
    public async Task<string> SendWhatsAppMessage(string to, string messageType, object content)
    {
        // Step 1: Build the API endpoint URL
        var url = $"{apiBaseUrl}/external/send-message";
        
        // Step 2: Create request payload
        var data = new
        {
            phoneNumberId = phoneNumberId,
            to = to,
            type = messageType,
            text = messageType == "text" ? content : null,
            templateName = messageType == "template" ? ((dynamic)content).templateName : null,
            templateLanguage = messageType == "template" ? ((dynamic)content).templateLanguage : null,
            templateVariables = messageType == "template" ? ((dynamic)content).templateVariables : null
        };

        // Step 3: Serialize to JSON and create HTTP content
        var json = JsonSerializer.Serialize(data);
        var httpContent = new StringContent(json, Encoding.UTF8, "application/json");

        // Step 4: Send POST request
        var response = await httpClient.PostAsync(url, httpContent);
        response.EnsureSuccessStatusCode();

        // Step 5: Parse response and extract message ID
        var responseBody = await response.Content.ReadAsStringAsync();
        var result = JsonSerializer.Deserialize<JsonElement>(responseBody);
        
        return result.GetProperty("data").GetProperty("messageId").GetString();
    }

    // ============================================
    // METHOD: Check Delivery Status
    // ============================================
    public async Task<object> CheckDeliveryStatus(string messageId)
    {
        // Step 1: Build the API endpoint URL with message ID
        var url = $"{apiBaseUrl}/external/delivery-status/{Uri.EscapeDataString(messageId)}";
        
        // Step 2: Send GET request
        var response = await httpClient.GetAsync(url);
        response.EnsureSuccessStatusCode();

        // Step 3: Parse response
        var responseBody = await response.Content.ReadAsStringAsync();
        var result = JsonSerializer.Deserialize<JsonElement>(responseBody);
        
        return result.GetProperty("data");
    }
}

// ============================================
// USAGE EXAMPLE
// ============================================
class Program
{
    static async Task Main(string[] args)
    {
        // Step 1: Create API instance
        var api = new ChatyugAPI();
        
        try
        {
            // Step 2: Send a template message
            var messageId = await api.SendWhatsAppMessage(
                "919876543210",  // Recipient phone number
                "template",      // Message type
                new              // Template content
                {
                    templateName = "order_confirmation",
                    templateLanguage = "en",
                    templateVariables = new[] { "John Doe", "ORD-12345", "β‚Ή1,299" }
                }
            );
            
            Console.WriteLine($"βœ… Message sent: {messageId}");
            
            // Step 3: Wait a few seconds for delivery
            await Task.Delay(5000);
            
            // Step 4: Check delivery status
            var status = await api.CheckDeliveryStatus(messageId);
            Console.WriteLine($"πŸ“Š Status: {status}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"❌ Error: {ex.Message}");
        }
    }
}

6.5 Dart / Flutter Example

This example shows how to integrate Chatyug API in a Flutter/Dart mobile application.

Step 1: Add Required Package

# Add to pubspec.yaml
dependencies:
  http: ^1.1.0
  dart:convert

Step 2: Complete Implementation

// ============================================
// CHATYUG API INTEGRATION - DART/FLUTTER EXAMPLE
// ============================================

import 'dart:convert';
import 'package:http/http.dart' as http;

class ChatyugAPI {
  // Step 1: Configure API credentials
  static const String apiBaseUrl = 'https://chatyug.com/api';
  final String apiKey;
  final String phoneNumberId;
  
  ChatyugAPI({
    required this.apiKey,
    required this.phoneNumberId,
  });

  // ============================================
  // METHOD: Send WhatsApp Message
  // ============================================
  /// Sends a WhatsApp message (text or template) to a recipient
  /// Returns the message ID from WhatsApp
  Future<String> sendWhatsAppMessage({
    required String to,
    required String messageType,
    required dynamic content,
  }) async {
    try {
      // Step 1: Build the API endpoint URL
      final url = Uri.parse('$apiBaseUrl/external/send-message');
      
      // Step 2: Create request payload
      final Map<String, dynamic> data = {
        'phoneNumberId': phoneNumberId,
        'to': to,
        'type': messageType,
      };
      
      // Step 3: Add message-specific fields
      if (messageType == 'text') {
        data['text'] = content;
      } else {
        data['templateName'] = content['templateName'];
        data['templateLanguage'] = content['templateLanguage'];
        data['templateVariables'] = content['templateVariables'] ?? [];
      }
      
      // Step 4: Send POST request with authentication
      final response = await http.post(
        url,
        headers: {
          'Authorization': 'Bearer $apiKey',
          'Content-Type': 'application/json',
        },
        body: jsonEncode(data),
      );
      
      // Step 5: Check response status
      if (response.statusCode == 200) {
        final result = jsonDecode(response.body);
        if (result['success'] == true) {
          print('βœ… Message sent successfully!');
          print('Message ID: ${result['data']['messageId']}');
          return result['data']['messageId'];
        } else {
          throw Exception('API error: ${result['message']}');
        }
      } else {
        throw Exception('HTTP ${response.statusCode}: ${response.body}');
      }
    } catch (e) {
      print('❌ Error sending message: $e');
      rethrow;
    }
  }

  // ============================================
  // METHOD: Check Delivery Status
  // ============================================
  /// Checks the delivery status of a previously sent message
  Future<Map<String, dynamic>> checkDeliveryStatus(String messageId) async {
    try {
      // Step 1: Build the API endpoint URL with message ID
      final encodedMessageId = Uri.encodeComponent(messageId);
      final url = Uri.parse('$apiBaseUrl/external/delivery-status/$encodedMessageId');
      
      // Step 2: Send GET request with authentication
      final response = await http.get(
        url,
        headers: {
          'Authorization': 'Bearer $apiKey',
        },
      );
      
      // Step 3: Parse and return response
      if (response.statusCode == 200) {
        final result = jsonDecode(response.body);
        return result['data'];
      } else {
        throw Exception('HTTP ${response.statusCode}: ${response.body}');
      }
    } catch (e) {
      print('❌ Error checking status: $e');
      rethrow;
    }
  }
}

// ============================================
// USAGE EXAMPLE
// ============================================
void main() async {
  final api = ChatyugAPI(
    apiKey: 'ck_your_api_key_here',
    phoneNumberId: '719142414620783',
  );
  
  try {
    // Send a template message
    final messageId = await api.sendWhatsAppMessage(
      to: '919876543210',
      messageType: 'template',
      content: {
        'templateName': 'order_confirmation',
        'templateLanguage': 'en',
        'templateVariables': ['John Doe', 'ORD-12345', 'β‚Ή1,299'],
      },
    );
    
    print('βœ… Message sent: $messageId');
    
    // Wait a few seconds, then check status
    await Future.delayed(Duration(seconds: 5));
    final status = await api.checkDeliveryStatus(messageId);
    print('πŸ“Š Status: ${status['status']}');
  } catch (e) {
    print('❌ Error: $e');
  }
}

6.6 Android - Kotlin Example

This example shows how to integrate Chatyug API in an Android application using Kotlin.

Step 1: Add Required Dependencies

// Add to build.gradle (Module: app)
dependencies {
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation 'com.google.code.gson:gson:2.10.1'
}

Step 2: Complete Implementation

// ============================================
// CHATYUG API INTEGRATION - KOTLIN EXAMPLE
// ============================================

import com.google.gson.Gson
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.IOException

class ChatyugAPI(
    private val apiKey: String,
    private val phoneNumberId: String
) {
    // Step 1: Configure API base URL
    private val apiBaseUrl = "https://chatyug.com/api"
    private val client = OkHttpClient()
    private val gson = Gson()
    private val jsonMediaType = "application/json; charset=utf-8".toMediaType()

    // ============================================
    // METHOD: Send WhatsApp Message
    // ============================================
    /**
     * Sends a WhatsApp message (text or template) to a recipient
     * @param to Recipient phone number
     * @param messageType "text" or "template"
     * @param content Message content (String for text, Map for template)
     * @return Message ID from WhatsApp
     */
    suspend fun sendWhatsAppMessage(
        to: String,
        messageType: String,
        content: Any
    ): String {
        try {
            // Step 1: Build the API endpoint URL
            val url = "$apiBaseUrl/external/send-message"
            
            // Step 2: Create request payload
            val data = mutableMapOf<String, Any>(
                "phoneNumberId" to phoneNumberId,
                "to" to to,
                "type" to messageType
            )
            
            // Step 3: Add message-specific fields
            if (messageType == "text") {
                data["text"] = content as String
            } else {
                val templateContent = content as Map<String, Any>
                data["templateName"] = templateContent["templateName"] as String
                data["templateLanguage"] = templateContent["templateLanguage"] as String
                data["templateVariables"] = templateContent["templateVariables"] ?: emptyList<String>()
            }
            
            // Step 4: Create request body
            val jsonBody = gson.toJson(data)
            val requestBody = jsonBody.toRequestBody(jsonMediaType)
            
            // Step 5: Build request with authentication
            val request = Request.Builder()
                .url(url)
                .post(requestBody)
                .addHeader("Authorization", "Bearer $apiKey")
                .addHeader("Content-Type", "application/json")
                .build()
            
            // Step 6: Execute request
            client.newCall(request).execute().use { response ->
                if (response.isSuccessful) {
                    val result = gson.fromJson(response.body?.string(), Map::class.java)
                    if (result["success"] == true) {
                        val data = result["data"] as Map<*, *>
                        println("βœ… Message sent: ${data["messageId"]}")
                        return data["messageId"] as String
                    } else {
                        throw IOException("API error: ${result["message"]}")
                    }
                } else {
                    throw IOException("HTTP ${response.code}: ${response.message}")
                }
            }
        } catch (e: Exception) {
            println("❌ Error sending message: ${e.message}")
            throw e
        }
    }

    // ============================================
    // METHOD: Check Delivery Status
    // ============================================
    /**
     * Checks the delivery status of a previously sent message
     * @param messageId The message ID returned from sendWhatsAppMessage
     * @return Status information map
     */
    suspend fun checkDeliveryStatus(messageId: String): Map<String, Any> {
        try {
            // Step 1: Build the API endpoint URL with message ID
            val encodedMessageId = java.net.URLEncoder.encode(messageId, "UTF-8")
            val url = "$apiBaseUrl/external/delivery-status/$encodedMessageId"
            
            // Step 2: Build request with authentication
            val request = Request.Builder()
                .url(url)
                .get()
                .addHeader("Authorization", "Bearer $apiKey")
                .build()
            
            // Step 3: Execute request
            client.newCall(request).execute().use { response ->
                if (response.isSuccessful) {
                    val result = gson.fromJson(response.body?.string(), Map::class.java)
                    return result["data"] as Map<String, Any>
                } else {
                    throw IOException("HTTP ${response.code}: ${response.message}")
                }
            }
        } catch (e: Exception) {
            println("❌ Error checking status: ${e.message}")
            throw e
        }
    }
}

// ============================================
// USAGE EXAMPLE (in a CoroutineScope)
// ============================================
/*
import kotlinx.coroutines.*

fun main() = runBlocking {
    val api = ChatyugAPI(
        apiKey = "ck_your_api_key_here",
        phoneNumberId = "719142414620783"
    )
    
    try {
        // Send a template message
        val messageId = api.sendWhatsAppMessage(
            to = "919876543210",
            messageType = "template",
            content = mapOf(
                "templateName" to "order_confirmation",
                "templateLanguage" to "en",
                "templateVariables" to listOf("John Doe", "ORD-12345", "β‚Ή1,299")
            )
        )
        
        println("βœ… Message sent: $messageId")
        
        // Wait a few seconds, then check status
        delay(5000)
        val status = api.checkDeliveryStatus(messageId)
        println("πŸ“Š Status: ${status["status"]}")
    } catch (e: Exception) {
        println("❌ Error: ${e.message}")
    }
}
*/

6.7 Android - Java Example

This example shows how to integrate Chatyug API in an Android application using Java.

Step 1: Add Required Dependencies

// Add to build.gradle (Module: app)
dependencies {
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    implementation 'com.google.code.gson:gson:2.10.1'
}

Step 2: Complete Implementation

// ============================================
// CHATYUG API INTEGRATION - JAVA EXAMPLE
// ============================================

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import okhttp3.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class ChatyugAPI {
    // Step 1: Configure API credentials
    private final String apiBaseUrl = "https://chatyug.com/api";
    private final String apiKey;
    private final String phoneNumberId;
    private final OkHttpClient client;
    private final Gson gson;
    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    public ChatyugAPI(String apiKey, String phoneNumberId) {
        this.apiKey = apiKey;
        this.phoneNumberId = phoneNumberId;
        this.client = new OkHttpClient();
        this.gson = new Gson();
    }

    // ============================================
    // METHOD: Send WhatsApp Message
    // ============================================
    /**
     * Sends a WhatsApp message (text or template) to a recipient
     * @param to Recipient phone number
     * @param messageType "text" or "template"
     * @param content Message content (String for text, Map for template)
     * @return Message ID from WhatsApp
     */
    public String sendWhatsAppMessage(String to, String messageType, Object content) throws IOException {
        try {
            // Step 1: Build the API endpoint URL
            String url = apiBaseUrl + "/external/send-message";
            
            // Step 2: Create request payload
            Map<String, Object> data = new HashMap<>();
            data.put("phoneNumberId", phoneNumberId);
            data.put("to", to);
            data.put("type", messageType);
            
            // Step 3: Add message-specific fields
            if ("text".equals(messageType)) {
                data.put("text", content);
            } else {
                @SuppressWarnings("unchecked")
                Map<String, Object> templateContent = (Map<String, Object>) content;
                data.put("templateName", templateContent.get("templateName"));
                data.put("templateLanguage", templateContent.get("templateLanguage"));
                data.put("templateVariables", templateContent.getOrDefault("templateVariables", new java.util.ArrayList<>()));
            }
            
            // Step 4: Create request body
            String jsonBody = gson.toJson(data);
            RequestBody requestBody = RequestBody.create(jsonBody, JSON);
            
            // Step 5: Build request with authentication
            Request request = new Request.Builder()
                    .url(url)
                    .post(requestBody)
                    .addHeader("Authorization", "Bearer " + apiKey)
                    .addHeader("Content-Type", "application/json")
                    .build();
            
            // Step 6: Execute request
            try (Response response = client.newCall(request).execute()) {
                if (response.isSuccessful()) {
                    String responseBody = response.body().string();
                    JsonObject result = gson.fromJson(responseBody, JsonObject.class);
                    
                    if (result.get("success").getAsBoolean()) {
                        JsonObject dataObj = result.getAsJsonObject("data");
                        String messageId = dataObj.get("messageId").getAsString();
                        System.out.println("βœ… Message sent: " + messageId);
                        return messageId;
                    } else {
                        throw new IOException("API error: " + result.get("message").getAsString());
                    }
                } else {
                    throw new IOException("HTTP " + response.code() + ": " + response.message());
                }
            }
        } catch (Exception e) {
            System.err.println("❌ Error sending message: " + e.getMessage());
            throw e;
        }
    }

    // ============================================
    // METHOD: Check Delivery Status
    // ============================================
    /**
     * Checks the delivery status of a previously sent message
     * @param messageId The message ID returned from sendWhatsAppMessage
     * @return Status information as JsonObject
     */
    public JsonObject checkDeliveryStatus(String messageId) throws IOException {
        try {
            // Step 1: Build the API endpoint URL with message ID
            String encodedMessageId = java.net.URLEncoder.encode(messageId, "UTF-8");
            String url = apiBaseUrl + "/external/delivery-status/" + encodedMessageId;
            
            // Step 2: Build request with authentication
            Request request = new Request.Builder()
                    .url(url)
                    .get()
                    .addHeader("Authorization", "Bearer " + apiKey)
                    .build();
            
            // Step 3: Execute request
            try (Response response = client.newCall(request).execute()) {
                if (response.isSuccessful()) {
                    String responseBody = response.body().string();
                    JsonObject result = gson.fromJson(responseBody, JsonObject.class);
                    return result.getAsJsonObject("data");
                } else {
                    throw new IOException("HTTP " + response.code() + ": " + response.message());
                }
            }
        } catch (Exception e) {
            System.err.println("❌ Error checking status: " + e.getMessage());
            throw e;
        }
    }
}

// ============================================
// USAGE EXAMPLE
// ============================================
/*
public class Main {
    public static void main(String[] args) {
        ChatyugAPI api = new ChatyugAPI(
            "ck_your_api_key_here",
            "719142414620783"
        );
        
        try {
            // Prepare template content
            Map<String, Object> templateContent = new HashMap<>();
            templateContent.put("templateName", "order_confirmation");
            templateContent.put("templateLanguage", "en");
            templateContent.put("templateVariables", java.util.Arrays.asList(
                "John Doe", "ORD-12345", "β‚Ή1,299"
            ));
            
            // Send a template message
            String messageId = api.sendWhatsAppMessage(
                "919876543210",
                "template",
                templateContent
            );
            
            System.out.println("βœ… Message sent: " + messageId);
            
            // Wait a few seconds, then check status
            Thread.sleep(5000);
            JsonObject status = api.checkDeliveryStatus(messageId);
            System.out.println("πŸ“Š Status: " + status.get("status").getAsString());
        } catch (Exception e) {
            System.err.println("❌ Error: " + e.getMessage());
        }
    }
}
*/

6.8 iOS - Swift Example

This example shows how to integrate Chatyug API in an iOS application using Swift.

Step 1: No External Dependencies Required

Swift's built-in URLSession is sufficient for making HTTP requests.

Step 2: Complete Implementation

// ============================================
// CHATYUG API INTEGRATION - SWIFT EXAMPLE
// ============================================

import Foundation

class ChatyugAPI {
    // Step 1: Configure API credentials
    private let apiBaseUrl = "https://chatyug.com/api"
    private let apiKey: String
    private let phoneNumberId: String
    
    init(apiKey: String, phoneNumberId: String) {
        self.apiKey = apiKey
        self.phoneNumberId = phoneNumberId
    }
    
    // ============================================
    // METHOD: Send WhatsApp Message
    // ============================================
    /// Sends a WhatsApp message (text or template) to a recipient
    /// - Parameters:
    ///   - to: Recipient phone number
    ///   - messageType: "text" or "template"
    ///   - content: Message content (String for text, Dictionary for template)
    /// - Returns: Message ID from WhatsApp
    func sendWhatsAppMessage(
        to: String,
        messageType: String,
        content: Any
    ) async throws -> String {
        // Step 1: Build the API endpoint URL
        guard let url = URL(string: "(apiBaseUrl)/external/send-message") else {
            throw NSError(domain: "Invalid URL", code: -1)
        }
        
        // Step 2: Create request payload
        var data: [String: Any] = [
            "phoneNumberId": phoneNumberId,
            "to": to,
            "type": messageType
        ]
        
        // Step 3: Add message-specific fields
        if messageType == "text" {
            data["text"] = content as? String
        } else if let templateContent = content as? [String: Any] {
            data["templateName"] = templateContent["templateName"]
            data["templateLanguage"] = templateContent["templateLanguage"]
            data["templateVariables"] = templateContent["templateVariables"] ?? []
        }
        
        // Step 4: Convert to JSON
        let jsonData = try JSONSerialization.data(withJSONObject: data)
        
        // Step 5: Create request with authentication
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("Bearer (apiKey)", forHTTPHeaderField: "Authorization")
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = jsonData
        
        // Step 6: Execute request
        let (responseData, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw NSError(domain: "Invalid response", code: -1)
        }
        
        if httpResponse.statusCode == 200 {
            if let result = try JSONSerialization.jsonObject(with: responseData) as? [String: Any],
               let success = result["success"] as? Bool, success == true,
               let data = result["data"] as? [String: Any],
               let messageId = data["messageId"] as? String {
                print("βœ… Message sent: (messageId)")
                return messageId
            } else {
                throw NSError(domain: "API error", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response format"])
            }
        } else {
            throw NSError(domain: "HTTP Error", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: "HTTP (httpResponse.statusCode)"])
        }
    }
    
    // ============================================
    // METHOD: Check Delivery Status
    // ============================================
    /// Checks the delivery status of a previously sent message
    /// - Parameter messageId: The message ID returned from sendWhatsAppMessage
    /// - Returns: Status information as Dictionary
    func checkDeliveryStatus(messageId: String) async throws -> [String: Any] {
        // Step 1: Build the API endpoint URL with message ID
        guard let encodedMessageId = messageId.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed),
              let url = URL(string: "(apiBaseUrl)/external/delivery-status/(encodedMessageId)") else {
            throw NSError(domain: "Invalid URL", code: -1)
        }
        
        // Step 2: Create request with authentication
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue("Bearer (apiKey)", forHTTPHeaderField: "Authorization")
        
        // Step 3: Execute request
        let (responseData, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw NSError(domain: "Invalid response", code: -1)
        }
        
        if httpResponse.statusCode == 200 {
            if let result = try JSONSerialization.jsonObject(with: responseData) as? [String: Any],
               let data = result["data"] as? [String: Any] {
                return data
            } else {
                throw NSError(domain: "Invalid response format", code: -1)
            }
        } else {
            throw NSError(domain: "HTTP Error", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: "HTTP (httpResponse.statusCode)"])
        }
    }
}

// ============================================
// USAGE EXAMPLE
// ============================================
/*
Task {
    let api = ChatyugAPI(
        apiKey: "ck_your_api_key_here",
        phoneNumberId: "719142414620783"
    )
    
    do {
        // Send a template message
        let messageId = try await api.sendWhatsAppMessage(
            to: "919876543210",
            messageType: "template",
            content: [
                "templateName": "order_confirmation",
                "templateLanguage": "en",
                "templateVariables": ["John Doe", "ORD-12345", "β‚Ή1,299"]
            ]
        )
        
        print("βœ… Message sent: (messageId)")
        
        // Wait a few seconds, then check status
        try await Task.sleep(nanoseconds: 5_000_000_000) // 5 seconds
        let status = try await api.checkDeliveryStatus(messageId: messageId)
        print("πŸ“Š Status: (status["status"] ?? "unknown")")
    } catch {
        print("❌ Error: (error.localizedDescription)")
    }
}
*/

7. Best Practices & Security

7.1 Security Best Practices

7.2 Error Handling

Always implement proper error handling in your integration:

// Example error handling pattern try { const result = await sendMessage(...); // Handle success } catch (error) { if (error.response) { // API returned an error response console.error('API Error:', error.response.status, error.response.data); } else if (error.request) { // Request was made but no response received console.error('Network Error:', error.message); } else { // Something else happened console.error('Error:', error.message); } // Implement retry logic or notify admin }

7.3 Rate Limiting

7.4 Message Content Guidelines

8. Troubleshooting Guide

8.1 Common Issues and Solutions

Issue: "401 Unauthorized" Error

Cause: Invalid or missing API key

Solution:

Issue: "404 Message not found" when checking status

Cause: Message wasn't saved to database or messageId is incorrect

Solution:

Issue: "Template not found" Error

Cause: Template name is incorrect or template not approved

Solution:

Issue: Message sent but not delivered

Possible Causes:

Solution: Check delivery status endpoint for error details

8.2 Diagnostic Endpoint

Use the diagnostic endpoint to troubleshoot issues:

GET https://chatyug.com/api/external/diagnostic?messageId=wamid.XXX&wabaId=XXX&checkWebhooks=true Authorization: Bearer YOUR_API_KEY

This endpoint provides:

8.3 Getting Help

If you're still experiencing issues:

πŸ“ž Support & Resources

API Base URL: https://chatyug.com/api

Documentation Version: 1.3

Last Updated: September 28, 2026

πŸ’‘ Need More Help?
For additional support, feature requests, or to report bugs, please contact the Chatyug support team through your account portal or email support.

9. Third-Party Integrator FAQ

Answers to common questions from commerce/chat integrators (updated June 2026, API v1.6).

9.1 Full webhook payload samples?

See Β§4.5 Outbound Webhooks for field-level JSON for message (text, media, button, list, nfm_reply), status, and order.

9.2 Webhook signature β€” what is signed?

HMAC-SHA256 of the exact raw POST body (UTF-8), secret from Webhook Settings, header value sha256=<hex>. No timestamp/replay header β€” dedupe on eventId.

9.3 Can we register webhooks via API?

No β€” portal only (API Configuration β†’ Webhook Settings). Programmatic registration is not available in v1.5.

9.4 Flow form β€” API or portal first?

POST /commerce/flows can create flows with flowJson (screens/fields). Complex forms may still be easier in the ChatYug/Meta flow builder; then publish and send via POST /messages/flow. Responses always arrive as webhook nfm_reply.

9.5 List standard templates via API?

Yes. GET /api/external/templates?status=APPROVED returns template names, languages, body text, and variables with placeholders. GET /templates/:templateId returns full detail and a sendMessageExample. Carousel templates: GET /templates/carousel.

9.6 Rate limits and errors for new endpoints?

AreaGuidance
Outbound webhooks~300 events/min per WABA
External APISubject to Meta WhatsApp throughput; avoid burst > 80 msg/sec per number
Interactive / flow send400 validation, 403 phone/WABA, 502 Meta rejection (e.g. session closed)
Orders / catalog400 invalid status/fields, 404 not found, 502 Meta catalog errors

9.7 Media URL lifetime?

/received_media/... paths are persisted on ChatYug. Meta-hosted media IDs expire β€” download and store in your system when processing webhooks or immediately after GET /messages/:id/media.

9.8 One WABA for multi-distributor?

Yes. One API key = one WABA (one or more phone numbers). Route distributors in your application using customer phone + your pincode/selection logic; ChatYug does not split by distributor.

9.9 Session window expiry timestamp?

Yes. GET /conversations and GET /conversations/:id/messages return session.sessionOpen, lastCustomerMessageAt, and expiresAt (last inbound + 24 hours). Use to auto-switch between text/interactive and templates.

9.10 Mark-as-read / typing indicators?

Not available in External API v1.5. Portal chat UI supports read state internally; API exposure is planned as a future enhancement (cosmetic for integrators).

9.11 Feature β†’ API quick map (commerce journey)

StepAPI
Keyword / inboundWebhook message or Chatbot Flows
Catalogue welcomesend-message, messages/product-list, messages/carousel
Pincode / locationsend-message + inbound webhook
Distributor / project pickersPOST /messages/interactive
Customer details formPOST /messages/flow β†’ nfm_reply
Order notificationsPUT /orders/:id/status or send-message template
Chat consoleGET /conversations, GET .../messages, webhooks
Hybrid architecture (recommended): Keep your portal/DB as source of truth for orders, distributors, pincode routing, and COD. Use ChatYug for WhatsApp transport β€” webhooks in, send-message / interactive / flow out. Optional: ChatYug Catalog/Orders/Chatbot APIs as accelerators, not system of record.

10. Known Gaps & Roadmap

ItemStatusWorkaround
Mark-as-read / typing via API❌ Not in v1.6Portal only
Webhook registration API❌ Portal onlyManual setup in Webhook Settings
Bulk carousel via API❌ Portal onlyUse portal bulk messaging
Register non-carousel templates via API❌ Portal onlyPortal template manager; list/send via API