Webhooks
Mit Webhooks reagieren Deine Systeme auf Ereignisse in Deinem Usetix-Konto — ein verkauftes Ticket, eine ausgelöste Rückerstattung, ein live geschaltetes Event — ohne unsere API abfragen zu müssen. Sobald ein abonniertes Ereignis eintritt, sendet Usetix eine signierte POST-Anfrage an eine von Dir festgelegte URL.
Webhooks verwaltest Du in Deinem Dashboard unter Einstellungen → Webhooks.
Zustellung
| Eigenschaft | Wert |
|---|---|
| Methode | POST |
| Content-Type | application/json |
| User-Agent | usetix/1.0.0 Webhook |
| Timeout | 7 Sekunden |
| Maximale Antwortgröße | 100 KB |
Jedes Ereignis erzeugt genau einen Zustellversuch pro passendem Webhook. Automatische Wiederholungen gibt es derzeit nicht — stell also sicher, dass Dein Endpunkt erreichbar ist und zügig antwortet.
Signierung
Jede Anfrage wird per HMAC-SHA256 mit dem signing_secret des Webhooks signiert. Das Secret wird Dir im Dashboard angezeigt, sobald Du den Webhook anlegst. Zwei Header werden mitgeschickt:
| Header | Beschreibung |
|---|---|
X-Webhook-Signature |
Hex-kodierter HMAC-SHA256 des rohen Request-Body, erzeugt mit dem Signing-Secret des Webhooks. |
X-Webhook-Timestamp |
ISO-8601-Zeitstempel (UTC) des Ereignisses — bleibt bei etwaigen künftigen Wiederholungen stabil. |
Verifizierung in Ruby:
expected = OpenSSL::HMAC.hexdigest("SHA256", signing_secret, request.raw_post)
Rack::Utils.secure_compare(expected, request.headers["X-Webhook-Signature"])
Verifizierung in Node.js:
const expected = crypto
.createHmac("sha256", signingSecret)
.update(rawBody)
.digest("hex");
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.header("X-Webhook-Signature")));
Berechne die Signatur immer über den rohen Request-Body — nicht über eine geparste oder neu serialisierte Kopie — und nutze einen zeitkonstanten Vergleich.
Automatische Deaktivierung
Wenn ein Webhook bei 10 aufeinanderfolgenden Zustellungen über mehr als 1 Stunde fehlschlägt, deaktiviert Usetix ihn automatisch. Es werden keine weiteren Zustellungen mehr versendet, bis Du den Webhook im Dashboard wieder aktivierst. Ein Endpunkt gilt als erfolgreich, wenn er HTTP 2xx zurückgibt; alles andere (Timeout, Nicht-2xx, TLS-Fehler, DNS-Fehler) zählt als Fehlschlag.
SSRF-Schutz
Webhook-URLs, die auf private, Loopback-, Link-Local- oder andere nicht-öffentliche IP-Adressen auflösen, werden bei der Zustellung abgelehnt. Verwende ausschließlich öffentliche Hostnamen.
Abonnierbare Ereignisse
Jeder Webhook abonniert eine oder mehrere der folgenden Aktionen:
order.paidorder.refundedorder.cancelledevent.publishedevent.unpublished
Payload-Envelope
Jeder Payload hat dieselbe äußere Struktur. Das eventable-Objekt variiert je nach Aktion.
{
"action": "order.paid",
"created_at": "2026-04-22T12:34:56Z",
"eventable": { "...": "siehe unten" },
"account": {
"name": "Example Promoter",
"subdomain": "example"
}
}
Order-Payloads
Wird gesendet bei order.paid, order.refunded und order.cancelled. Das Feld action zeigt Dir, welcher Übergang ausgelöst wurde.
{
"action": "order.paid",
"created_at": "2026-04-22T12:34:56Z",
"account": { "name": "Example Promoter", "subdomain": "example" },
"eventable": {
"id": "ord_c3a9f4e1",
"type": "Order",
"customer_email": "[email protected]",
"customer_name": "Jane Doe",
"total_amount": "42.00",
"currency": "EUR",
"status": "paid",
"paid_at": "2026-04-22T12:34:50Z",
"items": [
{
"id": "oi_4d2a8b9c",
"ticket_title": "General Admission",
"event_title": "Spring Showcase",
"price": "21.00"
}
]
}
}
| Feld | Typ | Hinweise |
|---|---|---|
eventable.id |
string | Öffentliche Bestellungs-ID. Stabil; kann als Korrelationsschlüssel gespeichert werden. |
eventable.customer_email |
string | E-Mail des Käufers. |
eventable.customer_name |
string | Name des Käufers. |
eventable.total_amount |
string | Dezimalwert als String kodiert (z. B. "42.00"), um Float-Präzisionsprobleme zu vermeiden. |
eventable.currency |
string | ISO-4217-Währungscode. |
eventable.status |
string | paid, refunded oder cancelled. |
eventable.paid_at |
string | null | ISO 8601 UTC. null bei Status ungleich paid. |
eventable.items[].id |
string | Öffentliche ID des Bestellpostens (eine pro Ticket). |
eventable.items[].ticket_title |
string | Titel des Tickettyps. |
eventable.items[].event_title |
string | Titel des Events, zu dem das Ticket gehört. |
eventable.items[].price |
string | Dezimalwert als String. |
Event-Payloads
Wird gesendet bei event.published und event.unpublished.
{
"action": "event.published",
"created_at": "2026-04-22T12:34:56Z",
"account": { "name": "Example Promoter", "subdomain": "example" },
"eventable": {
"slug": "spring-showcase",
"type": "Event",
"title": "Spring Showcase",
"starts_at": "2026-05-01T19:00:00Z",
"ends_at": "2026-05-01T23:00:00Z",
"venue": {
"name": "The Venue",
"city": "Berlin"
}
}
}
| Feld | Typ | Hinweise |
|---|---|---|
eventable.slug |
string | URL-Slug. Die öffentliche URL des Events lautet https://<subdomain>.usetix.io/events/<slug>. |
eventable.title |
string | Titel des Events. |
eventable.starts_at |
string | ISO 8601 UTC. |
eventable.ends_at |
string | ISO 8601 UTC. |
eventable.venue.name |
string | Name des Veranstaltungsorts. |
eventable.venue.city |
string | Stadt des Veranstaltungsorts. |
Lokal testen
Für die lokale Entwicklung liefern Dir Tools wie ngrok oder Cloudflare Tunnel eine öffentliche URL, die auf localhost weiterleitet. Richte einen Webhook auf diese URL ein, löse dann Aktionen in Deinem Konto aus und siehe echte Payloads in Echtzeit ankommen.