Unter Einstellungen → Webhooks fügen Sie die URL hinzu, an die die Ereignisse gesendet werden sollen. Sie erhalten ein Geheimnis zurück. Es wird zur Prüfung der Signatur verwendet und muss wie ein Passwort aufbewahrt werden.
Ein System kann sich auch selbst mit einem API-Schlüssel anmelden, ohne dass jemand das Dashboard öffnet. Das ist, wie eine Integration, die Sie nicht selbst geschrieben haben, sich verbindet: Sie erhalten Ihren Schlüssel und registriert ihre eigene Adresse.
Endpunkte auflisten
/ v1/ api/ webhooksDie Endpunkte des Unternehmens, auch die im Dashboard angelegten, jeweils mit ihrem Geheimnis und dem Ergebnis der letzten Zustellung.
curl https://api.kuvert.dk/v1/api/webhooks \
-H "Authorization: Bearer $KUVERT_API_KEY"200 OKJSON{
"endpoints": [
{
"id": "weh_5e8a1c3f7b9d42e6a0c4f8b2d6e1a3c9",
"url": "https://dit-system.dk/kuvert",
"events": [
"giftcard.issued",
"giftcard.redeemed"
],
"status": "active",
"secret": "whsec_docs-example-not-a-real-secret",
"lastDeliveryAt": "2026-09-29T09:12:44.000Z",
"lastDeliveryOk": true,
"lastDeliveryDetail": null,
"consecutiveFailures": 0,
"createdAt": "2026-09-01T07:30:00.000Z"
}
]
}Antwortfelder
Die Endpunkte stehen unter endpoints. Jeder hat diese Felder:
idstring- Die ID des Endpunkts (
weh_…). urlstring- Die Adresse, an die die Ereignisse gesendet werden.
eventsstring[]- Die Ereignisse, die der Endpunkt abonniert. Eine leere Liste bedeutet alle.
status"active" | "disabled"- Nur
activeerhält Zustellungen. secretstring- Das Geheimnis (
whsec_…), mit dem die Signatur berechnet wird. lastDeliveryAtstring | null- Wann zuletzt eine Zustellung versucht wurde.
null, wenn noch nie etwas gesendet wurde. lastDeliveryOkboolean | null- Ob die letzte Zustellung ein 2xx erhalten hat.
nullbedeutet nie versucht und nicht fehlgeschlagen. lastDeliveryDetailstring | null- Warum die letzte fehlschlug: ein HTTP-Status oder ein Transportfehler.
null, wenn sie gelang. consecutiveFailuresinteger- Fehlgeschlagene Zustellungen in Folge. Die erste, die gelingt, setzt die Zahl zurück.
createdAtstring- Wann der Endpunkt angelegt wurde.
Statuscodes
200
Die Liste, gegebenenfalls leer.
Ein Fehler hat immer die Form { "error": "…" }. Jeder Aufruf kann außerdem mit 401, 403, 429 und 500 antworten. Siehe Fehler.
Oft zusammen verwendet mit
Endpunkt hinzufügen
/ v1/ api/ webhooksRegistriert eine Adresse und antwortet mit dem Endpunkt und seinem Geheimnis. Er erhält sofort Zustellungen.
curl -X POST https://api.kuvert.dk/v1/api/webhooks \
-H "Authorization: Bearer $KUVERT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://dit-system.dk/kuvert",
"events": [
"giftcard.issued",
"giftcard.redeemed"
]
}'201 CreatedJSON{
"endpoint": {
"id": "weh_5e8a1c3f7b9d42e6a0c4f8b2d6e1a3c9",
"url": "https://dit-system.dk/kuvert",
"events": [
"giftcard.issued",
"giftcard.redeemed"
],
"status": "active",
"secret": "whsec_docs-example-not-a-real-secret",
"lastDeliveryAt": null,
"lastDeliveryOk": null,
"lastDeliveryDetail": null,
"consecutiveFailures": 0,
"createdAt": "2026-09-29T09:00:00.000Z"
}
}Body
urlstringErforderlich- Eine öffentliche https-Adresse, höchstens 2048 Zeichen. Interne und lokale Adressen werden abgelehnt.
eventsstring[]Erforderlich- Die Ereignisse, die gesendet werden sollen. Eine leere Liste bedeutet alle, auch die, die später hinzukommen.
Antwortfelder
Der Endpunkt steht unter endpoint, mit denselben Feldern wie bei Endpunkte auflisten.
Statuscodes
201
Der Endpunkt ist angelegt.
400
invalid_webhook_endpointDer Body passt nicht: Die Adresse ist keine öffentliche https-Adresse, oder ein Ereignis existiert nicht.
Ein Fehler hat immer die Form { "error": "…" }. Jeder Aufruf kann außerdem mit 401, 403, 429 und 500 antworten. Siehe Fehler.
Oft zusammen verwendet mit
Endpunkt ändern
/ v1/ api/ webhooksÄndert Adresse, Ereignisse oder Status. Die id des Endpunkts steht im Body, nicht im Pfad. Senden Sie nur die Felder, die Sie ändern möchten.
curl -X PATCH https://api.kuvert.dk/v1/api/webhooks \
-H "Authorization: Bearer $KUVERT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "weh_5e8a1c3f7b9d42e6a0c4f8b2d6e1a3c9",
"status": "disabled"
}'200 OKJSON{
"endpoint": {
"id": "weh_5e8a1c3f7b9d42e6a0c4f8b2d6e1a3c9",
"url": "https://dit-system.dk/kuvert",
"events": [
"giftcard.issued",
"giftcard.redeemed"
],
"status": "disabled",
"secret": "whsec_docs-example-not-a-real-secret",
"lastDeliveryAt": "2026-09-29T09:12:44.000Z",
"lastDeliveryOk": true,
"lastDeliveryDetail": null,
"consecutiveFailures": 0,
"createdAt": "2026-09-01T07:30:00.000Z"
}
}Body
idstringErforderlich- Die ID des Endpunkts (
weh_…). urlstringOptional- Eine neue Adresse, mit denselben Anforderungen wie beim Anlegen.
eventsstring[]Optional- Eine neue Liste von Ereignissen. Sie ersetzt die alte vollständig.
status"active" | "disabled"Optionaldisabledstoppt die Zustellungen, ohne den Endpunkt zu löschen.
Antwortfelder
Der geänderte Endpunkt steht unter endpoint.
Statuscodes
200
Der Endpunkt ist geändert.
400
invalid_requestDer Body passt nicht, meist weil die
idfehlt.404
webhook_endpoint_not_foundDer Endpunkt existiert in Ihrem Unternehmen nicht.
Ein Fehler hat immer die Form { "error": "…" }. Jeder Aufruf kann außerdem mit 401, 403, 429 und 500 antworten. Siehe Fehler.
Oft zusammen verwendet mit
Endpunkt löschen
/ v1/ api/ webhooks/ {id}Entfernt den Endpunkt endgültig. Möchten Sie nur pausieren, setzen Sie den Status stattdessen auf disabled.
curl -X DELETE https://api.kuvert.dk/v1/api/webhooks/weh_5e8a1c3f7b9d42e6a0c4f8b2d6e1a3c9 \
-H "Authorization: Bearer $KUVERT_API_KEY"200 OKJSON{
"deleted": true
}Pfadparameter
idstringErforderlich- Die ID des Endpunkts (
weh_…).
Antwortfelder
deletedboolean- Immer
true.
Statuscodes
200
Der Endpunkt ist gelöscht.
404
webhook_endpoint_not_foundDer Endpunkt existiert in Ihrem Unternehmen nicht.
Ein Fehler hat immer die Form { "error": "…" }. Jeder Aufruf kann außerdem mit 401, 403, 429 und 500 antworten. Siehe Fehler.
Oft zusammen verwendet mit
Jedes Ereignis wird als POST mit einem JSON-Body aus vier Feldern gesendet: id ist die eigene ID der Zustellung (whevt_…), type ist das Ereignis, createdAt der Zeitpunkt und data das, worum es im Ereignis geht. Drei Header kommen mit: content-type, kuvert-event-type und kuvert-signature.
POST /kuvert HTTP/1.1
Host: dit-system.dk
content-type: application/json
kuvert-event-type: giftcard.redeemed
kuvert-signature: t=1790673164,v1=067aae7b381a2e42b518ef5781e9747638aa4c987e4f68396bef9a06a78d71a8
{"id":"whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7","type":"giftcard.redeemed","createdAt":"2026-09-29T09:12:44.118Z","data":{"code":"KUV-7K9F-2XQD","amountOre":6400,"remainingBalanceOre":43600}}Die Signatur im Beispiel ist echt. Mit dem Geheimnis whsec_docs-example-not-a-real-secret ergibt das Rezept unter Die Signatur genau dieses v1. Lassen Sie Ihre eigene Prüfung darauf laufen, mit abgeschalteter Zeittoleranz, da der Zeitpunkt in der Vergangenheit liegt.
giftcard.issued
Ein Gutschein wird ausgestellt.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "giftcard.issued",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"code": "KUV-M4TQ-8WZR",
"initialOre": 50000,
"orderId": "ord_7d3f0b9c1e2a45f68b4c9d0e1f2a3b5c"
}
}giftcard.redeemed
Ein Betrag wird von einer Karte abgezogen.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "giftcard.redeemed",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"code": "KUV-7K9F-2XQD",
"amountOre": 6400,
"remainingBalanceOre": 43600
}
}order.paid
Eine Bestellung wurde bezahlt. Bei einer Ticketbestellung kommen die Antworten des Käufers auf die Fragen des Abends in answers mit, etwa ein Teamname, jeweils mit der Frage so, wie sie beim Beantworten lautete.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "order.paid",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"orderId": "ord_7d3f0b9c1e2a45f68b4c9d0e1f2a3b5c",
"totalOre": 50000,
"cardCodes": [
"KUV-M4TQ-8WZR"
],
"answers": []
}
}order.refunded
Eine Bestellung wird erstattet.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "order.refunded",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"orderId": "ord_7d3f0b9c1e2a45f68b4c9d0e1f2a3b5c",
"refundedOre": 50500
}
}ticket.issued
Ein Ticket wird von einer bezahlten Bestellung ausgestellt. Die Antworten der Bestellung auf die Fragen des Abends kommen auf jedem Ticket in answers mit.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "ticket.issued",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"code": "TKT-H3NW-7PKC",
"eventId": "evt_1a9c4e7b2d5f48a3b6c9e0d2f4a7b1c8",
"orderId": "ord_7d3f0b9c1e2a45f68b4c9d0e1f2a3b5c",
"answers": [
{
"questionId": "eqs_5c1e9a7d3b2f48a6",
"label": "Holdnavn",
"answer": "Ølnørderne"
}
],
"seller": {
"slug": "club-vortex",
"name": "Club Vortex"
},
"host": {
"slug": "club-vortex",
"name": "Club Vortex"
},
"cohosts": []
}
}order.disputed
Der Kartenaussteller des Käufers hat die Zahlung bestritten. Die Gutscheine und Tickets der Bestellung werden unterbrochen und können nicht verwendet werden.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "order.disputed",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"orderId": "ord_7d3f0b9c1e2a45f68b4c9d0e1f2a3b5c",
"frozenCards": 1,
"frozenTickets": 0
}
}order.dispute_closed
Der Fall ist beendet. won: true bedeutet, dass die Unterbrechung aufgehoben ist und die Karten wieder funktionieren; false bedeutet, dass sie storniert wurden.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "order.dispute_closed",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"orderId": "ord_7d3f0b9c1e2a45f68b4c9d0e1f2a3b5c",
"won": true,
"disputedOre": 50500
}
}booking.created
Ein Tisch wurde gebucht, vom Gast auf der Reservierungsseite, am Telefon oder an der Tür.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "booking.created",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"bookingId": "bkg_6c0e3a9f5b1d47e2a8c4f0b6d2e9a5c1",
"date": "2026-10-02",
"startsAt": "2026-10-02T17:30:00.000Z",
"endsAt": "2026-10-02T19:30:00.000Z",
"guests": 4,
"status": "confirmed",
"source": "storefront",
"guestName": "Mette Hansen",
"guestEmail": "mette@dit-domaene.dk",
"guestPhone": "+45 12 34 56 78",
"tableIds": [
"btb_0d4a8e2c6f1b43a9b7e5c3d1f9a2e6b8"
]
}
}booking.changed
Eine Tischreservierung hat eine neue Uhrzeit, ein neues Datum, eine neue Personenzahl, neue Tische oder einen neuen Status. Die ganze Buchung wird mitgeschickt, und status sagt, wo sie steht.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "booking.changed",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"bookingId": "bkg_6c0e3a9f5b1d47e2a8c4f0b6d2e9a5c1",
"date": "2026-10-02",
"startsAt": "2026-10-02T17:30:00.000Z",
"endsAt": "2026-10-02T19:30:00.000Z",
"guests": 6,
"status": "confirmed",
"source": "storefront",
"guestName": "Mette Hansen",
"guestEmail": "mette@dit-domaene.dk",
"guestPhone": "+45 12 34 56 78",
"tableIds": [
"btb_0d4a8e2c6f1b43a9b7e5c3d1f9a2e6b8"
]
}
}booking.cancelled
Eine Tischreservierung wurde storniert, vom Gast oder vom Restaurant. Buchungen, die aus einem anderen System übernommen werden, senden keine Ereignisse.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "booking.cancelled",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"bookingId": "bkg_6c0e3a9f5b1d47e2a8c4f0b6d2e9a5c1",
"date": "2026-10-02",
"startsAt": "2026-10-02T17:30:00.000Z",
"endsAt": "2026-10-02T19:30:00.000Z",
"guests": 4,
"status": "cancelled",
"source": "storefront",
"guestName": "Mette Hansen",
"guestEmail": "mette@dit-domaene.dk",
"guestPhone": "+45 12 34 56 78",
"tableIds": [
"btb_0d4a8e2c6f1b43a9b7e5c3d1f9a2e6b8"
]
}
}event.published
Eine Veranstaltung wurde in den Verkauf gestellt. Der ganze Abend kommt mit (Name, Zeiten, Ort, Serie, Line-up und Genres), in derselben Form wie die Liste unter GET /v1/merchants/{slug}, mit eventId statt id.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "event.published",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"eventId": "evt_1a9c4e7b2d5f48a3b6c9e0d2f4a7b1c8",
"slug": "vortex-dimension-2026-10-03",
"name": "Vortex Dimension",
"startsAt": "2026-10-03T21:00:00.000Z",
"endsAt": "2026-10-04T03:00:00.000Z",
"venue": "Club Vortex",
"bannerUrl": "https://api.kuvert.dk/v1/media/med_2b7e4c9a1f3d48e6b0a5c8d2f1e7a4b9",
"bannerWidth": 1080,
"bannerHeight": 1350,
"salesCloseAt": "2026-10-03T21:00:00.000Z",
"status": "published",
"cancelNote": null,
"series": null,
"seriesDate": null,
"subtitle": "No Boyz On Deck",
"lineup": [
{
"name": "Faustix",
"role": "DJ",
"time": "2026-10-03T23:00:00.000Z"
}
],
"genres": [
"House",
"Techno"
],
"doorsAt": "2026-10-03T21:00:00.000Z",
"lastEntryAt": "2026-10-04T01:30:00.000Z",
"dressCode": null,
"ticketsUrl": null,
"host": {
"slug": "club-vortex",
"name": "Club Vortex"
},
"cohosts": []
}
}event.updated
Etwas Öffentliches an einer veröffentlichten Veranstaltung hat sich geändert. changedFields sagt, was, etwa lineup, oder tiers, wenn sich ein Preis geändert hat.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "event.updated",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"eventId": "evt_1a9c4e7b2d5f48a3b6c9e0d2f4a7b1c8",
"slug": "vortex-dimension-2026-10-03",
"name": "Vortex Dimension",
"startsAt": "2026-10-03T21:00:00.000Z",
"endsAt": "2026-10-04T03:00:00.000Z",
"venue": "Club Vortex",
"bannerUrl": "https://api.kuvert.dk/v1/media/med_2b7e4c9a1f3d48e6b0a5c8d2f1e7a4b9",
"bannerWidth": 1080,
"bannerHeight": 1350,
"salesCloseAt": "2026-10-03T21:00:00.000Z",
"status": "published",
"cancelNote": null,
"series": null,
"seriesDate": null,
"subtitle": "No Boyz On Deck",
"lineup": [
{
"name": "Faustix",
"role": "DJ",
"time": "2026-10-03T23:00:00.000Z"
}
],
"genres": [
"House",
"Techno"
],
"doorsAt": "2026-10-03T21:00:00.000Z",
"lastEntryAt": "2026-10-04T01:30:00.000Z",
"dressCode": null,
"ticketsUrl": null,
"host": {
"slug": "club-vortex",
"name": "Club Vortex"
},
"cohosts": [],
"changedFields": [
"lineup"
]
}
}event.canceled
Eine veröffentlichte Veranstaltung wurde abgesagt. cancelNote ist die Nachricht, die Sie an die Gäste geschrieben haben.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "event.canceled",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"eventId": "evt_1a9c4e7b2d5f48a3b6c9e0d2f4a7b1c8",
"slug": "vortex-dimension-2026-10-03",
"name": "Vortex Dimension",
"startsAt": "2026-10-03T21:00:00.000Z",
"endsAt": "2026-10-04T03:00:00.000Z",
"venue": "Club Vortex",
"bannerUrl": "https://api.kuvert.dk/v1/media/med_2b7e4c9a1f3d48e6b0a5c8d2f1e7a4b9",
"bannerWidth": 1080,
"bannerHeight": 1350,
"salesCloseAt": "2026-10-03T21:00:00.000Z",
"status": "canceled",
"cancelNote": "Aftenen er aflyst. Alle billetter refunderes automatisk.",
"series": null,
"seriesDate": null,
"subtitle": "No Boyz On Deck",
"lineup": [
{
"name": "Faustix",
"role": "DJ",
"time": "2026-10-03T23:00:00.000Z"
}
],
"genres": [
"House",
"Techno"
],
"doorsAt": "2026-10-03T21:00:00.000Z",
"lastEntryAt": "2026-10-04T01:30:00.000Z",
"dressCode": null,
"ticketsUrl": null,
"host": {
"slug": "club-vortex",
"name": "Club Vortex"
},
"cohosts": []
}
}event.unpublished
Eine veröffentlichte Veranstaltung wurde auf Entwurf zurückgesetzt. Ein Entwurf sendet nie Ereignisse.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "event.unpublished",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"eventId": "evt_1a9c4e7b2d5f48a3b6c9e0d2f4a7b1c8",
"slug": "vortex-dimension-2026-10-03",
"name": "Vortex Dimension",
"startsAt": "2026-10-03T21:00:00.000Z",
"endsAt": "2026-10-04T03:00:00.000Z",
"venue": "Club Vortex",
"bannerUrl": "https://api.kuvert.dk/v1/media/med_2b7e4c9a1f3d48e6b0a5c8d2f1e7a4b9",
"bannerWidth": 1080,
"bannerHeight": 1350,
"salesCloseAt": "2026-10-03T21:00:00.000Z",
"status": "draft",
"cancelNote": null,
"series": null,
"seriesDate": null,
"subtitle": "No Boyz On Deck",
"lineup": [
{
"name": "Faustix",
"role": "DJ",
"time": "2026-10-03T23:00:00.000Z"
}
],
"genres": [
"House",
"Techno"
],
"doorsAt": "2026-10-03T21:00:00.000Z",
"lastEntryAt": "2026-10-04T01:30:00.000Z",
"dressCode": null,
"ticketsUrl": null,
"host": {
"slug": "club-vortex",
"name": "Club Vortex"
},
"cohosts": []
}
}Jede Lieferung trägt einen kuvert-signature-Header mit einem Zeitstempel und einem HMAC:
kuvert-signature: t=1753440000,v1=6f3a…Die Signatur ist HMAC-SHA256 in Hex über den String aus Zeitstempel, Punkt und dem rohen Body, berechnet mit dem Geheimnis Ihres Endpunkts:
HMAC_SHA256(secret, "{t}.{raw body}")# t og v1 fra kuvert-signature, body.json er den rå krop, præcis som den kom ind
printf '%s.%s' "$t" "$(cat body.json)" \
| openssl dgst -sha256 -hmac "$KUVERT_WEBHOOK_SECRET" -hex
# Sammenlign resultatet med v1- Bestätigen Sie mit 2xx, sobald Sie erhalten haben. Legen Sie die schwere Arbeit in eine Warteschlange.
- Die Zustellung erfolgt nach bestem Bemühen. Ein Endpunkt, der nicht erreichbar ist, hält nie einen Kauf beim Unternehmen auf.
- Wir warten höchstens 5 Sekunden auf die Antwort. Alles außer einem 2xx innerhalb dieser Zeit zählt als fehlgeschlagene Zustellung, auch eine Weiterleitung, der wir nicht folgen.
Schlägt eine Zustellung fehl, senden wir sie mit wachsenden Pausen erneut (1 Minute, 5 Minuten, 30 Minuten, 2 Stunden, 6 Stunden, 12 Stunden und 24 Stunden), insgesamt 8 Versuche über knapp zwei Tage. Es ist jedes Mal derselbe Body mit derselben ID, aber mit einer neuen Signatur, damit der Zeitstempel frisch ist. Erkennen Sie an der ID ein Ereignis, das Sie bereits empfangen haben: Kommt Ihre Antwort nicht bei uns an, senden wir es erneut.
Wie die letzte Zustellung verlaufen ist, steht beim Endpunkt: Endpunkte auflisten antwortet mit lastDeliveryAt, lastDeliveryOk, lastDeliveryDetail und consecutiveFailures, und dasselbe steht unter Einstellungen → Webhooks. Wir schalten einen Endpunkt nie von selbst ab. Nur Sie können ihn auf disabled setzen, und ein abgeschalteter Endpunkt erhält auch keine Wiederholungen.
Der Endpunkt muss eine öffentlich erreichbare HTTPS-Adresse sein. Interne Adressen werden bewusst abgelehnt, damit ein Endpunkt nicht benutzt werden kann, um in unser eigenes Netzwerk vorzudringen.
War diese Seite hilfreich?