Skip to content

Webhooks API

Webhook แจ้งเตือนแอปพลิเคชันของคุณแบบเรียลไทม์เมื่อมีเหตุการณ์เกิดขึ้นกับข้อมูลรับรองของคุณ — เมื่อมีการออก การนำส่ง การเพิกถอน หรือการเข้าชม แทนที่จะต้องคอยเรียก API (polling) คุณเพียงลงทะเบียนปลายทาง HTTPS แล้ว badges.ninja จะส่งคำขอ POST ที่ลงลายเซ็นไปยังปลายทางนั้นสำหรับทุกเหตุการณ์ที่คุณสมัครรับ

ปลายทางสำหรับการจัดการทั้งหมดต้องมีการยืนยันตัวตนผ่านส่วนหัว X-Api-Key ดูที่ การยืนยันตัวตน การสร้างหรือลบปลายทางต้องใช้คีย์ที่มีสิทธิ์ write ส่วนการแสดงรายการต้องใช้สิทธิ์ read ดูที่ คีย์ API

ลงทะเบียนปลายทาง

POST /webhooks

พารามิเตอร์

พารามิเตอร์ชนิดจำเป็นคำอธิบาย
urlstringใช่ปลายทาง HTTPS ของคุณ ต้องขึ้นต้นด้วย https://
eventsstring[]ไม่ชนิดเหตุการณ์ที่จะสมัครรับ หากละไว้หรือส่ง ["*"] จะได้รับทุกเหตุการณ์

ตัวอย่าง

bash
curl -X POST https://api.badges.ninja/webhooks \
  -H "X-Api-Key: bws_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/badges",
    "events": ["credential.issued", "credential.delivered"]
  }'

การตอบกลับ

201 Created ความลับสำหรับลงลายเซ็น (signing secret) จะถูกส่งกลับเพียงครั้งเดียวเท่านั้น — จัดเก็บไว้ตอนนี้ เพราะคุณไม่สามารถเรียกดูได้อีก

json
{
  "id": "e4b19ff5-063d-4799-bd75-d03641be624f",
  "url": "https://example.com/hooks/badges",
  "events": ["credential.issued", "credential.delivered"],
  "secret": "2be6d335682c1658242fde3a523fd8a2493bab8c"
}

แสดงรายการปลายทาง

GET /webhooks

ส่งกลับปลายทางที่คุณลงทะเบียนไว้ ความลับสำหรับลงลายเซ็นจะไม่รวมอยู่ในผลลัพธ์เลย

json
{
  "webhooks": [
    {
      "id": "e4b19ff5-063d-4799-bd75-d03641be624f",
      "url": "https://example.com/hooks/badges",
      "events": ["credential.issued", "credential.delivered"],
      "active": true,
      "failureCount": 0,
      "createdAt": 1787685415083,
      "lastStatus": 200,
      "lastDeliveryAt": 1787685480777
    }
  ]
}

ลบปลายทาง

DELETE /webhooks/{id}
bash
curl -X DELETE https://api.badges.ninja/webhooks/e4b19ff5-063d-4799-bd75-d03641be624f \
  -H "X-Api-Key: bws_your_api_key_here"

การนำส่งไปยังปลายทางจะหยุดทันที

ชนิดเหตุการณ์

เหตุการณ์เกิดขึ้นเมื่อ…
credential.issuedมีการออกข้อมูลรับรองให้แก่ผู้รับ
credential.deliveredอีเมลแจ้งเตือนของข้อมูลรับรองถูกส่งไปยังผู้รับ (แบบเดี่ยวหรือแบบกลุ่ม)
credential.revokedข้อมูลรับรองถูกเพิกถอน
credential.viewedหน้าข้อมูลรับรองสาธารณะของผู้รับถูกเข้าชมเป็นครั้งแรก

เพย์โหลดการนำส่ง

การนำส่งทุกครั้งเป็นคำขอ POST ที่มีเนื้อความ JSON รูปแบบดังนี้:

json
{
  "id": "b1c2d3e4-...",
  "type": "credential.issued",
  "createdAt": 1787685415083,
  "data": { }
}
ฟิลด์คำอธิบาย
idรหัสการนำส่งที่ไม่ซ้ำกัน (ใช้เพื่อขจัดรายการซ้ำ)
typeชนิดเหตุการณ์
createdAtเวลาประทับของเหตุการณ์ (epoch มิลลิวินาที)
dataเพย์โหลดเฉพาะของแต่ละเหตุการณ์ (ดูด้านล่าง)

data แยกตามชนิดเหตุการณ์

credential.issued

json
{
  "awardId": "https://api.badges.ninja/certify-badge/award/<guid>",
  "badgeId": "https://api.badges.ninja/certify-badge/badge/<guid>",
  "badgeName": "Advanced Certification",
  "recipient": { "email": "jane@example.com", "name": "Jane Doe" },
  "issuedOn": "2026-08-25",
  "expires": null
}

credential.delivered

json
{
  "awardId": "https://api.badges.ninja/certify-badge/award/<guid>",
  "recipient": { "email": "jane@example.com", "name": "Jane Doe" },
  "badgeName": "Advanced Certification"
}

credential.revoked

json
{ "awardId": "https://api.badges.ninja/certify-badge/award/<guid>", "reason": "Issued in error" }

credential.viewed

json
{
  "awardId": "https://api.badges.ninja/certify-badge/award/<guid>",
  "recipient": { "email": "jane@example.com", "name": "Jane Doe" }
}

ส่วนหัวของคำขอ

การนำส่งแต่ละครั้งจะมีส่วนหัวเหล่านี้:

ส่วนหัวคำอธิบาย
X-Bws-Eventชนิดเหตุการณ์ (เหมือนกับ type ในเนื้อความ)
X-Bws-Deliveryรหัสการนำส่ง (เหมือนกับ id ในเนื้อความ)
X-Bws-Signaturesha256= ตามด้วยค่า HMAC-SHA256 ของเนื้อความคำขอดิบ โดยใช้ความลับของปลายทางเป็นคีย์
User-Agentbadges.ninja-webhooks/1

การตรวจสอบลายเซ็น

ตรวจสอบลายเซ็นทุกครั้งก่อนที่จะเชื่อถือการนำส่ง คำนวณค่า HMAC-SHA256 ของเนื้อความคำขอดิบโดยใช้ความลับสำหรับลงลายเซ็นของปลายทางคุณ แล้วเปรียบเทียบ (แบบใช้เวลาคงที่) กับส่วนหัว X-Bws-Signature

js
import crypto from "node:crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected = "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody, "utf8").digest("hex");
  const a = Buffer.from(signatureHeader);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

ใช้ไบต์ตรงตามที่คุณได้รับมา — การแยกวิเคราะห์ (parse) แล้วแปลง JSON กลับเป็นสตริงใหม่จะเปลี่ยนไบต์และทำให้ลายเซ็นใช้ไม่ได้

การลองใหม่และความน่าเชื่อถือ

  • การนำส่งจะถือว่าสำเร็จเมื่อปลายทางของคุณตอบกลับด้วยสถานะ 2xx
  • การนำส่งที่ล้มเหลวจะถูกลองใหม่ในจำนวนครั้งไม่มาก ปลายทางที่ล้มเหลวต่อเนื่องจะสะสมค่า failureCount และหลังจากล้มเหลวติดต่อกัน 20 ครั้ง ปลายทางจะถูกปิดใช้งานโดยอัตโนมัติ (active: false) และหยุดรับการนำส่งจนกว่าคุณจะแก้ไขและลงทะเบียนปลายทางใหม่
  • การนำส่งอาจมาถึงมากกว่าหนึ่งครั้ง ใช้รหัส X-Bws-Delivery (หรือ id ในเนื้อความ) เพื่อทำให้ตัวจัดการของคุณเป็น idempotent
  • ตอบกลับอย่างรวดเร็ว (ภายในประมาณ 10 วินาที) ทำงานหนักแบบอะซิงโครนัสหลังจากตอบรับแล้ว

การจัดการ Webhook ในแดชบอร์ด

คุณยังสามารถจัดการปลายทางได้โดยไม่ต้องใช้ API — เปิด Webhooks ในแถบด้านข้างของแดชบอร์ดเพื่อเพิ่ม แสดงรายการ และลบปลายทาง รวมถึงเลือกว่าปลายทางแต่ละรายการจะรับเหตุการณ์ใดบ้าง

badges.ninja Documentation