Startseite Funktionen Preise Blog Docs
Einloggen Kostenlos starten
English | Deutsch

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.paid
  • order.refunded
  • order.cancelled
  • event.published
  • event.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",
    "order_code": "7K3Q9D2A",
    "display_number": "7K3Q-9D2A",
    "type": "Order",
    "customer_email": "buyer@example.com",
    "customer_name": "Jane Doe",
    "customer_company": "Acme GmbH",
    "customer_phone": "+49 30 1234567",
    "total_amount": "42.00",
    "net_amount": "35.29",
    "vat_amount": "6.71",
    "vat_rate": "19.0",
    "currency": "EUR",
    "status": "paid",
    "paid_at": "2026-04-22T12:34:50Z",
    "invoice_number": "INV-1-2026-00042",
    "payment_provider": "stripe",
    "payment_id": "pi_3OqXyz2eZvKYlo2C0AbCdEfG",
    "attribution": {
      "utm_source": "google",
      "utm_medium": "cpc",
      "utm_campaign": "spring-launch",
      "utm_term": "concert tickets berlin",
      "utm_content": "ad-variant-a",
      "ref": "partner:radiox"
    },
    "b2b_invoice_requested": true,
    "billing_address": {
      "street": "Musterstr. 1",
      "postal_code": "10115",
      "city": "Berlin",
      "country": "DE",
      "vat_id": "DE123456789"
    },
    "items": [
      {
        "id": "oi_4d2a8b9c",
        "check_in_code": "9M5V2H8C",
        "display_check_in_code": "9M5V-2H8C",
        "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.order_code string Menschenlesbarer Bestell-Code. Kann in kunden- oder teamseitigen Oberflächen angezeigt werden.
eventable.display_number string Formatierter Bestell-Code für die Anzeige, typischerweise gruppiert als XXXX-XXXX.
eventable.customer_email string E-Mail des Käufers.
eventable.customer_name string Name des Käufers.
eventable.customer_company string | null Firmenname, den der Käufer beim Checkout angegeben hat. null, wenn nicht angegeben.
eventable.customer_phone string | null Telefonnummer, die der Käufer beim Checkout angegeben hat. null, wenn nicht angegeben.
eventable.total_amount string Bruttogesamtbetrag, dezimal als String kodiert (z. B. "42.00"), um Float-Präzisionsprobleme zu vermeiden.
eventable.net_amount string Nettoanteil von total_amount, berechnet mit derselben Aufteilung wie auf der Kundenrechnung — beide Beträge stimmen auf den Cent überein.
eventable.vat_amount string MwSt.-Anteil von total_amount (total_amount − net_amount).
eventable.vat_rate string Effektiv angewendeter MwSt.-Satz als Prozent (z. B. "19.0", "7.7" oder "0.0" bei MwSt.-Befreiung).
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.invoice_number string | null Rechnungsnummer der Kundenrechnung, sobald die Rechnung erzeugt wurde. null bei order.cancelled und kurzzeitig zwischen Zahlung und Rechnungserstellung.
eventable.payment_provider string "stripe" oder "paypal" — welcher Anbieter diese Bestellung verarbeitet hat.
eventable.payment_id string | null Anbieter-spezifische Zahlungsreferenz. Bei Stripe die Payment-Intent-ID (pi_...) — füge sie ins Stripe-Dashboard ein, um die Zahlung zu finden. Bei PayPal die Capture-ID (oder die Order-ID vor dem Capture). null, solange die Bestellung nicht bezahlt ist.
eventable.attribution object Marketing-Attribution, beim Absenden der Bestellung erfasst. Immer vorhanden; leeres Objekt {}, wenn der Käufer nie mit Tracking-Parametern kam. Siehe Attribution.
eventable.b2b_invoice_requested boolean true, wenn der Käufer beim Checkout eine Geschäftsrechnung angefordert hat.
eventable.billing_address object Immer vorhanden. Einzelne Felder sind null, wenn der Käufer keine Geschäftsrechnung angefordert hat. Siehe Rechnungsadresse.
eventable.items[].id string Öffentliche ID des Bestellpostens (eine pro Ticket).
eventable.items[].check_in_code string Menschenlesbarer Ticket-Check-in-Code. Kann Team und Kunden angezeigt werden.
eventable.items[].display_check_in_code string Formatierter Check-in-Code für die Anzeige, typischerweise gruppiert als XXXX-XXXX.
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.

Attribution

Das attribution-Objekt erfasst, woher der Käufer kam. UTM-Parameter und ref werden aus der URL der Checkout-Seite — und als Fallback aus dem Referer-Header der vorigen Seite — beim Absenden der Bestellung gelesen. Es gibt keine clientseitige Speicherung und kein Cookie-Banner: Die Daten reisen ausschließlich mit dem Formular mit. In der Praxis bedeutet das Last-Click-Attribution — die Quelle, die den Käufer zur Checkout-Seite gebracht hat, ist die, die gespeichert wird.

Feld Typ Hinweise
utm_source string | weggelassen z. B. "google", "facebook", "newsletter".
utm_medium string | weggelassen z. B. "cpc", "email", "social".
utm_campaign string | weggelassen Kampagnenname aus der URL.
utm_term string | weggelassen Bezahltes Keyword, falls vorhanden.
utm_content string | weggelassen Anzeigen-/Creative-Variante, falls vorhanden.
ref string | weggelassen Frei wählbarer Referral-Code. Die Usetix-Shop-Footer-Links hängen ref=shop:<subdomain> an, wenn ein Käufer von einem Shop zur Marketingseite durchklickt — Registrierungen aus diesem Shop tragen so diese Quelle.

Es werden nur tatsächlich erfasste Felder gesendet. Käufer ohne Tracking-Parameter erhalten "attribution": {}.

Rechnungsadresse

Wird gefüllt, wenn der Käufer beim Checkout eine Geschäftsrechnung angefordert hat (b2b_invoice_requested: true). Bei B2C-Bestellungen ist das Objekt vorhanden, alle Felder sind aber null.

Feld Typ Hinweise
street string | null Straße und Hausnummer.
postal_code string | null Postleitzahl.
city string | null Stadt.
country string | null ISO-3166-1-Alpha-2-Ländercode (z. B. "DE", "CH", "AT").
vat_id string | null EU-USt-IdNr., sofern angegeben. Wird beim Checkout für grenzüberschreitende EU-Bestellungen über VIES validiert.

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.