Skip to content

การผสานรวม Webhook ​

Webhook ของ SORI API จะแจ้งระบบของคุณเกี่ยวกับเหตุการณ์สำคัญที่เกิดขึ้นภายใน แพลตฟอร์ม SORI เมื่อเชื่อมต่อ endpoint ของคุณเอง คุณจะตอบสนองต่อกิจกรรมของ แคมเปญ การยืนยันตัวตนของอุปกรณ์ และการอัปเดตโดยผู้ดูแลระบบได้แบบเกือบเรียลไทม์

ภาพรวม ​

SORI API มีฟังก์ชัน Webhook สำหรับส่ง payload แบบ JSON ที่มีโครงสร้างทันทีที่ เกิดเหตุการณ์สำคัญ โดยกำหนดค่า Webhook ได้ภายใน SORI Console และรองรับหมวดหมู่ ต่อไปนี้:

  • เหตุการณ์แคมเปญ (`campaign.*`) ทริกเกอร์เมื่ออุปกรณ์รู้จำแคมเปญได้ หรือเมื่อผู้ใช้คลิกไปยัง action URL
  • เหตุการณ์การยืนยันตัวตน (`authentication`) ทริกเกอร์เมื่ออุปกรณ์ลงชื่อเข้าใช้ผ่านขั้นตอนการยืนยันตัวตนของ SORI สำเร็จ
  • เหตุการณ์เกี่ยวกับสื่อ (Material) ของผู้ดูแลระบบ (`admin.material.*`)การแจ้งเตือนแบบเลือกใช้ ซึ่งส่งออกมาเมื่อมีการสร้าง อัปเดต หรือลบสื่อภายใน Console

การแจ้งเตือนแบบเกือบเรียลไทม์ช่วยให้คุณสามารถ:

  • การวิเคราะห์ทันที ติดตามและวิเคราะห์ประสิทธิภาพของแคมเปญทันทีที่มีกิจกรรมเกิดขึ้น
  • การตรวจสอบการยืนยันตัวตน กระทบยอดการเข้าสู่ระบบของอุปกรณ์กับบันทึกเซสชันของคุณเอง
  • การตรวจจับการทุจริต ตรวจจับรูปแบบที่ผิดปกติทันทีที่เกิดขึ้น
  • แดชบอร์ดสด แสดงข้อมูลล่าสุดบนแดชบอร์ด

การตั้งค่า Webhook ​

SORI มีสองวิธีในการกำหนดค่า endpoint ของ Webhook:

URL ของ Webhook ส่วนกลาง ​

  • ตั้งค่าในส่วน Settings
  • ใช้กับทุกแคมเปญโดยค่าเริ่มต้น
  • ทำหน้าที่เป็น endpoint สำรองเมื่อแคมเปญไม่ได้กำหนดค่าแทนที่

URL ของ Webhook เฉพาะแคมเปญ ​

  • กำหนดค่าใน Advanced settings ของแต่ละแคมเปญ
  • แทนที่ URL ของ Webhook ส่วนกลางสำหรับแคมเปญนั้นโดยเฉพาะ
  • มีประโยชน์เมื่อแคมเปญแต่ละรายการต้องใช้การจัดการเฉพาะ

การสมัครรับเหตุการณ์ (Settings → Webhooks) ​

ใน Account Settings คุณสามารถเลือกหมวดหมู่เหตุการณ์ที่บัญชีจะรับได้:

  • Campaign events: สมัครรับการแจ้งเตือน campaign.*(กิจกรรม impression และ click)
  • Authentication events: สมัครรับเหตุการณ์authentication ที่ส่งออกมาระหว่างอุปกรณ์เข้าสู่ระบบ
  • Administrative events: สมัครรับการแจ้งเตือนadmin.material.* ที่ส่งเมื่อผู้ใช้ Console จัดการสื่อ

บัญชีใหม่จะเปิดใช้ Campaign events และ Authentication events โดยค่าเริ่มต้น ผู้ดูแลระบบสามารถปรับช่องทำเครื่องหมายเหล่านี้ได้ทุกเมื่อ และการเปลี่ยนแปลงจะมีผล ทันทีต่อการส่ง Webhook ครั้งถัดไป

วิธีตั้งค่า Webhook ​

  1. เตรียม endpoint แบบ HTTPS บนเซิร์ฟเวอร์เพื่อรับเหตุการณ์ Webhook
  2. กำหนดค่าอย่างใดอย่างหนึ่งต่อไปนี้:
    • URL ของ Webhook ส่วนกลางใน Settings หรือ
    • URL ของ Webhook เฉพาะแคมเปญในการตั้งค่าของแต่ละแคมเปญ
  3. เลือกหมวดหมู่เหตุการณ์ที่ต้องการรับใน Settings → Webhooks
  4. ตรวจสอบว่า endpoint รองรับคำขอ POST ที่มี payload แบบ JSON และตอบกลับ อย่างรวดเร็ว (ภายในห้าวินาที)

ประเภทเหตุการณ์ ​

payload ของ Webhook แต่ละรายการมีฟิลด์ event ซึ่งเก็บชื่อเหตุการณ์มาตรฐาน เพื่อความชัดเจน การผสานรวมใหม่ควรแยกการทำงานตามค่า event

เหตุการณ์การแสดงผลแคมเปญ (campaign.impression) ​

ส่งเมื่อแคมเปญแสดงบนอุปกรณ์ของผู้ใช้สำเร็จ เหตุการณ์นี้พร้อมใช้งานเมื่อเปิดใช้ Campaign events

เหตุการณ์การคลิกแคมเปญ (campaign.click) ​

ส่งเมื่อผู้ใช้แตะแคมเปญและถูกเปลี่ยนเส้นทางไปยัง action URL เหตุการณ์นี้พร้อมใช้งาน เมื่อเปิดใช้ Campaign events

เหตุการณ์การยืนยันตัวตน (authentication) ​

ส่งหลังจากอุปกรณ์ยืนยันตัวตนกับ SORI เสร็จสมบูรณ์ payload มีพารามิเตอร์ query ที่ส่งมาพร้อมกับคำขอยืนยันตัวตน ซึ่งใช้เชื่อมโยงการเข้าสู่ระบบกับตัวระบุเซสชัน ของคุณเองได้

การจัดการเหตุการณ์ Webhook ​

เมื่อ endpoint ได้รับเหตุการณ์ Webhook จะมี payload แบบ JSON ที่ระบุรายละเอียด ของเหตุการณ์ ตัวอย่างของเหตุการณ์แต่ละประเภทแสดงไว้ด้านล่าง ฟิลด์ที่ระบุเป็น null อาจถูกละไว้ทั้งหมดเมื่อไม่มีค่า

Schema ของ payload ​

ฟิลด์ต่อไปนี้จะถูกส่งมากับ Webhook ของแคมเปญทุกรายการ เว้นแต่จะระบุไว้เป็นอย่างอื่น

ฟิลด์ประเภทคำอธิบาย
eventstringชื่อเหตุการณ์มาตรฐาน เช่น campaign.impression, campaign.click หรือ authentication
event_typestringสำเนาเดิมของ event ซึ่งยังส่งออกมาเพื่อรองรับความเข้ากันได้ย้อนหลัง
account_idstringตัวระบุบัญชี SORI ที่เป็นเจ้าของแคมเปญ
created_atISO 8601 stringเวลาฝั่งเซิร์ฟเวอร์เมื่อสร้างออบเจ็กต์เหตุการณ์
timestampISO 8601 stringเวลาประทับการส่งที่เพิ่มก่อนส่งออก ใช้ประโยชน์ในการตรวจสอบ idempotency
activity_idstringตัวระบุที่ไม่ซ้ำกันของ impression หรือ click นั้น (เฉพาะเหตุการณ์แคมเปญ)
device_idstringอุปกรณ์/เซสชันที่ทริกเกอร์เหตุการณ์ หากมีข้อมูล
platformstringแพลตฟอร์มไคลเอนต์ที่ SDK รายงาน (Android, iOS เป็นต้น)
metadataobjectคู่คีย์-ค่าแบบกำหนดเองที่ส่งต่อจาก SDK หรือคำขอยืนยันตัวตน
geolocation.viewer_countrystringประเทศที่ได้จาก CloudFront GeoIP (ส่งคืน Unknown หากตรวจหาไม่ได้)
geolocation.viewer_regionstringภูมิภาค/รัฐที่ได้จากการค้นหา GeoIP
geolocation.viewer_citystringเมืองที่ได้จากการค้นหา GeoIP
campaign.idstringตัวระบุแคมเปญที่ปรับให้อยู่ในรูปแบบมาตรฐาน ซึ่งรับประกันว่าตรงกับ ID ใน Console
campaign.namestringชื่อแคมเปญที่แสดง ณ เวลาที่เกิดเหตุการณ์
campaign.tagsstring[]แท็กที่กำหนดให้แคมเปญ (อาร์เรย์ว่างหากไม่มี)
campaign.imagestring (URL)URL แบบ HTTPS ของภาพขนาดย่อของแคมเปญ หากกำหนดไว้
campaign.propertiesobjectคุณสมบัติการรายงานที่มีผลซึ่งบันทึกเป็น snapshot สำหรับกิจกรรมนี้
material.idstringตัวระบุสื่อที่รู้จำได้ (เฉพาะเหตุการณ์แคมเปญ)
material.namestringชื่อสื่อ โดยจะละฟิลด์นี้เมื่อไม่มีสื่อที่เกี่ยวข้อง
material.tagsstring[]แท็กที่จัดเก็บไว้ในระเบียนสื่อ (อาร์เรย์ว่างหากไม่มี)
material.imagestring (URL)URL ของภาพตัวอย่างสื่อ หากมี

ระบบจะหา campaign.properties ณ เวลาที่เกิดกิจกรรมตามลำดับความสำคัญดังนี้: ค่าเริ่มต้นของคุณสมบัติแคมเปญ ของบัญชี คุณสมบัติแคมเปญที่ระบุไว้ และคุณสมบัติ Audio Marker ตัวอย่างเช่น ค่าเริ่มต้น media=TV จะใช้เฉพาะเมื่อแคมเปญและ marker ไม่ได้ระบุ media แต่หาก marker มีค่า media=Youtube ผลลัพธ์จะเป็น Youtube ออบเจ็กต์ที่ได้คือ snapshot ณ เวลาที่เกิดเหตุการณ์ ดังนั้นการเปลี่ยนการตั้งค่าภายหลังจะไม่เปลี่ยนระเบียนกิจกรรมที่มีอยู่หรือ payload ของ Webhook

ฟิลด์เดิม

event_type, campaign_id, campaign_name, material_id และ material_name เป็นฟิลด์ซ้ำแบบเดิมที่คงไว้เพื่อรองรับความเข้ากันได้ย้อนหลังเท่านั้น และจะถูกนำออก ในรุ่นถัดไป โปรดปรับการผสานรวมให้ใช้ฟิลด์ event ร่วมกับออบเจ็กต์ campaign และ material ที่ซ้อนอยู่ เพื่อความชัดเจน payload ตัวอย่างด้านล่างจึงไม่รวม คีย์ที่เลิกใช้เหล่านี้

สำหรับเหตุการณ์ impression:

json
{
  "event": "campaign.impression",
  "account_id": "acc_123456",
  "created_at": "2025-10-14T05:25:12.421Z",
  "activity_id": "act_7890",
  "device_id": "device_abc",
  "platform": "Android",
  "metadata": {
    "session": "abcd-1234"
  },
  "geolocation": {
    "viewer_country": "United States",
    "viewer_region": "Michigan",
    "viewer_city": "Ann Arbor"
  },
  "campaign": {
    "id": "cmp_1234",
    "name": "Fall Promotion",
    "tags": [
      "retail",
      "autumn"
    ],
    "image": "https://cdn.soriapi.com/campaigns/cmp_1234.png"
  },
  "material": {
    "id": "mat_5678",
    "name": "Audio Spot 15s",
    "tags": [
      "audio",
      "15s"
    ],
    "image": "https://cdn.soriapi.com/materials/mat_5678.png"
  },
  "timestamp": "2025-10-14T05:25:12.421Z"
}

สำหรับเหตุการณ์ click:

json
{
  "event": "campaign.click",
  "account_id": "acc_123456",
  "created_at": "2025-10-14T05:26:03.009Z",
  "activity_id": "act_7890",
  "device_id": "device_abc",
  "platform": "Android",
  "metadata": {
    "session": "abcd-1234",
    "utm_source": "push"
  },
  "geolocation": {
    "viewer_country": "United States",
    "viewer_region": "Michigan",
    "viewer_city": "Ann Arbor"
  },
  "campaign": {
    "id": "cmp_1234",
    "name": "Fall Promotion",
    "tags": [
      "retail",
      "autumn"
    ],
    "image": "https://cdn.soriapi.com/campaigns/cmp_1234.png"
  },
  "material": {
    "id": "mat_5678",
    "name": "Audio Spot 15s",
    "tags": [
      "audio",
      "15s"
    ],
    "image": "https://cdn.soriapi.com/materials/mat_5678.png"
  },
  "timestamp": "2025-10-14T05:26:03.009Z"
}

สำหรับเหตุการณ์การยืนยันตัวตน:

json
{
  "event": "authentication",
  "account_id": "acc_123456",
  "created_at": "2025-10-14T05:15:44.832Z",
  "device_id": "device_abc",
  "platform": "Android",
  "metadata": {
    "app_version": "1.8.0",
    "session": "abcd-1234"
  },
  "geolocation": {
    "viewer_country": "United States",
    "viewer_region": "Michigan",
    "viewer_city": "Ann Arbor"
  },
  "timestamp": "2025-10-14T05:15:44.832Z"
}

Metadata แบบกำหนดเอง ​

ฟิลด์ metadata คือแมปแบบคีย์-ค่าที่ SDK ส่งมา (สำหรับเหตุการณ์แคมเปญ) หรือมาจากพารามิเตอร์ query ของคำขอยืนยันตัวตน (สำหรับเหตุการณ์การยืนยันตัวตน) ฟิลด์นี้ใช้ระบุผู้ใช้หรืออุปกรณ์และส่งบริบทเพิ่มเติมได้ โดยเป็นฟิลด์ที่เลือกใช้ได้ โปรดดูรายละเอียดเพิ่มเติมในส่วน ตัวให้บริการ Metadata

หมายเหตุเกี่ยวกับข้อมูลตำแหน่ง

ค่าตำแหน่งทางภูมิศาสตร์อยู่ภายในออบเจ็กต์ geolocation ข้อมูลนี้อ้างอิงจาก การประมาณค่าของ CloudFront GeoIP และอาจไม่แม่นยำเสมอไป หากไม่มีข้อมูลที่แน่นอน ระบบจะส่งคืนค่า Unknown

ข้อกำหนดในการตอบกลับ ​

endpoint ของ Webhook ควร:

  • ตอบกลับด้วยรหัสสถานะ 2xx เพื่อยืนยันว่าได้รับข้อมูลแล้ว
  • ประมวลผลเหตุการณ์แบบอะซิงโครนัสหากทำได้ เพื่อหลีกเลี่ยงการบล็อก
  • จัดการเหตุการณ์แบบ idempotent (โดยใช้เวลาประทับหรือ activity ID)
  • ตอบกลับภายใน 5 วินาที เพื่อหลีกเลี่ยงการหมดเวลา

ข้อควรพิจารณาด้านความปลอดภัย ​

  • ใช้ HTTPS สำหรับ endpoint ของ Webhook ทุกครั้งที่ทำได้ เพื่อรักษาความเป็นส่วนตัวของข้อมูล
  • ตรวจสอบโครงสร้าง payload ที่เข้ามาและยืนยันค่า event
  • เก็บ URL ของ Webhook ไว้เป็นความลับ
  • เมื่อกำหนดค่า app_id และ secret_key แล้ว ให้ตรวจสอบ header X-SORI-Signature กับสำเนา payload ของคุณเพื่อยืนยันความถูกต้อง

การจัดการข้อผิดพลาด ​

หาก endpoint รับเหตุการณ์ไม่สำเร็จ SORI API Server จะ:

  • ลองส่งรายการที่ล้มเหลวใหม่สูงสุด 3 ครั้ง
  • ใช้การหน่วงเวลาแบบทวีคูณ (exponential backoff) ระหว่างการลองใหม่แต่ละครั้ง
  • ยกเลิกเหตุการณ์หลังจากลองใหม่ครบทุกครั้งแล้ว

ตัวอย่างการนำไปใช้ ​

Python (FastAPI) ​

python
from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/webhook")
async def webhook(request: Request):
    payload = await request.json()
    event = payload.get("event")

    if event == "campaign.impression":
        # Handle impression event
        pass
    elif event == "campaign.click":
        # Handle click event
        pass
    elif event == "authentication":
        # Handle authentication event
        pass

    return {"status": "success"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=3000)

Node.js (Express) ​

typescript
import express from "express";

const app = express();
app.use(express.json());

app.post("/webhook", (req, res) => {
  const payload = req.body;
  const event = payload.event;

  if (event === "campaign.impression") {
    // Handle impression event
  } else if (event === "campaign.click") {
    // Handle click event
  } else if (event === "authentication") {
    // Handle authentication event
  }

  res.status(200).send({ status: "success" });
});

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});