Messages
Send Message
Send text messages to customers via WhatsApp, Instagram, or Messenger.
POST /api/v1/public/messages/send
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
phone_number | string | no | Customer phone number. Use this to lookup customer for any channel. |
customer_id | string | no | Customer ID (CUID). Alternative to phone_number. |
instagram_username | string | no | Instagram username. Alternative lookup method. |
channel | string | yes | "whatsapp", "instagram", or "messenger" |
message_type | string | yes | WhatsApp: "text", "image", "document", "audio", "video", "template". Instagram: "text", "image", "media_share". Messenger: "text", "image", "video", "audio", "file". |
content | string | no | Text message content (required for text messages) |
media_url | string | no | Media URL for image/video/audio/file messages |
reply_to_message_id | string | no | WhatsApp 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_id | string | no | WhatsApp 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_id | string | no | Instagram account ID. Required when customer has conversations with multiple Instagram accounts. |
facebook_page_id | string | no | Facebook Page ID. Required when customer has conversations with multiple Facebook Pages. |
Note: At least one of phone_number, customer_id, or instagram_username is required to identify the customer.
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
- JavaScript
- Python
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"
}'
const response = await fetch("https://chat.api.co.id/api/v1/public/messages/send", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
phone_number: "628123456789",
channel: "whatsapp",
message_type: "text",
content: "Hello! This is a test message.",
whatsapp_phone_number_id: "clyyy9876543210" // optional
})
});
import requests
response = requests.post(
"https://chat.api.co.id/api/v1/public/messages/send",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
json={
"phone_number": "628123456789",
"channel": "whatsapp",
"message_type": "text",
"content": "Hello! This is a test message.",
"whatsapp_phone_number_id": "clyyy9876543210" # optional
}
)
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"
}
}
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"
}'
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
| Parameter | Type | Required | Description |
|---|---|---|---|
phone_number | string | yes | Recipient phone number in E.164 format |
channel | string | yes | Must be "whatsapp" |
message_type | string | yes | Must be "template" |
template.name | string | yes | Template name as registered in Meta |
template.language.code | string | yes | Language code (e.g., "en_US", "id") |
template.components | array | no | Template variables for header, body, buttons |
whatsapp_phone_number_id | string | no | Specify 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" }
]
}
]
}
}
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 Type | Parameter Type | Usage |
|---|---|---|
header | text, image, video, document | Header variables or media |
body | text, currency, date_time | Body text variables |
button (url) | text | Dynamic URL suffix |
button (copy_code) | coupon_code | Coupon/OTP code to copy |
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
| Parameter | Type | Required | Description |
|---|---|---|---|
template_name | string | yes | Lowercase letters, numbers, and underscores only. Must be unique per language. |
category | string | yes | "MARKETING", "UTILITY", or "AUTHENTICATION" |
language | string | no | Language code (default "en_US"), e.g. "id" |
body | string | yes | Message body text. Use {{1}}, {{2}}, ... for variables (sequential from 1). |
variables | string[] | no | Example value for each {{n}} placeholder. Count must match the placeholders. |
header | object | no | { type: TEXT|IMAGE|VIDEO|DOCUMENT, text, media_handle }. Media headers need a resumable-upload media_handle (not a public URL). |
footer | string | no | Footer text, max 60 characters. |
buttons | object[] | no | Array of { type: QUICK_REPLY|URL|PHONE_NUMBER|OTP, text, url, phone_number }. |
whatsapp_phone_number_id | string | no | Target a specific number's WABA. Defaults to your primary connected number. |
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
- JavaScript
- Python
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"]
}'
const response = await fetch("https://chat.api.co.id/api/v1/public/templates", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
template_name: "order_update",
category: "UTILITY",
language: "id",
body: "Halo {{1}}, pesanan {{2}} Anda sedang diproses.",
variables: ["Budi", "INV-12345"],
}),
});
const { data } = await response.json();
const templateId = data.id; // pass to the submit endpoint
import requests
res = requests.post(
"https://chat.api.co.id/api/v1/public/templates",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"template_name": "order_update",
"category": "UTILITY",
"language": "id",
"body": "Halo {{1}}, pesanan {{2}} Anda sedang diproses.",
"variables": ["Budi", "INV-12345"],
},
)
template_id = res.json()["data"]["id"]
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
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
- JavaScript
- Python
curl -X POST "https://chat.api.co.id/api/v1/public/templates/TEMPLATE_ID/submit" \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
"https://chat.api.co.id/api/v1/public/templates/" + templateId + "/submit",
{ method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const { data } = await response.json();
console.log(data.status, data.meta_template_id);
import requests
res = requests.post(
f"https://chat.api.co.id/api/v1/public/templates/{template_id}/submit",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
print(res.json())
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
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | no | Filter by status: "APPROVED", "PENDING", "REJECTED", "PAUSED", "DISABLED" |
category | string | no | Filter by category: "MARKETING", "UTILITY", "AUTHENTICATION" |
whatsapp_phone_number_id | string | no | Filter templates by phone number's WABA |
limit | number | no | Max results (1-500, default 100) |
offset | number | no | Skip N results for pagination (default 0) |
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
- JavaScript
- Python
curl -X GET "https://chat.api.co.id/api/v1/public/templates?status=APPROVED&limit=50" \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
"https://chat.api.co.id/api/v1/public/templates?status=APPROVED",
{ headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const { data, pagination } = await response.json();
const approvedTemplates = data.filter(t => t.status === "APPROVED");
import requests
response = requests.get(
"https://chat.api.co.id/api/v1/public/templates",
headers={"Authorization": "Bearer YOUR_API_KEY"},
params={"status": "APPROVED", "limit": 50}
)
templates = response.json()["data"]
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
| Parameter | Type | Required | Description |
|---|---|---|---|
templateId | string | yes | Template ID (CUID) from the list endpoint |
Example Request
- cURL
- JavaScript
- Python
curl -X GET "https://chat.api.co.id/api/v1/public/templates/cmxxx1234567890" \
-H "Authorization: Bearer YOUR_API_KEY"
const templateId = "cmxxx1234567890";
const response = await fetch(
`https://chat.api.co.id/api/v1/public/templates/${templateId}`,
{ headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const { data } = await response.json();
import requests
template_id = "cmxxx1234567890"
response = requests.get(
f"https://chat.api.co.id/api/v1/public/templates/{template_id}",
headers={"Authorization": "Bearer YOUR_API_KEY"}
)
template = response.json()["data"]
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
image— JPEG, PNG (max 5MB)document— PDF, DOC, DOCX (max 100MB)audio— MP3, OGG (max 16MB)video— MP4 (max 16MB)
image— JPEG, PNGmedia_share— Share existing media
Messenger
image— JPEG, PNG, GIFvideo— MP4audio— MP3, WAVfile— 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
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
| Parameter | Type | Required | Description |
|---|---|---|---|
interactive.type | string | yes | "cta_url" for URL button, "button" for reply buttons, or "list" for list menu |
interactive.body.text | string | yes | Main message text (max 1024 characters) |
interactive.header | object | no | Optional header. For list type: text only (max 60 chars). For others: text, image, video, or document |
interactive.footer.text | string | no | Optional footer text (max 60 characters) |
interactive.action | object | yes | Action configuration (CTA URL params, buttons array, or list sections) |
interactive.action.button | string | no | For list type: Button text to open the list menu (max 20 characters) |
interactive.action.sections | array | no | For list type: Array of sections (1-10 sections) |
sections[].title | string | no | Section title (max 24 characters) |
sections[].rows | array | yes | Array of row items in the section (1-10 rows) |
rows[].id | string | yes | Unique identifier for the row (max 200 characters). Returned in webhook when user selects this item |
rows[].title | string | yes | Row title displayed to user (max 24 characters) |
rows[].description | string | no | Row description (max 72 characters) |
Limits
| Element | Limit |
|---|---|
| Body text | 1024 characters |
| Header text | 60 characters |
| Footer text | 60 characters |
| Button title | 20 characters |
| CTA display text | 20 characters |
| Reply buttons | Maximum 3 buttons |
| Button ID | 256 characters |
| List Menu Limits | |
| List button text | 20 characters |
| List sections | 1-10 sections |
| Rows per section | 1-10 rows |
| Section title | 24 characters |
| Row ID | 200 characters |
| Row title | 24 characters |
| Row description | 72 characters |
Typing Indicator
Send typing indicator ("sedang mengetik...") to customers before sending a message.
POST /api/v1/public/conversations/:customerIdentifier/typing
URL Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
customerIdentifier | string | yes | Customer ID (CUID), phone number, or Instagram username |
Request Body (Optional)
| Parameter | Type | Required | Description |
|---|---|---|---|
channel | string | no | "whatsapp" or "instagram". Auto-detected if not provided. |
whatsapp_phone_number_id | string | no | WhatsApp phone number ID. Specify which business number sends the typing indicator. |
instagram_account_id | string | no | Instagram account ID. Required when customer has conversations with multiple Instagram accounts. |
Rate Limit: Maximum 1 request per 3 seconds per customer. If exceeded, the API returns 429 with Retry-After header.
Example Request
- cURL (Phone)
- cURL (Simple)
- JavaScript
- Python
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"
}'
curl -X POST "https://chat.api.co.id/api/v1/public/conversations/628123456789/typing" \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch("https://chat.api.co.id/api/v1/public/conversations/628123456789/typing", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
channel: "whatsapp",
whatsapp_phone_number_id: "clyyy9876543210" // optional
})
});
import requests
response = requests.post(
"https://chat.api.co.id/api/v1/public/conversations/628123456789/typing",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
json={
"channel": "whatsapp",
"whatsapp_phone_number_id": "clyyy9876543210" # optional
}
)
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
| Code | Description |
|---|---|
NotFound | Customer not found |
WindowClosed | 24-hour messaging window expired |
RateLimitExceeded | Max 1 request per 3 seconds per customer |
ConfigurationError | Channel not available for customer |
ConnectionError | WhatsApp/Instagram account disconnected |
AmbiguousAccount | Customer 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
| Parameter | Type | Required | Description |
|---|---|---|---|
messageId | string | yes | Message ID (CUID) to mark as read |
Important: Only inbound messages can be marked as read. Attempting to mark an outbound message as read will return a validation error.
WhatsApp: Requires connected WhatsApp Business Account. The message must have a valid wamId.
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
- JavaScript
- Python
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"
const response = await fetch("https://chat.api.co.id/api/v1/public/messages/msg_xyz789/read", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
}
});
import requests
response = requests.post(
"https://chat.api.co.id/api/v1/public/messages/msg_xyz789/read",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"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
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