ยืนยันตัวตน / OTP
มีสองแบบ ยืนยันด้วย API key ทั้งคู่
SMS OTP ดูแลครบวงจร ตั้งแต่สร้างรหัส ส่ง ไปจนถึงตรวจสอบ
| การทำงาน | Route |
|---|---|
| ส่งรหัส | POST /sms/otp |
| ตรวจสอบรหัส | POST /sms/otp/{otpId}/verify |
| ส่งรหัสอีกครั้ง | POST /sms/otp/{otpId}/resend |
| อ่านสถานะ | GET /sms/otp/{otpId} |
ตรวจสอบอย่างเดียว ใช้กับรหัสที่ระบบสร้างจากที่อื่น เช่นตอนสมัครสมาชิก โดยอ้างอิงด้วย ref_code ไม่ใช่ session id
| การทำงาน | Route |
|---|---|
| ตรวจสอบรหัส | POST /otp/verify |
เลือกแบบไหน
ถ้าเริ่มจากศูนย์ ใช้ POST /sms/otp ส่วน /otp/verify ใช้เมื่อมีระบบอื่นสร้างรหัสไว้แล้ว และส่ง ref_code มาให้ — รหัสที่คุณสร้างเองและส่งผ่าน SMS ของคุณเอง ตรวจสอบที่นั่นไม่ได้ เพราะระบบหาไม่เจอ
ส่ง OTP
curl -X POST "$ME_BASE/sms/otp" \
-H "API-Key: $ME_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"mode": "auto",
"operator": "smart",
"sender_name": "YourBrand",
"recipient": "+66886578553",
"config": { "length": 6, "type": "numeric", "validity_seconds": 300 }
}'{
"id": "0f3c…",
"mode": "auto",
"recipient": "+66886578553",
"ref_code": "A1B2",
"expires_at": "2026-09-19T04:21:00Z",
"status": "sent",
"message_id": "tx-…"
}เก็บ id ไว้ ใช้อ้างอิงตอนตรวจสอบ ส่งซ้ำ และอ่านสถานะ ส่วนที่แสดงให้ผู้ใช้เห็นคือ ref_code — ตัวรหัสจะไม่ถูกส่งกลับมา ระบบเก็บไว้เป็นค่าแฮชเท่านั้น จึงไม่มีทางดึงกลับได้
| ฟิลด์ | หมายเหตุ |
|---|---|
mode | auto (ระบบสร้างและตรวจสอบให้) หรือ provided (คุณสร้างเอง ระบบส่งให้อย่างเดียว) |
operator | ais, dtac, true_corp หรือ smart ให้ระบบเลือกเส้นทางให้ |
sender_name | ชื่อผู้ส่งที่องค์กรคุณได้รับอนุมัติ ดูรายละเอียดด้านล่าง |
recipient | รูปแบบ E.164 |
message_template | ต้องมี ถ้าไม่ส่งมา ระบบใช้ข้อความมาตรฐาน |
message_template_id | id ของเทมเพลตที่คุณบันทึกไว้ ใช้พร้อม message_template ไม่ได้ |
config | เฉพาะ auto — length 4–10, type, validity_seconds 1–3600 |
otp / ref_code | เฉพาะ provided — รหัสและเลขอ้างอิงของคุณเอง |
sender_name ใช้ข้อตกลงเดียวกับ SMS ชื่อที่ยังไม่ได้รับอนุมัติ จะถูกปฏิเสธด้วย 403 ไม่ใช่เปลี่ยนให้เงียบๆ ดูรายชื่อที่ใช้ได้จาก GET /organization/{organizationId}/sender-names — route นั้นใช้ token ของผู้ใช้ ไม่ใช่ API-Key ดู ชื่อผู้ส่ง
ในรายการนั้นจะมีชื่อกลางของแพลตฟอร์มรวมอยู่ด้วย สังเกตจาก is_platform_default: true ซึ่งทุกองค์กรส่งได้โดยไม่ต้องขออนุมัติเอง แต่ชื่อนี้มีเฉพาะเมื่อผู้ดูแลระบบตั้งค่าไว้ จึงควรอ่านจากรายการจริง ไม่ใช่สมมติว่ามี
เทมเพลตข้อความ
เป็นตัวบังคับ ข้อความที่ไม่มีตัวนี้จะคิดค่าส่ง SMS ทั้งที่ผู้ใช้ทำอะไรต่อไม่ได้ ส่วน ใส่หรือไม่ใส่ก็ได้
// เขียนตรงๆ
{ "message_template": "{{otp}} คือรหัส OTP ของคุณ Ref: {{ref_code}}" }
// หรืออ้างด้วย id จาก /template-message/organization/{organizationId}
{ "message_template_id": "8f1c…" }ส่งมาทั้งคู่จะถูกปฏิเสธด้วย 400 เพราะค่าเดียวที่มีสองแหล่งทำให้ไม่รู้ว่าคุณตั้งใจแบบไหน และการเดาจะส่งข้อความที่คุณไม่ได้เขียนออกไป
id ของเทมเพลตจะถูกแปลงเป็นข้อความก่อนประกอบเป็น SMS การแก้เทมเพลตทีหลัง จึงไม่กระทบข้อความที่ส่งไปแล้ว รวมถึงการส่งซ้ำ ซึ่งใช้ข้อความ ณ ตอนส่งครั้งแรก
ส่งซ้ำอย่างปลอดภัย
ใส่ Idempotency-Key แล้วการยิงซ้ำจะได้ OTP เดิมกลับมา ไม่ใช่รหัสใหม่ การอ้างเทมเพลตด้วย id กับการพิมพ์ข้อความเดียวกันตรงๆ นับเป็นคำขอเดียวกัน เพราะ key ครอบข้อความที่แปลงแล้ว
ถ้าใช้ key เดิมกับเนื้อหาที่ต่างกันจริงจะถูกปฏิเสธ — การคืนผลลัพธ์เดิมในกรณีนั้น เท่ากับบอกว่ามีรหัสถูกส่งออกไปแล้วทั้งที่ไม่เคยส่ง
ตรวจสอบ OTP จาก SMS OTP
curl -X POST "$ME_BASE/sms/otp/$OTP_ID/verify" \
-H "API-Key: $ME_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "otp": "884213" }'{ "id": "0f3c…", "verified": false, "status": "pending", "attempts_remaining": 2 }รหัสผิดได้ 200 ไม่ใช่ 4xx
คำขอถูกต้องดีอยู่ แค่รหัสผิดเท่านั้น ให้ดูที่ verified ไม่ใช่ status code
attempts_remaining จะหายไปเมื่อ session ถูกล็อก ซึ่งไม่เท่ากับ 0 หน้าจอที่แสดง "เหลือ N ครั้ง" ควรไม่แสดงอะไรเลยในกรณีนั้น
ตรวจสอบรหัสที่สร้างจากที่อื่น
curl -X POST "$ME_BASE/otp/verify" \
-H "API-Key: $ME_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+66886578553",
"code": "884213",
"ref_code": "A1B2",
"purpose": "signup"
}'| ฟิลด์ | หมายเหตุ |
|---|---|
phone_number | รูปแบบ E.164 |
code | รหัสที่ผู้ใช้กรอก |
ref_code | รหัสอ้างอิงที่ได้ตอนสร้างรหัส |
email | ไม่บังคับ |
purpose | ขอบเขตการใช้สิทธิ์ เช่น signup, welcome_credit |
{
"is_valid": true,
"status": "verified",
"message": "OK",
"is_new": true
}is_new อาจไม่มีค่า ซึ่งไม่เท่ากับ false
is_new ตอบคำถามว่า "หมายเลขนี้ใช้สิทธิ์สำหรับ purpose นี้เป็นครั้งแรกหรือไม่" ค่านี้จะไม่ถูกส่งมาเมื่อไม่มีอะไรให้ตอบ คือไม่ได้ส่ง purpose มา หรือระบบบันทึกสิทธิ์ติดต่อไม่ได้
ความต่างนี้สำคัญมากถ้าคุณใช้ค่านี้ตัดสินการให้สิทธิ์
// ถูกต้อง: ถ้าไม่มีค่า ให้ระงับไว้ ไม่ใช่จ่ายสิทธิ์ทั้งที่ยังไม่รู้
if (res.is_new === true) grantWelcomeCredit();การถือว่าไม่มีค่าเท่ากับ false นั้นปลอดภัย แต่ถ้าเขียนกลับด้าน (if (!res.is_new)) จะกลายเป็นจ่ายสิทธิ์ทุกครั้งที่เกิดข้อผิดพลาด แทนที่จะไม่จ่ายเลย
API key เป็นตัวระบุโปรเจกต์
Key จะ resolve ไปยังโปรเจกต์เดียว และการใช้สิทธิ์จะถูกบันทึกกับโปรเจกต์นั้น key ที่ resolve ไม่ได้จะถูกปฏิเสธด้วย 401 — ดู การยืนยันตัวตน
