หน้านี้อธิบาย API ส่งข้อความเข้าแชท และ Developer webhook ที่ส่งเหตุการณ์ของแชทไปยังระบบของคุณ ใช้คู่กับ Developer API References (Contact API, Chat API) และ Customer Profile Webhook
1. เริ่มต้นใช้งาน
- สร้าง API Key ที่ ตั้งค่า › Developer › API Key แล้วเลือกสิทธิ์
ส่งข้อความเข้าแชท(ปิดไว้เป็นค่าเริ่มต้น) · ส่งคีย์ใน headerx-easyrobin-api-key - Base URL: https://<โดเมนของระบบ>/api/partner/v1
- ทุก API รับและตอบเป็น JSON (
Content-Type: application/json) · คำตอบเป็นรูปแบบเดียวกับ Developer API References:{ ok, status_code, message, data }
2. ส่งข้อความเข้าแชท
Method : POST · URL : /chats/:conversation_id/messages · Authorization : API Key (สิทธิ์ messages:write)
conversation_id คือห้องแชท ได้จาก Developer webhook (message.created) · ห้องแชทของธุรกิจอื่นจะตอบ 404
Request body
Key | คำอธิบาย |
typetext | image · required | ประเภทข้อความ: text หรือ image |
textstring | ข้อความ (เมื่อ type เป็น text) · ไม่เกิน 5,000 ตัวอักษร |
image_urlstring (uri) | ลิงก์รูปแบบ https ที่เปิดได้จากอินเทอร์เน็ต (เมื่อ type เป็น image) · ลิงก์ในเครือข่ายภายในหรือ IP ส่วนตัวจะตอบ 400 · ไม่เกิน 2,000 ตัวอักษร |
Header เพิ่มเติม : Idempotency-Key (ไม่บังคับ, ไม่เกิน 128 ตัวอักษร) — ส่งค่าเดิมซ้ำภายใน 24 ชั่วโมงจะได้คำตอบเดิม ไม่ส่งข้อความซ้ำ
Examples
text
curl --location --request POST '{BASE_URL}/chats/<conversation_id>/messages' \
--header 'x-easyrobin-api-key: <your_app_api_key>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: order-1024-shipped' \
--data '{"type":"text","text":"สวัสดีค่ะ ออเดอร์ของคุณจัดส่งแล้ว"}'image
curl --location --request POST '{BASE_URL}/chats/<conversation_id>/messages' \
--header 'x-easyrobin-api-key: <your_app_api_key>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: order-1024-shipped' \
--data '{"type":"image","image_url":"https://example.com/receipt.jpg"}'Response
Status | เมื่อ | message |
200 | ส่งแล้ว | Message has been sent |
202 | บันทึกแล้ว ช่องทางกำลังส่ง | Message is being sent |
400 | ข้อมูลที่ส่งมาไม่ถูกต้อง | text is required (at most 5000 characters) |
401 | ไม่มี API Key หรือคีย์ไม่ถูกต้อง | You do not have valid permission |
403 | คีย์ไม่มีสิทธิ์ messages:write | This API key does not have the messages:write scope |
404 | ไม่พบห้องแชท (หรือเป็นห้องแชทของธุรกิจอื่น) | Not found chat room |
409 | แชทถูกบล็อกหรือเป็นสแปม | Chat room cannot receive messages |
429 | ส่งเกินจำนวนที่คีย์ส่งได้ (รอตาม Retry-After วินาที) | Too many requests, try again later |
502 | ส่งไม่ถึง หรือช่องทางใช้งานไม่ได้ | Service unavailable, try again later |
503 | ระบบแชทไม่ตอบภายใน 5 วินาที (ชั่วคราว) · ลองใหม่หลัง Retry-After วินาที ด้วย Idempotency-Key เดิม | Service busy, try again later |
{
"ok": true,
"status_code": 200,
"message": "Message has been sent",
"data": {
"message_id": "0192f3a0-7c1e-7b2a-9d4e-2f1a3b4c5d6e",
"status": "sent"
}
}จำกัด 5 ครั้งต่อวินาทีต่อคีย์ (ยืดหยุ่นได้ถึง 20) · เกินจะได้ 429 พร้อม header Retry-After (วินาที)
3. Developer webhook
เปิดที่ ตั้งค่า › Developer › Webhook แท็บ เหตุการณ์แชท: ใส่ URL (https เท่านั้น) เลือกเหตุการณ์ แล้วเปิดใช้งาน · ระบบจะ POST JSON ไปที่ URL นั้น ตอบกลับ 2xx ภายใน 10 วินาที (ลองใหม่สูงสุด 3 ครั้งเมื่อเครือข่ายล้มเหลว, 429 หรือ 5xx)
Event | เมื่อ |
message.created | มีข้อความใหม่ในแชทของธุรกิจ (จากลูกค้า สมาชิก แชทบอท หรือ API) |
chat.closed | แชทจบแล้ว (ปิดเคสแล้ว) |
customer.created | มีรายชื่อลูกค้าใหม่ในธุรกิจ |
customer.updated | ธุรกิจแก้ไขข้อมูลรายชื่อลูกค้า |
chat.assigned | แชทมีผู้รับผิดชอบคนใหม่ (สมาชิกรับแชทเอง หรือรับแชทที่ถูกโอนมา) |
chat.opened | แชทรอสมาชิกตอบ (เปิดเคสใหม่ หรือเปิดเคสเดิมอีกครั้ง) · แชทที่แชทบอทกำลังตอบจะยังไม่ส่ง จนกว่าจะส่งต่อให้สมาชิก มีสมาชิกรับ หรือมอบหมายแชท |
Headers
Header | คำอธิบาย |
X-Easyrobin-Timestamp | เวลาที่ลงลายเซ็น (unix วินาที) · ปฏิเสธถ้าห่างจากเวลาของคุณเกิน 5 นาที |
X-Easyrobin-Signature | ลายเซ็นของคำขอ (ดูวิธีตรวจในหัวข้อ 4) · เทียบแบบ constant time |
X-Easyrobin-Event | ชื่อเหตุการณ์ เช่น message.created |
message.created payload
Key | คำอธิบาย |
eventmessage.created · required | ชื่อเหตุการณ์ (ค่าเดียวกับ header X-Easyrobin-Event) |
event_idstring (uuid) · required | รหัสเหตุการณ์ เหมือนเดิมทุกครั้งที่ลองส่งใหม่ ใช้กันการประมวลผลซ้ำ |
occurred_atstring (date-time) · required | เวลาที่เกิดเหตุการณ์ (ISO 8601) |
business_idstring (uuid) · required | รหัสธุรกิจ |
conversation_idstring (uuid) · required | รหัสห้องแชท · ใช้ส่งข้อความกลับด้วย POST /chats/{conversation_id}/messages |
customer_idstring | null (uuid) · required | รหัสรายชื่อลูกค้า · เป็น null เมื่อเป็นแชทกลุ่ม |
kindstring · required | ประเภทแชท: direct (แชทเดี่ยว) หรือ group (แชทกลุ่ม) |
messageobject · required | ข้อความที่ส่งเข้ามา (ผู้ส่ง ประเภท และเนื้อหา) |
{
"event": "message.created",
"event_id": "0192f3a0-8a11-7c3d-9e2f-1a2b3c4d5e6f",
"occurred_at": "2026-10-01T09:30:00Z",
"business_id": "0192f3a0-0000-7000-8000-000000000001",
"conversation_id": "0192f3a0-1111-7000-8000-000000000002",
"customer_id": "0192f3a0-2222-7000-8000-000000000003",
"kind": "direct",
"message": {
"id": "0192f3a0-3333-7000-8000-000000000004",
"conversation_id": "0192f3a0-1111-7000-8000-000000000002",
"direction": "inbound",
"type": "text",
"content": {
"text": "สนใจสินค้าค่ะ"
},
"sender": {
"kind": "customer"
},
"status": "received",
"reference_id": null,
"provider_message_id": "5190000000000001",
"error": null,
"created_at": "2026-10-01T09:30:00Z",
"updated_at": "2026-10-01T09:30:00Z"
}
}chat.closed payload
Key | คำอธิบาย |
eventchat.closed · required | ชื่อเหตุการณ์ (ค่าเดียวกับ header X-Easyrobin-Event) |
event_idstring (uuid) · required | รหัสเหตุการณ์ เหมือนเดิมทุกครั้งที่ลองส่งใหม่ ใช้กันการประมวลผลซ้ำ |
occurred_atstring (date-time) · required | เวลาที่เกิดเหตุการณ์ (ISO 8601) |
business_idstring (uuid) · required | รหัสธุรกิจ |
conversation_idstring (uuid) · required | รหัสห้องแชท · ใช้ส่งข้อความกลับด้วย POST /chats/{conversation_id}/messages |
modestring · required | แชทจบแบบไหน (สมาชิกปิดเอง หรือระบบปิดอัตโนมัติ) |
auto_actionstring | null · required | การทำงานอัตโนมัติที่ปิดแชท · เป็น null เมื่อไม่ได้ปิดอัตโนมัติ |
caseobject · required | เคสของแชทนี้ |
{
"event": "chat.closed",
"event_id": "0192f3a0-8a12-7c3d-9e2f-1a2b3c4d5e70",
"occurred_at": "2026-10-01T10:05:00Z",
"business_id": "0192f3a0-0000-7000-8000-000000000001",
"conversation_id": "0192f3a0-1111-7000-8000-000000000002",
"mode": "manual",
"auto_action": null,
"case": {
"id": "0192f3a0-4444-7000-8000-000000000005",
"short_id": 1024,
"status": "closed",
"closed_at": "2026-10-01T10:05:00Z"
}
}customer.created payload
Key | คำอธิบาย |
eventcustomer.created · required | ชื่อเหตุการณ์ (ค่าเดียวกับ header X-Easyrobin-Event) |
event_idstring (uuid) · required | รหัสเหตุการณ์ เหมือนเดิมทุกครั้งที่ลองส่งใหม่ ใช้กันการประมวลผลซ้ำ |
occurred_atstring (date-time) · required | เวลาที่เกิดเหตุการณ์ (ISO 8601) |
business_idstring (uuid) · required | รหัสธุรกิจ |
customerobject · required | ข้อมูลรายชื่อลูกค้าตามที่ธุรกิจบันทึกไว้ (รูปแบบเดียวกันทั้ง customer.created และ customer.updated) |
{
"business_id": "01a1211d-e35c-70f9-b67e-000000000001",
"customer": {
"address": null,
"age": 34,
"birth_date": "1992-05-01",
"chat_tags": [
{
"id": "01a12340-0000-7000-8000-0000000007a9",
"keyword": "VIP"
}
],
"contact_tags": [],
"customer_code": "C-0042",
"display_name": "สมชาย ใจดี",
"email": "[email protected]",
"fields": {
"01a12340-0000-7000-8000-0000000f1e1d": 42
},
"first_name": "สมชาย",
"gender": "male",
"id": "01a12340-0000-7000-8000-0000000c0575",
"last_name": "ใจดี",
"org_name": null,
"phone": "0812345678",
"primary_identity": {
"provider": "line",
"channel_id": "01a12340-0000-7000-8000-00000000ca11",
"external_id": "U0123456789abcdef0123456789abcdef",
"display_name": "Somchai"
},
"referral_tags": [],
"tin": null
},
"event": "customer.created",
"event_id": "01a12340-0000-7000-8000-000000000e03",
"occurred_at": "2026-10-10T10:42:00Z"
}customer.updated payload
Key | คำอธิบาย |
eventcustomer.updated · required | ชื่อเหตุการณ์ (ค่าเดียวกับ header X-Easyrobin-Event) |
event_idstring (uuid) · required | รหัสเหตุการณ์ เหมือนเดิมทุกครั้งที่ลองส่งใหม่ ใช้กันการประมวลผลซ้ำ |
occurred_atstring (date-time) · required | เวลาที่เกิดเหตุการณ์ (ISO 8601) |
business_idstring (uuid) · required | รหัสธุรกิจ |
customerobject · required | ข้อมูลรายชื่อลูกค้าตามที่ธุรกิจบันทึกไว้ (รูปแบบเดียวกันทั้ง customer.created และ customer.updated) |
changedarray · required | ข้อมูลที่ธุรกิจแก้ไข (ชื่อ key · ช่องข้อมูลที่ธุรกิจสร้างเองเป็น fields.<field_id>) |
{
"business_id": "01a1211d-e35c-70f9-b67e-000000000001",
"changed": [
"phone",
"chat_tags",
"fields.01a12340-0000-7000-8000-0000000f1e1d"
],
"customer": {
"address": null,
"age": 34,
"birth_date": "1992-05-01",
"chat_tags": [
{
"id": "01a12340-0000-7000-8000-0000000007a9",
"keyword": "VIP"
}
],
"contact_tags": [],
"customer_code": "C-0042",
"display_name": "สมชาย ใจดี",
"email": "[email protected]",
"fields": {
"01a12340-0000-7000-8000-0000000f1e1d": 42
},
"first_name": "สมชาย",
"gender": "male",
"id": "01a12340-0000-7000-8000-0000000c0575",
"last_name": "ใจดี",
"org_name": null,
"phone": "0812345678",
"primary_identity": {
"provider": "line",
"channel_id": "01a12340-0000-7000-8000-00000000ca11",
"external_id": "U0123456789abcdef0123456789abcdef",
"display_name": "Somchai"
},
"referral_tags": [],
"tin": null
},
"event": "customer.updated",
"event_id": "01a12340-0000-7000-8000-000000000e04",
"occurred_at": "2026-10-10T10:42:00Z"
}chat.assigned payload
Key | คำอธิบาย |
eventchat.assigned · required | ชื่อเหตุการณ์ (ค่าเดียวกับ header X-Easyrobin-Event) |
event_idstring (uuid) · required | รหัสเหตุการณ์ เหมือนเดิมทุกครั้งที่ลองส่งใหม่ ใช้กันการประมวลผลซ้ำ |
occurred_atstring (date-time) · required | เวลาที่เกิดเหตุการณ์ (ISO 8601) |
business_idstring (uuid) · required | รหัสธุรกิจ |
conversation_idstring (uuid) · required | รหัสห้องแชท · ใช้ส่งข้อความกลับด้วย POST /chats/{conversation_id}/messages |
customer_idstring | null (uuid) · required | รหัสรายชื่อลูกค้า · เป็น null เมื่อเป็นแชทกลุ่ม |
assigneeobject · required | ผู้รับผิดชอบแชทตอนนี้ |
previous_assigneeobject | null · required | ผู้รับผิดชอบก่อนหน้า · เป็น null เมื่อยังไม่มี |
{
"assignee": {
"user_id": "01a12340-0000-7000-8000-00000000a6e7",
"team_id": null
},
"business_id": "01a1211d-e35c-70f9-b67e-000000000001",
"conversation_id": "01a12340-0000-7000-8000-00000000c0de",
"customer_id": "01a12340-0000-7000-8000-0000000c0575",
"event": "chat.assigned",
"event_id": "01a12340-0000-7000-8000-000000000e05",
"occurred_at": "2026-10-10T10:42:00Z",
"previous_assignee": null
}chat.opened payload
Key | คำอธิบาย |
eventchat.opened · required | ชื่อเหตุการณ์ (ค่าเดียวกับ header X-Easyrobin-Event) |
event_idstring (uuid) · required | รหัสเหตุการณ์ เหมือนเดิมทุกครั้งที่ลองส่งใหม่ ใช้กันการประมวลผลซ้ำ |
occurred_atstring (date-time) · required | เวลาที่เกิดเหตุการณ์ (ISO 8601) |
business_idstring (uuid) · required | รหัสธุรกิจ |
conversation_idstring (uuid) · required | รหัสห้องแชท · ใช้ส่งข้อความกลับด้วย POST /chats/{conversation_id}/messages |
reopenedboolean · required | true = ลูกค้าทักกลับมาหลังแชทจบแล้ว |
caseobject · required | เคสของแชทนี้ |
{
"business_id": "01a1211d-e35c-70f9-b67e-000000000001",
"case": {
"id": "01a12340-0000-7000-8000-00000000ca5e",
"short_id": 1042,
"status": "inprogress"
},
"conversation_id": "01a12340-0000-7000-8000-00000000c0de",
"event": "chat.opened",
"event_id": "01a12340-0000-7000-8000-000000000e06",
"occurred_at": "2026-10-10T10:42:00Z",
"reopened": false
}event_id เหมือนเดิมทุกครั้งที่ลองส่งใหม่ ใช้กันการประมวลผลซ้ำ · X-Easyrobin-Event บอกชื่อเหตุการณ์4. ตรวจ signature
ทุกคำขอลงลายเซ็นด้วย signing secret ของธุรกิจ (ดู/เปลี่ยนได้ที่ ตั้งค่า › Developer › Webhook แท็บ Signing Secret) · X-Easyrobin-Signature = sha256=<hex HMAC-SHA256(secret, "<X-Easyrobin-Timestamp>.<raw body>")>
- อ่าน body แบบ raw bytes ก่อนแปลง JSON
- ปฏิเสธถ้า
X-Easyrobin-Timestampห่างจากเวลาปัจจุบันเกิน 5 นาที - คำนวณลายเซ็นแล้วเทียบแบบ constant time
Node.js
import crypto from "node:crypto";
// raw = the request body bytes exactly as received (do not parse and re-serialise first)
function verify(raw, headers, secret) {
const ts = headers["x-easyrobin-timestamp"];
if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // 5 minutes
const want = "sha256=" + crypto.createHmac("sha256", secret).update(ts + ".").update(raw).digest("hex");
const got = String(headers["x-easyrobin-signature"] ?? "");
return got.length === want.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want));
}Python
import hashlib, hmac, time
def verify(raw: bytes, headers, secret: str) -> bool:
ts = headers.get("X-Easyrobin-Timestamp", "")
if not ts or abs(time.time() - int(ts)) > 300: # 5 minutes
return False
want = "sha256=" + hmac.new(secret.encode(), ts.encode() + b"." + raw, hashlib.sha256).hexdigest()
return hmac.compare_digest(headers.get("X-Easyrobin-Signature", ""), want)