เชื่อมต่อการโทรเสียง WhatsApp กับระบบบริการลูกค้า ตั้งแต่สร้างลิงก์ถึงรับ Webhook
การเพิ่มการโทรเสียง WhatsApp ผ่าน UnifyPort เริ่มจากแบ็กเอนด์เตรียมสายด้วย media_type: "voice" แล้วส่งลิงก์เบราว์เซอร์ที่เป็นส่วนตัวให้เจ้าหน้าที่ผู้รับผิดชอบ ระบบจะเริ่มโทรเมื่อเจ้าหน้าที่เปิดลิงก์และกด Start call จากนั้นแบ็กเอนด์ผูก call_id ที่ได้รับกับงานบริการลูกค้า และติดตามผลผ่านเหตุการณ์การโทร
บทความนี้ครอบคลุม การโทรออกแบบตัวต่อตัวที่เจ้าหน้าที่เป็นผู้เริ่มในเบตาแบบปิด UnifyPort ใช้อินเทอร์เฟซที่ไม่เป็นทางการ เนื้อหานี้จึงไม่ใช่คู่มือ Meta WhatsApp Business Calling API อ่านภาพรวมและวิธีสมัครได้ในประกาศเบตาการโทร
1. เตรียมบัญชีและเลือกบทสนทนา
ติดต่อฝ่ายสนับสนุนทางเทคนิค เพื่อขอสิทธิ์การโทรให้เวิร์กสเปซ การสร้าง API key ไม่ได้เปิดสิทธิ์โดยอัตโนมัติ is_beta เป็นแฟล็กสิทธิ์แบบอ่านอย่างเดียว ไม่ใช่ค่าที่เปิดเองผ่าน metadata ของเวิร์กสเปซได้
ก่อนแสดงปุ่มโทร ให้ตรวจสอบว่าบัญชีรับส่งข้อความ WhatsApp ได้รับอนุญาตแล้ว และ runtime_status เป็น running เลือกบทสนทนาส่วนตัวจากบัญชีเดียวกัน และเก็บ conversation_id ที่ API ส่งกลับตามเดิม ห้ามแทนด้วยหมายเลขโทรศัพท์หรือประกอบรหัสผู้ให้บริการขึ้นเอง
ในแอป ให้ผูกเวิร์กสเปซ บัญชี บทสนทนา และงานบริการที่เลือกกับการกระทำครั้งนี้ พร้อมตรวจสิทธิ์เจ้าหน้าที่ที่แบ็กเอนด์ ข้อความใหม่หรือการเปลี่ยนบทสนทนาต้องไม่เปลี่ยนผู้รับของสายที่เตรียมไว้โดยไม่แจ้ง
ขั้นตอนต่อไปนี้เก็บ API key ไว้ที่เซิร์ฟเวอร์ เบราว์เซอร์ได้รับเฉพาะลิงก์ส่วนตัวที่จำเป็นต่อการโทรครั้งนั้น
2. เตรียมสายโทรเสียงที่แบ็กเอนด์
แทนค่าตัวอย่างด้วย API key ของเวิร์กสเปซ บัญชีที่เชื่อมต่อแล้ว และบทสนทนาที่เลือก จากนั้นรันที่เซิร์ฟเวอร์:
curl -X POST 'https://api.unifyport.ai/v1/calls' \
-H 'X-Api-Key: <YOUR_API_KEY>' \
-H 'Content-Type: application/json' \
-d '{
"account_id": "acc_example",
"to": { "id": "<conversation_id>", "type": "user" },
"media_type": "voice"
}'
เอกสารสร้างการโทร ระบุว่าเมื่อสำเร็จจะได้ 201 Created พร้อมรูปแบบการตอบกลับดังนี้ ค่าทั้งหมดเป็นตัวอย่าง ไม่ใช่หลักฐานจากการโทรจริง:
{
"request_id": "req_example",
"data": {
"call_id": "call_example",
"direction": "outbound",
"media_type": "voice",
"state": "preparing",
"join_url": "https://calls.example.com/call/call_example#ticket=example-only",
"expires_at": "2026-10-27T08:02:00Z"
}
}
บันทึก data.call_id คู่กับบัญชี บทสนทนา และงานที่เลือก และเก็บ request_id ไว้ตรวจสอบปัญหา ให้ถือว่า data.join_url เป็นข้อมูลลับ ส่งต่อค่าเดิมให้เจ้าหน้าที่ผู้รับผิดชอบเท่านั้น ห้ามให้ URL เต็มเข้าไปในบันทึก ระบบวิเคราะห์ หรือข้อความสาธารณะ
201 และ preparing หมายถึงลิงก์พร้อมแล้ว ไม่ได้หมายความว่าโทรศัพท์ลูกค้ากำลังดัง หรือลูกค้ารับสายแล้ว
3. ให้เจ้าหน้าที่เริ่มโทรในเบราว์เซอร์
แสดงลูกค้าที่เลือกและสถานะ “พร้อมเริ่มโทร” ในหน้าระบบงาน ให้ join_url เฉพาะเจ้าหน้าที่ที่เกี่ยวข้อง เมื่อเปิดหน้าการโทร เจ้าหน้าที่ต้องอนุญาตให้ใช้ไมโครโฟนและกด Start call ก่อน expires_at
ช่วงเตรียมสายมีค่าเริ่มต้นสองนาทีนับจากการสร้าง แต่เซิร์ฟเวอร์ปรับได้ จึงต้องอ่าน expires_at แทนการคำนวณเวลาหมดอายุแบบตายตัว ช่วงเวลานี้ไม่ได้จำกัดความยาวของสายที่เชื่อมต่อแล้ว ต้องมีสิทธิ์ใช้ไมโครโฟน ส่วนสิทธิ์ใช้กล้องเป็นทางเลือก
ป้องกันการส่งซ้ำระหว่างรอคำตอบจากการสร้างสาย หากคำตอบสูญหาย ให้แสดงว่าผลยังไม่ชัดเจนและสอบถามสายปัจจุบันก่อนพิจารณาสร้างใหม่ การปิดปุ่มเพียงอย่างเดียวไม่สามารถประสานเจ้าหน้าที่หลายคนได้ แบ็กเอนด์จึงต้องจัดการความขัดแย้งระดับบัญชีด้วย
4. ผูก Webhook กับงานบริการลูกค้า
หาก endpoint ของ Webhook ระบุรายการเหตุการณ์โดยตรง ให้เพิ่มสองชื่อต่อไปนี้โดยเก็บการสมัครรับเหตุการณ์อื่นที่ยังต้องใช้ไว้ด้วย นี่เป็นเพียงส่วนของฟิลด์ ไม่ใช่คำขอสร้าง endpoint ทั้งชุด:
{
"subscribed_events": ["call.answered", "call.ended"]
}
ในเวิร์กสเปซที่มีสิทธิ์เบตา การใช้ * รายการว่าง หรือไม่ส่งรายการ จะรวมเหตุการณ์การโทรที่มีสิทธิ์รับด้วย การสมัครแบบ wildcard ไม่ได้ให้สิทธิ์เหตุการณ์การโทรแก่เวิร์กสเปซที่อยู่นอกเบตา
| ข้อมูลที่ได้รับ | การทำงานของแอป |
|---|---|
data.call_id จากคำตอบการสร้าง | ผูกสายกับงานบริการที่เลือก |
data.call.id ใน call.answered | บันทึกว่าสายนี้เชื่อมต่อแล้ว |
data.call.id, data.reason, data.duration_seconds ใน call.ended | บันทึกเหตุผลที่จบสายและระยะเวลาที่เชื่อมต่อ |
ก่อนประมวลผลเหตุการณ์ที่มีลายเซ็น ให้ตั้งค่า signing_secret ที่ endpoint และเก็บความลับนี้ไว้บนเซิร์ฟเวอร์
ตรวจสอบลายเซ็นด้วยเนื้อหาคำขอดิบ และตัดเหตุการณ์ซ้ำด้วย id ใน envelope ตามข้อกำหนดการส่ง Webhook จับคู่ account_id และ data.call.id กับความสัมพันธ์ที่บันทึกไว้ เหตุการณ์ที่ล่าช้าหรือซ้ำต้องไม่ทำให้สายที่จบแล้วกลับมาแสดงว่ากำลังสนทนา
เก็บหลักฐานการรับสายแยกไว้ด้วย เพราะ duration_seconds ปัดเศษลง ค่า 0 เพียงอย่างเดียวจึงพิสูจน์ไม่ได้ว่าไม่มีการรับสาย ความล้มเหลวแบบ synchronous ตอนสร้างอาจไม่มีเหตุการณ์จบสาย ต้องบันทึกข้อผิดพลาด HTTP แยกกัน ดู payload เต็มได้ในเอกสารเหตุการณ์รับสาย และเหตุการณ์จบสาย การจบสายไม่ได้หมายความว่างานบริการลูกค้าได้รับการแก้ไขโดยอัตโนมัติ
5. สอบถามหรือจบสายที่ระบบจัดการ
เมื่อต้องกู้สถานะหรือค้นหารหัสสายที่สูญหาย ให้ใช้endpoint สอบถามสายปัจจุบัน:
curl --get 'https://api.unifyport.ai/v1/calls/active' \
-H 'X-Api-Key: <YOUR_API_KEY>' \
--data-urlencode 'account_id=acc_example'
data: {} หมายถึงขณะนี้ไม่มีสายที่ UnifyPort จัดการสำหรับบัญชีนั้น ไม่ใช่ประวัติการโทร ไม่ยืนยันผลของความพยายามครั้งก่อน และไม่สามารถใช้ตรวจจับสายในแอป WhatsApp โดยตรงได้ คำตอบนี้ไม่ออก join_url แบบใช้ครั้งเดียวให้ใหม่
เมื่อเจ้าหน้าที่เลือกจบสายที่ระบุได้แล้ว ให้ใช้ call_id ที่บันทึกไว้และบัญชีเจ้าของสาย:
curl -X POST 'https://api.unifyport.ai/v1/calls/call_example/hangup' \
-H 'X-Api-Key: <YOUR_API_KEY>' \
-H 'Content-Type: application/json' \
-d '{"account_id":"acc_example"}'
endpoint วางสาย ส่งสถานะสุดท้าย ended หรือ failed การส่งคำขอซ้ำอาจได้สถานะสุดท้ายที่มีอยู่แล้ว อย่าสั่งวางสายซ้ำเพียงเพราะได้รับ failed และอย่าจบสายของเจ้าหน้าที่คนอื่นเพื่อให้สร้างสายใหม่ได้
ปัญหาที่พบบ่อยในการเชื่อมต่อ
| สถานการณ์ | ขั้นตอนถัดไป |
|---|---|
403 beta_feature_not_enabled | ให้ฝ่ายสนับสนุนตรวจสิทธิ์เวิร์กสเปซ การแก้ metadata เปิดสิทธิ์ไม่ได้ |
| ลิงก์เตรียมสายหมดอายุ | หยุดใช้ลิงก์เดิม ตรวจสายปัจจุบัน แล้วให้เจ้าหน้าที่ตัดสินใจอย่างชัดเจนว่าจะเตรียมใหม่หรือไม่ |
| เบราว์เซอร์ใช้ไมโครโฟนไม่ได้ | ตรวจสิทธิ์ไมโครโฟนของหน้าการโทรก่อนคาดหวังว่าจะโทรออกได้ |
| การสร้างขัดแย้งกับสายที่มีอยู่ | สอบถามสายที่ระบบจัดการ แล้วตรวจผู้รับผิดชอบและวัตถุประสงค์ในระบบงาน |
| คำขอสร้างหรือวางสายหมดเวลารอ | เก็บผลเป็นยังไม่ชัดเจน ตรวจเทียบสถานะปัจจุบันและเหตุการณ์ที่ได้รับก่อนตัดสินใจลองใหม่ |
ตรวจรับกระบวนการบริการลูกค้าให้ครบ
ใช้บทสนทนาทดสอบที่ได้รับความยินยอมเพื่อตรวจการเชื่อมต่อและวางสายตามปกติ ลิงก์หมดอายุ การปฏิเสธสิทธิ์ไมโครโฟน การกดซ้ำ ความขัดแย้งของบัญชี และคำตอบ HTTP ที่สูญหาย ตรวจว่าเหตุการณ์ที่ส่งซ้ำไม่สร้างบันทึกงานซ้ำ และ URL เต็มไม่หลุดเข้า log รายการเหล่านี้เป็นการตรวจรับที่ควรทำกับระบบของคุณ ไม่ใช่ผลทดสอบที่บทความอ้างว่าได้ดำเนินการแล้ว
โทรอัตโนมัติตามรายการหมายเลขได้ไหม?
ไม่ได้ ขั้นตอนนี้ต้องใช้ conversation_id ของบัญชี และให้คนกด Start call ในเบราว์เซอร์
การเชื่อมต่อเดียวกันใช้รับสายเข้าได้ไหม?
ยังไม่ได้ในเบตาปัจจุบัน call.incoming เป็นสัญญาที่สงวนไว้ การมีชื่อในรายการเหตุการณ์ไม่ได้เปิดการรับสายเข้า การโทรกลุ่ม บันทึกเสียง ถอดเสียง การเชื่อมต่อ SIP/PBX และเจ้าหน้าที่เสียง AI ก็อยู่นอกขอบเขตเบตานี้
ทีมควรเริ่มจากตรงไหน?
หากยังไม่มีสิทธิ์ ให้ติดต่อฝ่ายสนับสนุนทางเทคนิคเพื่อสมัคร พร้อม ID เวิร์กสเปซและรูปแบบงานบริการที่ต้องการใช้ อย่าส่ง API key หรือลิงก์การโทรส่วนตัว หลังเปิดสิทธิ์แล้ว ให้ทำตามเอกสารสร้างการโทร เพื่อทดสอบบทสนทนาที่เลือกหนึ่งรายการ ก่อนนำปุ่มโทรไปใช้ในกระบวนการบริการลูกค้า
เปลี่ยนการเชื่อมต่อข้อความให้เป็น pipeline ผลิตภัณฑ์ที่เสถียร
เริ่มจากการส่งผ่าน API เดียว แล้วส่งข้อความขาเข้าทั้งหมดกลับสู่ระบบธุรกิจของคุณด้วย event มาตรฐาน