Skip to main content

Messages

Send Message​

Send text messages to customers via WhatsApp, Instagram, or Messenger.

POST /api/v1/public/messages/send

Parameters​

ParameterTypeRequiredDescription
phone_numberstringnoCustomer phone number. Use this to lookup customer for any channel.
customer_idstringnoCustomer ID (CUID). Alternative to phone_number.
instagram_usernamestringnoInstagram username. Alternative lookup method.
channelstringyes"whatsapp", "instagram", or "messenger"
message_typestringyesWhatsApp: "text", "image", "document", "audio", "video", "template". Instagram: "text", "image", "media_share". Messenger: "text", "image", "video", "audio", "file".
contentstringnoText message content (required for text messages)
media_urlstringnoMedia URL for image/video/audio/file messages
reply_to_message_idstringnoWhatsApp only. Quote/reply a previous message — pass the inbound message's wamid (data.message_id or data.raw.context.id from the webhook). The original message renders as a quoted reply above yours.
whatsapp_phone_number_idstringnoWhatsApp phone number ID. Use to specify which business phone number sends the message. If not provided, auto-resolved from customer linkage or primary number.
instagram_account_idstringnoInstagram account ID. Required when customer has conversations with multiple Instagram accounts.
facebook_page_idstringnoFacebook Page ID. Required when customer has conversations with multiple Facebook Pages.
info

Note: At least one of phone_number, customer_id, or instagram_username is required to identify the customer.

warning

Multi-Account / Multi-Number: For WhatsApp, use whatsapp_phone_number_id to specify which business phone number sends the message. If omitted, the API auto-resolves from the customer's linked number or your primary number. For Instagram/Messenger, specify instagram_account_id or facebook_page_id when a customer has multiple conversations.

Example Request​

curl -X POST "https://chat.api.co.id/api/v1/public/messages/send" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "628123456789",
"channel": "whatsapp",
"message_type": "text",
"content": "Hello! This is a test message.",
"whatsapp_phone_number_id": "clyyy9876543210"
}'

Response​

{
"success": true,
"data": {
"message_id": "msg_xyz789",
"customer_id": "clxxx1234567890",
"whatsapp_phone_number_id": "clyyy9876543210",
"status": "sent",
"channel": "whatsapp",
"timestamp": "2025-01-07T10:30:00.000Z"
}
}
info

The whatsapp_phone_number_id in the response indicates which business phone number was used to send the message. This is useful for tracking which number was used when you have multiple WhatsApp phone numbers connected.

Reply / quote a previous message (WhatsApp)​

Add reply_to_message_id to render your message as a quoted reply to a previous message. Use the inbound message's wamid — available in the webhook payload as data.message_id (or data.raw.context.id when the customer replied to one of your messages).

curl -X POST "https://chat.api.co.id/api/v1/public/messages/send" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "6281234567890",
"channel": "whatsapp",
"message_type": "text",
"content": "Betul kak, untuk treatment itu harganya Rp 150.000 😊",
"reply_to_message_id": "wamid.HBgMNjI4MTIzNDU2Nzg5"
}'
info

Works for all WhatsApp message types (text, image, document, etc.) — just add reply_to_message_id alongside your normal payload. The referenced message must be a recent message in the same conversation; very old wamids may not render as a quote.

Send Template​

Send pre-approved WhatsApp template messages. Templates don't require a 24-hour messaging window.

POST /api/v1/public/messages/send

Parameters​

ParameterTypeRequiredDescription
phone_numberstringyesRecipient phone number in E.164 format
channelstringyesMust be "whatsapp"
message_typestringyesMust be "template"
template.namestringyesTemplate name as registered in Meta
template.language.codestringyesLanguage code (e.g., "en_US", "id")
template.componentsarraynoTemplate variables for header, body, buttons
whatsapp_phone_number_idstringnoSpecify which business phone number to send from. If omitted, auto-resolved.

Example with Body Variables​

{
"phone_number": "628123456789",
"channel": "whatsapp",
"message_type": "template",
"whatsapp_phone_number_id": "clyyy9876543210",
"template": {
"name": "order_confirmation",
"language": { "code": "id" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "John Doe" },
{ "type": "text", "text": "INV-12345" }
]
}
]
}
}

Template with Header Media​

For templates with image, video, or document headers, add a header component with the media URL.

{
"phone_number": "628123456789",
"channel": "whatsapp",
"message_type": "template",
"template": {
"name": "promo_with_image",
"language": { "code": "id" },
"components": [
{
"type": "header",
"parameters": [
{ "type": "image", "image": { "link": "https://example.com/promo.jpg" } }
]
},
{
"type": "body",
"parameters": [
{ "type": "text", "text": "FLASH SALE" },
{ "type": "text", "text": "50%" }
]
}
]
}
}

Supported header media types:

  • image - JPEG, PNG (max 5MB)
  • video - MP4 (max 16MB)
  • document - PDF, DOC (max 100MB)

Template with Dynamic URL Button​

For templates with dynamic URL buttons (URL contains {{1}}), add a button component.

{
"phone_number": "628123456789",
"channel": "whatsapp",
"message_type": "template",
"template": {
"name": "order_tracking",
"language": { "code": "id" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "INV-12345" }
]
},
{
"type": "button",
"sub_type": "url",
"index": 0,
"parameters": [
{ "type": "text", "text": "tracking-abc123" }
]
}
]
}
}

Template with Copy Code Button​

For marketing templates with copy code buttons (coupon codes, promo codes).

{
"phone_number": "628123456789",
"channel": "whatsapp",
"message_type": "template",
"template": {
"name": "promo_code",
"language": { "code": "id" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "FLASH SALE" }
]
},
{
"type": "button",
"sub_type": "copy_code",
"index": 0,
"parameters": [
{ "type": "coupon_code", "coupon_code": "SAVE20OFF" }
]
}
]
}
}

Authentication Template (OTP)​

For authentication templates with OTP codes. Include both body and copy code button.

{
"phone_number": "628123456789",
"channel": "whatsapp",
"message_type": "template",
"template": {
"name": "otp_verification",
"language": { "code": "id" },
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "123456" }
]
},
{
"type": "button",
"sub_type": "copy_code",
"index": 0,
"parameters": [
{ "type": "coupon_code", "coupon_code": "123456" }
]
}
]
}
}
warning

For authentication templates, always include both the OTP in body parameters AND in the copy code button to ensure users can both see and copy the code.

Component Parameters Reference​

Component TypeParameter TypeUsage
headertext, image, video, documentHeader variables or media
bodytext, currency, date_timeBody text variables
button (url)textDynamic URL suffix
button (copy_code)coupon_codeCoupon/OTP code to copy
info

Template must be approved by Meta before use. The components field is optional and only needed if your template has variables.

Create Template​

Create a new WhatsApp message template via API (step 1 of 2). This only saves the template record — call Submit Template next to send it to Meta for approval.

POST /api/v1/public/templates

Body Parameters​

ParameterTypeRequiredDescription
template_namestringyesLowercase letters, numbers, and underscores only. Must be unique per language.
categorystringyes"MARKETING", "UTILITY", or "AUTHENTICATION"
languagestringnoLanguage code (default "en_US"), e.g. "id"
bodystringyesMessage body text. Use {{1}}, {{2}}, ... for variables (sequential from 1).
variablesstring[]noExample value for each {{n}} placeholder. Count must match the placeholders.
headerobjectno{ type: TEXT|IMAGE|VIDEO|DOCUMENT, text, media_handle }. Media headers need a resumable-upload media_handle (not a public URL).
footerstringnoFooter text, max 60 characters.
buttonsobject[]noArray of { type: QUICK_REPLY|URL|PHONE_NUMBER|OTP, text, url, phone_number }.
whatsapp_phone_number_idstringnoTarget a specific number's WABA. Defaults to your primary connected number.
info

The response returns the new template id with status: "PENDING". It is not sent to Meta yet — pass that id to Submit Template. For AUTHENTICATION templates Meta auto-generates the body (no footer; OTP button only).

Example Request​

curl -X POST "https://chat.api.co.id/api/v1/public/templates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template_name": "order_update",
"category": "UTILITY",
"language": "id",
"body": "Halo {{1}}, pesanan {{2}} Anda sedang diproses.",
"variables": ["Budi", "INV-12345"]
}'

Response​

{
"success": true,
"data": {
"id": "cmxxx1234567890",
"template_name": "order_update",
"language": "id",
"status": "PENDING",
"category": "UTILITY",
"created_at": "2026-06-19T10:00:00.000Z"
}
}

Submit Template to Meta​

Submit a previously-created template to Meta for approval (step 2 of 2). Use the template id returned by Create Template.

POST /api/v1/public/templates/:id/submit

info

On success the template gets a meta_template_id and stays PENDING while Meta reviews it (minutes up to 24h). Poll List Templates / Get Template for the final status. If Meta rejects it, the response returns an error and the template becomes REJECTED with a reason.

Example Request​

curl -X POST "https://chat.api.co.id/api/v1/public/templates/TEMPLATE_ID/submit" \
-H "Authorization: Bearer YOUR_API_KEY"

Response​

{
"success": true,
"data": {
"id": "cmxxx1234567890",
"template_name": "order_update",
"language": "id",
"status": "PENDING",
"category": "UTILITY",
"meta_template_id": "1234567890123456",
"submitted_at": "2026-06-19T10:00:05.000Z"
}
}

List Templates​

Retrieve all WhatsApp message templates for the authenticated user. Use this to find available templates and their approval status.

GET /api/v1/public/templates

Query Parameters​

ParameterTypeRequiredDescription
statusstringnoFilter by status: "APPROVED", "PENDING", "REJECTED", "PAUSED", "DISABLED"
categorystringnoFilter by category: "MARKETING", "UTILITY", "AUTHENTICATION"
whatsapp_phone_number_idstringnoFilter templates by phone number's WABA
limitnumbernoMax results (1-500, default 100)
offsetnumbernoSkip N results for pagination (default 0)
info

Use this endpoint to find your template name and verify it's APPROVED before sending. Filter by status=APPROVED to see only ready-to-use templates.

Example Request​

curl -X GET "https://chat.api.co.id/api/v1/public/templates?status=APPROVED&limit=50" \
-H "Authorization: Bearer YOUR_API_KEY"

Response​

{
"success": true,
"data": [
{
"id": "cmxxx1234567890",
"template_name": "otp_verification",
"language": "en_US",
"status": "APPROVED",
"category": "AUTHENTICATION",
"content": "{{1}} is your verification code.",
"header_type": null,
"header_content": null,
"footer_content": null,
"buttons": [
{
"type": "OTP",
"text": "Copy Code"
}
],
"variables": ["123456"],
"has_variables": true,
"created_at": "2026-04-03T09:52:02.297Z",
"updated_at": "2026-04-03T09:52:02.297Z"
}
],
"pagination": {
"total": 1,
"limit": 100,
"offset": 0,
"has_more": false
}
}

Get Template Detail​

Retrieve a specific template by ID, including all components, buttons, and metadata.

GET /api/v1/public/templates/:templateId

URL Parameters​

ParameterTypeRequiredDescription
templateIdstringyesTemplate ID (CUID) from the list endpoint

Example Request​

curl -X GET "https://chat.api.co.id/api/v1/public/templates/cmxxx1234567890" \
-H "Authorization: Bearer YOUR_API_KEY"

Response​

{
"success": true,
"data": {
"id": "cmxxx1234567890",
"template_name": "promo_with_image",
"language": "id",
"status": "APPROVED",
"category": "MARKETING",
"content": "Hi {{1}}, get {{2}} off on your next purchase!",
"header_type": "IMAGE",
"header_content": "https://example.com/promo.jpg",
"footer_content": "Reply STOP to unsubscribe",
"buttons": [
{
"type": "URL",
"text": "Shop Now",
"url": "https://example.com/shop/{{1}}"
}
],
"variables": ["John", "50%"],
"has_variables": true,
"created_at": "2026-04-03T09:52:02.297Z",
"updated_at": "2026-04-03T09:52:02.297Z"
}
}

Send Media​

Send images, documents, audio, or video files.

POST /api/v1/public/messages/send

Supported Media Types by Channel​

WhatsApp

  • image — JPEG, PNG (max 5MB)
  • document — PDF, DOC, DOCX (max 100MB)
  • audio — MP3, OGG (max 16MB)
  • video — MP4 (max 16MB)

Instagram

  • image — JPEG, PNG
  • media_share — Share existing media

Messenger

  • image — JPEG, PNG, GIF
  • video — MP4
  • audio — MP3, WAV
  • file — Any file type

Example​

{
"phone_number": "628123456789",
"channel": "whatsapp",
"message_type": "image",
"media_url": "https://example.com/image.jpg",
"caption": "Check out this image!",
"whatsapp_phone_number_id": "clyyy9876543210"
}

Send Interactive​

Send WhatsApp interactive messages with CTA URL buttons, Reply buttons, or List Menu.

POST /api/v1/public/messages/send

info

WhatsApp Only: Interactive messages are only available for WhatsApp channel. They require an active 24-hour messaging window.

Interactive Types​

CTA URL Button

Send a message with a clickable URL button. Great for directing customers to your website, tracking links, or external resources.

Reply Buttons

Send a message with up to 3 quick reply buttons. Perfect for surveys, confirmations, or guiding customer choices.

List Menu

Send a message with a list menu containing up to 10 sections and 10 items per section. Ideal for product catalogs, service menus, or any scenario requiring organized selection options.

CTA URL Button Example​

{
"phone_number": "628123456789",
"channel": "whatsapp",
"message_type": "interactive",
"whatsapp_phone_number_id": "clyyy9876543210",
"interactive": {
"type": "cta_url",
"body": {
"text": "Check out our latest products!"
},
"footer": {
"text": "Tap the button below"
},
"action": {
"name": "cta_url",
"parameters": {
"display_text": "Visit Store",
"url": "https://example.com/store"
}
}
}
}

Reply Buttons Example​

{
"phone_number": "628123456789",
"channel": "whatsapp",
"message_type": "interactive",
"whatsapp_phone_number_id": "clyyy9876543210",
"interactive": {
"type": "button",
"body": {
"text": "How would you rate our service?"
},
"footer": {
"text": "Your feedback helps us improve"
},
"action": {
"buttons": [
{
"type": "reply",
"reply": { "id": "rating_good", "title": "Good" }
},
{
"type": "reply",
"reply": { "id": "rating_ok", "title": "Okay" }
},
{
"type": "reply",
"reply": { "id": "rating_bad", "title": "Bad" }
}
]
}
}
}

List Menu Example​

{
"phone_number": "628123456789",
"channel": "whatsapp",
"message_type": "interactive",
"whatsapp_phone_number_id": "clyyy9876543210",
"interactive": {
"type": "list",
"body": {
"text": "Please select a service from our menu"
},
"header": {
"type": "text",
"text": "Our Services"
},
"footer": {
"text": "Tap the button to see options"
},
"action": {
"button": "View Menu",
"sections": [
{
"title": "Hair Services",
"rows": [
{
"id": "haircut",
"title": "Haircut",
"description": "Basic haircut service"
},
{
"id": "hair_coloring",
"title": "Hair Coloring",
"description": "Full hair coloring service"
}
]
},
{
"title": "Spa Services",
"rows": [
{
"id": "massage",
"title": "Massage",
"description": "60-minute relaxing massage"
},
{
"id": "facial",
"title": "Facial Treatment",
"description": "Deep cleansing facial"
}
]
}
]
}
}
}

Parameters​

ParameterTypeRequiredDescription
interactive.typestringyes"cta_url" for URL button, "button" for reply buttons, or "list" for list menu
interactive.body.textstringyesMain message text (max 1024 characters)
interactive.headerobjectnoOptional header. For list type: text only (max 60 chars). For others: text, image, video, or document
interactive.footer.textstringnoOptional footer text (max 60 characters)
interactive.actionobjectyesAction configuration (CTA URL params, buttons array, or list sections)
interactive.action.buttonstringnoFor list type: Button text to open the list menu (max 20 characters)
interactive.action.sectionsarraynoFor list type: Array of sections (1-10 sections)
sections[].titlestringnoSection title (max 24 characters)
sections[].rowsarrayyesArray of row items in the section (1-10 rows)
rows[].idstringyesUnique identifier for the row (max 200 characters). Returned in webhook when user selects this item
rows[].titlestringyesRow title displayed to user (max 24 characters)
rows[].descriptionstringnoRow description (max 72 characters)

Limits​

ElementLimit
Body text1024 characters
Header text60 characters
Footer text60 characters
Button title20 characters
CTA display text20 characters
Reply buttonsMaximum 3 buttons
Button ID256 characters
List Menu Limits
List button text20 characters
List sections1-10 sections
Rows per section1-10 rows
Section title24 characters
Row ID200 characters
Row title24 characters
Row description72 characters

Typing Indicator​

Send typing indicator ("sedang mengetik...") to customers before sending a message.

POST /api/v1/public/conversations/:customerIdentifier/typing

URL Parameters​

ParameterTypeRequiredDescription
customerIdentifierstringyesCustomer ID (CUID), phone number, or Instagram username

Request Body (Optional)​

ParameterTypeRequiredDescription
channelstringno"whatsapp" or "instagram". Auto-detected if not provided.
whatsapp_phone_number_idstringnoWhatsApp phone number ID. Specify which business number sends the typing indicator.
instagram_account_idstringnoInstagram account ID. Required when customer has conversations with multiple Instagram accounts.
info

Rate Limit: Maximum 1 request per 3 seconds per customer. If exceeded, the API returns 429 with Retry-After header.

Example Request​

curl -X POST "https://chat.api.co.id/api/v1/public/conversations/628123456789/typing" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "whatsapp",
"whatsapp_phone_number_id": "clyyy9876543210"
}'

Instagram Example​

// Send typing to Instagram customer
POST /api/v1/public/conversations/ig_username/typing
Content-Type: application/json

{
"channel": "instagram",
"instagram_account_id": "your_ig_account_id"
}

Response​

{
"success": true,
"data": {
"customer_id": "clxxx1234567890",
"channel": "whatsapp",
"typing_sent": true,
"timestamp": "2026-01-26T10:30:00.000Z"
}
}

Error Codes​

CodeDescription
NotFoundCustomer not found
WindowClosed24-hour messaging window expired
RateLimitExceededMax 1 request per 3 seconds per customer
ConfigurationErrorChannel not available for customer
ConnectionErrorWhatsApp/Instagram account disconnected
AmbiguousAccountCustomer has multiple IG accounts, specify instagram_account_id

Mark as Read​

Mark a message as read and send read receipt to the customer.

POST /api/v1/public/messages/:messageId/read

URL Parameters​

ParameterTypeRequiredDescription
messageIdstringyesMessage ID (CUID) to mark as read
info

Important: Only inbound messages can be marked as read. Attempting to mark an outbound message as read will return a validation error.

warning

WhatsApp: Requires connected WhatsApp Business Account. The message must have a valid wamId.

info

Multi-Number: The read receipt is automatically sent via the same WhatsApp phone number that received the message. No additional parameters needed.

Supported Channels​

  • WhatsApp: Sends read receipt via WhatsApp API
  • Instagram: Sends mark_seen via Instagram API
  • Messenger: Not supported via Public API

Example Request​

curl -X POST "https://chat.api.co.id/api/v1/public/messages/msg_xyz789/read" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json"

Success Response​

{
"success": true,
"data": {
"message_id": "msg_xyz789",
"status": "read",
"channel": "whatsapp",
"updated_at": "2026-01-27T10:30:00.000Z"
}
}

Error Responses​

404 - Message Not Found

{
"error": {
"code": "NotFound",
"message": "Message not found"
}
}

400 - Validation Error (Outbound Message)

{
"error": {
"code": "ValidationError",
"message": "Only inbound messages can be marked as read"
}
}

400 - Configuration Error (WhatsApp Not Connected)

{
"error": {
"code": "ConfigurationError",
"message": "Cannot send read receipt: missing configuration"
}
}

Try it out​

tip

Press Try it and paste your API key (kc_live_...) into the API key (Bearer) field. Get the key from API Keys. Requests run against your live account, so endpoints that send, create, or delete act on real data.

  • POST/api/v1/public/messages/sendSend Message
  • GET/api/v1/public/templatesList Templates
  • POST/api/v1/public/templatesCreate Template
  • POST/api/v1/public/templates/{id}/submitSubmit Template to Meta
  • GET/api/v1/public/templates/{templateId}Get Template Detail
  • POST/api/v1/public/conversations/{customerIdentifier}/typingTyping Indicator
  • POST/api/v1/public/messages/{messageId}/readMark as Read
Was this page helpful?