← บทความทั้งหมด
บทช่วยสอน

เชื่อมต่อการโทรเสียง 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 หรือลิงก์การโทรส่วนตัว หลังเปิดสิทธิ์แล้ว ให้ทำตามเอกสารสร้างการโทร เพื่อทดสอบบทสนทนาที่เลือกหนึ่งรายการ ก่อนนำปุ่มโทรไปใช้ในกระบวนการบริการลูกค้า

UnifyPort API

เปลี่ยนการเชื่อมต่อข้อความให้เป็น pipeline ผลิตภัณฑ์ที่เสถียร

เริ่มจากการส่งผ่าน API เดียว แล้วส่งข้อความขาเข้าทั้งหมดกลับสู่ระบบธุรกิจของคุณด้วย event มาตรฐาน