דילוג לתוכן
תיעוד · Webhooks

Webhooks

Usher יכול לעדכן את השרתים שלך במה שקורה בסביבה ברגע שזה קורה: מישהו מצטרף, מתחבר, משנה את הפרטים שלו או עוזב. כל אירוע הוא בקשת POST חתומה לכתובת שבוחרים, שנשלחת שוב אם השרת שלך לא עונה.

הוספת נקודת קצה

ב-Usher Console, פותחים את הסביבה ועוברים אל Webhooks ← נקודת קצה חדשה. מזינים כתובת https:// שלך ובוחרים את האירועים שהיא צריכה לשמוע עליהם. סוד החתימה של נקודת הקצה (whsec_…) מוצג ב-Console פעם אחת בלבד: כדאי לשמור אותו יחד עם שאר הסודות של השרת.

אירועמתי
user.createdמישהו יצר חשבון
user.updatedחשבון השתנה: כתובת דוא״ל, שם, אימות או חסימה (כולל חסימה על ידי ספריית המשתמשים של הארגון, דרך SCIM)
user.deletedחשבון נמחק
session.createdמישהו התחבר
session.endedסשן הסתיים: התנתקות, ביטול או פקיעה מוקדמת

שליחת בדיקה שולחת אירוע webhook.test לנקודת הקצה הזו בלבד, לא משנה לאילו אירועים היא רשומה.

מה מגיע

בקשה
POST /usher/webhooks HTTP/1.1
content-type: application/json
user-agent: Usher-Webhooks/1
webhook-id: msg_2fk7x…
webhook-timestamp: 1793643116
webhook-signature: v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE=
גוף
{
  "type": "user.created",
  "timestamp": "2026-11-02T18:11:56.412Z",
  "data": {
    "user": {
      "id": "Qz3m…",
      "email": "lena@acme.com",
      "emailVerified": false,
      "name": "Lena Live",
      "image": null,
      "banned": false,
      "createdAt": "2026-11-02T18:11:56.380Z",
      "updatedAt": "2026-11-02T18:11:56.380Z"
    }
  }
}

האירועים מתארים אנשים וסשנים, ואף פעם לא סודות: אין בהם אסימונים, גיבובי סיסמאות או תפקידים, וגם לא הסיבה שבגללה מישהו נחסם.

בדיקה של כל משלוח

Usher חותם לפי המפרט Standard Webhooks, כך שכל אחת מהספריות שלו בודקת את החתימה בשבילך. יש לדחות כל דבר שלא עובר אימות. הספריות גם דוחות חותמת זמן שרחוקה ביותר מחמש דקות, כך שאי אפשר לשלוח שוב משלוח ישן.

app/usher/webhooks/route.ts
// a route that receives Usher's webhooks (Node, the standardwebhooks package)
import { Webhook } from 'standardwebhooks'

const wh = new Webhook(process.env.USHER_WEBHOOK_SECRET!) // whsec_…, from Usher Console

export async function POST(request: Request) {
  const body = await request.text() // the raw body: the signature covers it byte for byte
  let event: { type: string; timestamp: string; data: Record<string, unknown> }
  try {
    event = wh.verify(body, Object.fromEntries(request.headers)) as typeof event
  } catch {
    return new Response('bad signature', { status: 400 })
  }
  // webhook-id is the same on every attempt: skip one you have handled already
  if (await alreadyHandled(request.headers.get('webhook-id')!)) return new Response(null, { status: 204 })
  await queue(event) // answer quickly; do the work afterwards
  return new Response(null, { status: 204 })
}
  • עונים תוך 15 שניות עם כל 2xx. כל תשובה אחרת, כולל הפניה, נחשבת לכישלון.
  • מצפים לכפילויות. הודעה שומרת על אותו webhook-id בכל ניסיון; אפשר לדלג על הודעה שכבר טופלה.
  • לא סומכים על הסדר. ניסיונות חוזרים יכולים להגיע אחרי אירועים חדשים יותר.

כשהשרת שלך לא עונה

Usher מנסה שוב אחרי 5 שניות, 5 דקות, 30 דקות, שעתיים, 5 שעות, 10 שעות ועוד 10 שעות (בערך יום וחצי בסך הכול), ואז מוותר. יומן המשלוחים ב-Console מציג את כל הניסיונות של כל הודעה ואת התשובה האחרונה של השרת שלך, ושליחה חוזרת מתחילה משלוח מחדש.

סוד חדש

סוד חדש ב-Console יוצר סוד נוסף ומציג אותו פעם אחת. במשך 24 השעות הבאות כל משלוח נושא שתי חתימות, אחת לכל סוד, כך שאפשר לפרוס את הסוד החדש בלי לפספס שום דבר. אחר כך רק הסוד החדש חותם.

מאיפה המשלוחים מגיעים

ה-Webhooks יוצאים מ-Usher דרך פרוקסי היציאה שלו, שלא מתחבר לכתובות פרטיות או פנימיות. נקודת קצה חייבת להיות כתובת https:// בשם ציבורי: לא כתובת IP, לא שם פרטי, ולא כתובת באחד הדומיינים של Usher עצמו.