ให้ AI รับสายและโทรออกจากระบบของคุณ
https://api.agenticos.tech/api/v1/voice-agent · ข้อมูลใน API ใช้ camelCase1. ภาพรวม
| ต้องการ | ใช้ | ยืนยันสิทธิ์ด้วย |
|---|---|---|
| ให้ลูกค้าคุยกับ AI ด้วยเสียงบนเว็บ/แอป | หน้าโทรสำเร็จรูป /v/<embed key> (ลิงก์ หรือ iframe) | Embed key |
| ให้ AI โทรออกหารายชื่อลูกค้า | Campaign API | JWT |
| ดึงบทสนทนา สรุป และข้อมูลที่ AI เก็บได้ | Calls API | JWT |
| ให้ AI รับสายเข้าเบอร์บริษัท | ผูกเบอร์กับ agent (Numbers) | JWT |
ก่อนเริ่ม: ต้องขออะไรจากแพลตฟอร์ม
บางอย่างขอจากแอดมินขององค์กรลูกค้า (owner หรือ admin ในบัญชี AgenticOS) บางอย่างต้องให้ทีม AgenticOS ทำ เพราะเกี่ยวกับระบบโทรศัพท์ ขอให้ครบตามสิ่งที่จะใช้ก่อนเริ่มเขียนโค้ด
| ต้องขอ | ใช้ทำอะไร | ขอจาก / หาได้ที่ | ใช้กับ |
|---|---|---|---|
| บัญชีผู้ใช้สำหรับเชื่อมระบบ | login เพื่อรับ JWT (ข้อ 3) ขอเป็นอีเมลเฉพาะของระบบ บทบาท admin และยืนยันอีเมลแล้ว | แอดมิน: ตั้งค่า → สมาชิก → เชิญ | โทรออก, ผลการโทร, เบอร์ |
| ชื่อ agent ที่จะใช้ | หา agent id เองด้วย GET /agents แล้วใช้กับแคมเปญ | แอดมิน: วอยซ์เอเจนต์ → Agents | โทรออก |
| รายชื่อข้อมูลที่อยากได้หลังวางสาย | ให้แอดมินตั้ง Variables to Extract ใน agent ด้วยชื่อ field ที่ตกลงกัน ค่าจะมาใน collected | แอดมิน: agent → ตัวแปรที่ต้องเก็บ (Variables to Extract) | ผลการโทร |
| ลิงก์โทรบนเว็บ (embed key) | ฝัง iframe หรือเปิดใน WebView (ข้อ 2) | แอดมิน: agent → แชร์ → คัดลอกลิงก์/โค้ดฝัง | ปุ่มโทรบนเว็บ |
| เพดานสายบนเว็บ | จำนวนสายต่อชั่วโมงต่อ IP และความยาวสาย (ค่าเริ่มต้น 20 สาย, 5 นาที) | แอดมิน: ตั้งค่าวอยซ์เอเจนต์ | ปุ่มโทรบนเว็บ |
| เบอร์ที่ AI รับสาย | เบอร์ต้องต่อเข้าระบบโทรศัพท์ของ AgenticOS ก่อน แล้วแอดมินผูกเบอร์กับ agent | ทีม AgenticOS (เชื่อมเบอร์) + แอดมิน (ผูก agent) | รับสายเข้า |
| หมายเลขที่ขึ้นตอนโทรออก | เบอร์ที่ลูกค้าเห็นตอน AI โทรไป ระบุเองไม่ได้ ต้องเปิดเส้นทางให้ก่อน | ทีม AgenticOS (เปิดเส้นทาง) + แอดมิน (เลือกในตั้งค่า) | โทรออก |
| เวลาทำการ | นอกเวลาบอทจะแจ้งปิดทำการ ต้องรู้เพื่อสั่งเริ่มแคมเปญในเวลาทำการ | แอดมิน: ตั้งค่าวอยซ์เอเจนต์ → เวลาทำการ | โทรออก |
| แพ็กเกจที่ใช้งานได้ | ถ้าช่วงทดลองหมดอายุ ระบบจะไม่รับหรือโทรออก | owner ขององค์กร | ทุกอย่าง |
สวัสดีครับ ทีมพัฒนาจะเชื่อมระบบกับวอยซ์เอเจนต์ของ AgenticOS รบกวนขอดังนี้ครับ
1. บัญชีผู้ใช้สำหรับเชื่อมระบบ บทบาท admin อีเมล: integration@<บริษัท> — ส่งรหัสผ่านผ่าน password manager
2. ชื่อ agent ที่จะใช้โทรออก / รับสาย
3. ตั้ง "ตัวแปรที่ต้องเก็บ (Variables to Extract)" ใน agent ตามนี้: customer_name, province, budget_thb, interested, callback_time
4. ลิงก์โทรบนเว็บของ agent (แชร์ → คัดลอกลิงก์) ถ้าจะฝังบนเว็บ/แอป
5. เวลาทำการ และเพดานสายบนเว็บที่ตั้งไว้
6. (ถ้าให้ AI รับสายเข้า) เบอร์ที่จะใช้ — ขอทีม AgenticOS เชื่อมเบอร์เข้าระบบ และหมายเลขที่จะขึ้นตอนโทรออก2. ฝังปุ่มคุยด้วยเสียงบนเว็บไซต์หรือแอป
แต่ละ agent มีหน้าโทรสำเร็จรูป เปิดในเบราว์เซอร์แล้วกดคุยได้ทันที ไม่ต้องเขียนโค้ด WebRTC เอง ใช้ได้ทั้งแชร์เป็นลิงก์ ฝังเป็น iframe บนเว็บ หรือเปิดใน WebView ของแอป
ขอลิงก์
- ในแอป: วอยซ์เอเจนต์ → เลือก agent → แชร์ → คัดลอกลิงก์หรือโค้ดฝัง
- หรือผ่าน API (owner/admin):
POST /agents/{agent_id}/embed-keyคืน{ "embedKey": "emb_…", "url": "https://app.agenticos.tech/v/emb_…" }เรียกซ้ำได้ค่าเดิม
<iframe
src="https://app.agenticos.tech/v/emb_XXXXXXXX?theme=light"
width="400" height="640"
style="border:0;border-radius:16px"
allow="microphone"
title="Voice assistant"></iframe>allow="microphone"จำเป็น ไม่งั้นเบราว์เซอร์ไม่ให้ใช้ไมค์?theme=dark·light·aurora·minimal(ค่าเริ่มต้น dark)- หน้าโทรแสดงข้อความขออนุญาตบันทึกเสียงก่อนเริ่ม และตอนนี้ข้อความบนหน้าเป็นภาษาไทย
ใช้ในแอปมือถือผ่าน WebView
เปิดลิงก์ /v/<embed key> ใน WebView ได้เลย สิ่งที่ต้องทำเพิ่มคือให้ WebView ใช้ไมโครโฟนและเล่นเสียงได้ ถ้าไม่ทำ กดปุ่มโทรแล้วจะเงียบหรือขึ้นว่าไม่ได้รับอนุญาต
| เรื่อง | iOS (WKWebView) | Android (WebView) |
|---|---|---|
| สิทธิ์ไมโครโฟนของแอป | NSMicrophoneUsageDescription | RECORD_AUDIO · MODIFY_AUDIO_SETTINGS และขอสิทธิ์ตอนใช้งาน |
| ให้หน้าเว็บใช้ไมค์ | iOS 15+: requestMediaCapturePermissionFor ใน WKUIDelegate ตอบ .grant | onPermissionRequest ใน WebChromeClient ตอบ grant RESOURCE_AUDIO_CAPTURE |
| เล่นเสียงของ AI | allowsInlineMediaPlayback = true | mediaPlaybackRequiresUserGesture = false |
| อื่น ๆ | iOS 14.3 ขึ้นไป (WebRTC ใน WKWebView) | javaScriptEnabled = true · domStorageEnabled = true |
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
config.mediaTypesRequiringUserActionForPlayback = []
let webView = WKWebView(frame: .zero, configuration: config)
webView.uiDelegate = self
webView.load(URLRequest(url: URL(string: "https://app.agenticos.tech/v/emb_XXXXXXXX?theme=light")!))
// iOS 15+: let the call page use the microphone (only for our host)
func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin,
initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType,
decisionHandler: @escaping (WKPermissionDecision) -> Void) {
decisionHandler(origin.host.hasSuffix("agenticos.tech") ? .grant : .deny)
}
// Info.plist: NSMicrophoneUsageDescription = "ใช้ไมโครโฟนเพื่อคุยกับผู้ช่วย AI"// AndroidManifest.xml
// <uses-permission android:name="android.permission.RECORD_AUDIO" />
// <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
// Ask RECORD_AUDIO at runtime first (ActivityResultContracts.RequestPermission), then:
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.settings.mediaPlaybackRequiresUserGesture = false
webView.webChromeClient = object : WebChromeClient() {
override fun onPermissionRequest(request: PermissionRequest) {
val ours = request.origin.host?.endsWith("agenticos.tech") == true
if (ours && PermissionRequest.RESOURCE_AUDIO_CAPTURE in request.resources) {
request.grant(arrayOf(PermissionRequest.RESOURCE_AUDIO_CAPTURE))
} else {
request.deny()
}
}
}
webView.loadUrl("https://app.agenticos.tech/v/emb_XXXXXXXX?theme=light")- Flutter: ใช้ flutter_inappwebview แล้วตอบ grant ใน onPermissionRequest พร้อมขอสิทธิ์ไมค์ของแอป
- React Native: ใช้ react-native-webview, ขอสิทธิ์ไมค์ของแอปก่อน และบน iOS ตั้ง mediaCapturePermissionGrantType="grant"
- ยังส่งข้อมูลลูกค้า (เช่นชื่อหรือรหัสสมาชิก) เข้าไปในสายผ่านลิงก์ไม่ได้ ทุกสายจากหน้านี้เป็นผู้โทรไม่ระบุตัวตน
ความปลอดภัยของลิงก์
- embed key เป็นค่าสาธารณะ ใครมีลิงก์ก็โทรได้ ระบบจึงจำกัดจำนวนสายและความยาวสายไว้ (ดูข้อ 8)
- ลิงก์หลุดหรือถูกใช้ผิด:
POST /agents/{agent_id}/embed-key/rotateได้ key ใหม่ และลิงก์เดิมใช้ไม่ได้ทันที - เปลี่ยน key ไม่กระทบ agent ประวัติการโทร หรือเบอร์โทร
3. ยืนยันตัวตนสำหรับ API จัดการระบบ (JWT)
API ส่วนโทรออก ผลการโทร และเบอร์ ใช้ access token (JWT) ของผู้ใช้ในองค์กร ตอนนี้ยังไม่มี API key แยก แนะนำให้สร้างผู้ใช้สำหรับเชื่อมระบบโดยเฉพาะ (บทบาท admin) แล้วให้เซิร์ฟเวอร์ของคุณ login ด้วยบัญชีนั้น
POST/api/v1/auth/login
curl -s https://api.agenticos.tech/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"••••••••"}'
# → { "access_token": "eyJ…", "token_type": "bearer" }- ส่งทุกคำขอด้วย header
Authorization: Bearer <access_token> - token อายุ 24 ชั่วโมง ยังไม่มี refresh token ให้ login ใหม่เมื่อได้ 401
- ผู้ใช้อยู่หลายองค์กร: token ผูกกับองค์กรแรก เปลี่ยนด้วย
POST /api/v1/auth/switch-tenant{"tenant_id": "<uuid>"} - เก็บรหัสผ่านและ token ไว้บนเซิร์ฟเวอร์เท่านั้น ห้ามใส่ในแอปหรือหน้าเว็บ
สิทธิ์: อ่านข้อมูลต้องมีสิทธิ์ใช้โมดูลวอยซ์เอเจนต์ ส่วนสร้าง/แก้ agent แคมเปญ เบอร์ และรายชื่อ ต้องเป็น owner หรือ admin
4. ให้ AI โทรออกด้วยแคมเปญ
AI โทรออกผ่านแคมเปญเท่านั้น ถ้าต้องการโทรหาคนเดียว ให้สร้างแคมเปญที่มี 1 รายชื่อ ระบบหยิบรายชื่อไปโทรทุก ~8 วินาทีขณะแคมเปญอยู่ในสถานะ running
- สร้างแคมเปญ (สถานะ draft)
- เพิ่มรายชื่อ
- สั่งเริ่ม: เปลี่ยนสถานะเป็น running
- ติดตามผลรายชื่อ และดึงผลการโทร (ข้อ 5)
POST/api/v1/voice-agent/campaigns
{
"name": "Follow up · September leads",
"agentId": "<voice agent uuid>",
"objective": "Call people who asked about the starter package. Ask permission for 2 minutes, answer questions, and book a site survey.",
"maxConcurrent": 1
}| field | รายละเอียด |
|---|---|
name | 1–200 ตัวอักษร (บังคับ) |
agentId | agent ที่จะโทร ต้องมี ไม่งั้นระบบไม่โทร |
objective | เป้าหมาย/สคริปต์ของแคมเปญ ≤4000 ตัวอักษร ใส่ในการเปิดสายทุกสาย |
maxConcurrent | จำนวนสายพร้อมกัน 1–5 (ค่าเริ่มต้น 2) |
status | draft · running · paused · done · scheduled |
POST/api/v1/voice-agent/campaigns/{id}/contacts
{
"contacts": [
{ "phone": "0812345678", "name": "Somchai" },
{ "phone": "+66898765432", "name": "Malee" }
]
}
# → 201 { "added": 2, "skipped": 0, "total": 2 }- สูงสุด 5,000 รายชื่อต่อครั้ง เบอร์ซ้ำ (ในแคมเปญหรือในชุดเดียวกัน) จะถูกข้าม
- เบอร์ 3–32 ตัว ระบบเก็บเฉพาะตัวเลขและ + นำหน้า เบอร์ +66/66 จะโทรเป็น 0…
- ยังไม่มีอัปโหลด CSV ผ่าน API ให้แปลงเป็น JSON ก่อนส่ง
PATCH/api/v1/voice-agent/campaigns/{id}
เริ่มและหยุดแคมเปญด้วย {"status": "running"} / {"status": "paused"}
GET/api/v1/voice-agent/campaigns/{id}/contacts
{
"contacts": [
{ "id": "…", "phone": "0812345678", "name": "Somchai", "status": "connected", "attempts": 1, "callId": "…", "updatedAt": "…" }
],
"counts": { "connected": 1, "no_answer": 1 }
}| สถานะรายชื่อ | ความหมาย |
|---|---|
pending | รอโทร |
calling | กำลังโทร |
connected | รับสายและคุยแล้ว ดูผลที่ callId |
no_answer | ไม่รับสายภายใน 40 วินาที |
failed | สายไม่ว่างหรือถูกปฏิเสธ |
voicemail | เจอระบบฝากข้อความ |
- ไม่มีการโทรซ้ำอัตโนมัติ สั่งโทรซ้ำด้วย
POST /campaigns/{id}/contacts/retry-failed(no_answer, failed, voicemail ทั้งหมด) หรือPOST /campaigns/{id}/contacts/{contact_id}/retry - แคมเปญเปลี่ยนเป็น done เองเมื่อไม่มีรายชื่อรอโทร
5. ผลการโทร: บทสนทนา สรุป และข้อมูลที่ AI เก็บได้
GET/api/v1/voice-agent/calls/{id}
{
"id": "c9a3cb03-…",
"direction": "outbound",
"channel": "phone",
"status": "completed",
"phone": "0812345678",
"agentId": "…",
"startedAt": "2026-09-30T03:51:42Z",
"durationSec": 209,
"intent": "booked_site_survey",
"summary": {
"text": "The customer is interested in the starter package…",
"keyPoints": ["Has 40 m² in Nakhon Pathom", "Budget about 1M"],
"outcome": "booked_site_survey",
"nextAction": "Sales to call back Friday morning",
"sentiment": "positive",
"usage": { "totalThb": 4.82, "breakdown": [ … ] }
},
"collected": { "province": "Nakhon Pathom", "space_sqm": 40, "budget_thb": 1000000, "consent": true },
"recordingUrl": "https://…(valid for 1 hour)",
"transcript": [
{ "seq": 1, "who": "agent", "text": "Hello, this is …", "atOffset": "00:01" },
{ "seq": 2, "who": "customer", "text": "Yes, go ahead", "atOffset": "00:06" }
]
}| field | ความหมาย |
|---|---|
collected | ข้อมูลที่ AI ดึงจากบทสนทนาตอนวางสาย ตามตัวแปรที่ตั้งไว้ใน agent (Variables to Extract) ชนิด string, number, boolean, date, email, phone |
summary | สรุปการโทร จุดสำคัญ ผลลัพธ์ สิ่งที่ต้องทำต่อ อารมณ์ และค่าใช้จ่าย AI |
transcript | บทสนทนาทีละประโยค who เป็น agent หรือ customer |
recordingUrl | ไฟล์เสียง ลิงก์ใช้ได้ 1 ชั่วโมง ขอใหม่ได้โดยเรียก endpoint นี้ซ้ำ |
status | completed · voicemail |
GET/api/v1/voice-agent/collected
รายการเฉพาะสายที่มีข้อมูลที่ AI เก็บได้: [{ "callId", "agentId", "caller", "phone", "channel", "startedAt", "collected" }]
GET/api/v1/voice-agent/calls
ทุกสายเรียงจากใหม่ไปเก่า ตอนนี้ยังไม่มีการแบ่งหน้าหรือตัวกรอง ให้เก็บ id ที่ประมวลผลแล้วฝั่งคุณ และดึงรายละเอียดด้วย /calls/{id}
# Python — pull new captured leads every few minutes
import requests
H = {"Authorization": f"Bearer {token}"}
rows = requests.get("https://api.agenticos.tech/api/v1/voice-agent/collected", headers=H, timeout=30).json()
for r in rows:
if r["callId"] in seen:
continue
save_lead(r["phone"], r["collected"]) # your CRM
seen.add(r["callId"])6. ติดตามสายที่กำลังคุย
GET /liveสายที่กำลังคุยตอนนี้GET /live/streamServer-Sent Events: snapshot, start, turn (ข้อความทีละประโยค), end ส่ง ping ทุก 15 วินาที- EventSource ของเบราว์เซอร์ใส่ header Authorization ไม่ได้ ให้ใช้ fetch แล้วอ่าน stream เอง
7. เบอร์โทรและการรับสายเข้า
ให้ AI รับสายเข้าเบอร์บริษัท: เพิ่มเบอร์แล้วผูกกับ agent เบอร์ต้องต่อเข้าระบบโทรศัพท์ของ AgenticOS อยู่แล้ว (ติดต่อทีมงานเพื่อเชื่อมเบอร์) การเพิ่มเบอร์ผ่าน API ไม่ได้เปิดเบอร์ใหม่ให้
POST https://api.agenticos.tech/api/v1/voice-agent/numbers
{ "label": "Head office", "number": "021234567", "agentId": "<voice agent uuid>", "status": "connected" }- สายเข้าจับคู่กับ agent ด้วยตัวเลขของเบอร์ (ไม่สนขีดหรือเว้นวรรค)
- เปลี่ยน agent ที่รับสาย:
PATCH /numbers/{id}{"agentId": "…"} - หมายเลขที่ขึ้นปลายทางตอนโทรออก เลือกได้จากเส้นทางที่ระบบเปิดไว้ (GET /settings → availableOutboundRoutes) ระบุหมายเลขเองไม่ได้
8. ข้อจำกัด
| เรื่อง | ค่า |
|---|---|
| หน้าโทรบนเว็บ: จำนวนสายต่อชั่วโมง | ตั้งได้ในองค์กร ค่าเริ่มต้น 20 สาย/ชั่วโมง ต่อ IP (0 = ไม่จำกัด) |
| หน้าโทรบนเว็บ: ความยาวสาย | ค่าเริ่มต้น 5 นาที (ตั้งได้ 30 วินาที–60 นาที) |
| สายโทรออกจากแคมเปญ | ยาวสุด 10 นาทีต่อสาย |
| สายพร้อมกันต่อแคมเปญ | 1–5 |
| รอสายก่อนนับว่าไม่รับ | 40 วินาที |
| สายนอกเวลาทำการ | แจ้งปิดทำการ ยาวสุด 45 วินาที |
| รายชื่อต่อคำขอ | 5,000 |
| อายุ access token | 24 ชั่วโมง |
9. ยังไม่รองรับในตอนนี้
เขียนไว้ให้ชัดเพื่อวางแผนการเชื่อมระบบได้ถูก ถ้าต้องการข้อไหน แจ้งทีมงานได้
- API key สำหรับเซิร์ฟเวอร์ (ตอนนี้ใช้ login + JWT)
- webhook แจ้งเมื่อวางสายหรือได้ข้อมูลลูกค้า (ตอนนี้ดึงเป็นรอบ)
- endpoint สั่งโทรออกทันทีทีละเบอร์ (ตอนนี้ใช้แคมเปญ 1 รายชื่อ)
- การแบ่งหน้าและตัวกรองของ /calls
- ส่งข้อมูลลูกค้าเข้าไปในสายผ่านลิงก์โทรบนเว็บ (ตอนนี้รองรับแค่ ?theme=)
- เริ่มแคมเปญตามเวลาที่ตั้งไว้ และโทรซ้ำอัตโนมัติ
ดู endpoint ทั้งหมดได้ที่ Swagger: https://api.agenticos.tech/docs