Under Innstillinger → Webhooks legger du til URL-en hendelsene skal sendes til. Du får en hemmelighet tilbake. Den brukes til å verifisere signaturen og skal oppbevares som et passord.
Et system kan også abonnere på seg selv med en API-nøkkel, uten at noen åpner dashbordet. Det er slik en integrasjon du ikke selv har skrevet kobler seg på: den får din nøkkel og registrerer sin egen adresse.
List endepunkter
/ v1/ api/ webhooksBedriftens endepunkter, også de som er opprettet i dashbordet, hvert med sin hemmelighet og utfallet av den siste leveringen.
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"
}
]
}Svarfelter
Endepunktene ligger under endpoints. Hvert har disse feltene:
idstring- Endepunktets id (
weh_…). urlstring- Adressen hendelsene sendes til.
eventsstring[]- Hendelsene endepunktet abonnerer på. En tom liste betyr alle.
status"active" | "disabled"- Bare
activefår leveranser. secretstring- Hemmeligheten (
whsec_…) signaturen regnes ut med. lastDeliveryAtstring | null- Når det sist ble forsøkt en leveranse.
nullhvis ingenting er sendt. lastDeliveryOkboolean | null- Om den siste leveringen fikk 2xx.
nullbetyr aldri forsøkt og ikke feilet. lastDeliveryDetailstring | null- Hvorfor den siste feilet: en HTTP-status eller en transportfeil.
nullnår den lyktes. consecutiveFailuresinteger- Feilede leveranser på rad. Den første som lykkes, nullstiller tallet.
createdAtstring- Når endepunktet ble opprettet.
Statuskoder
200
Listen, eventuelt tom.
En feil har alltid formen { "error": "…" }. Alle kall kan dessuten svare 401, 403, 429 og 500. Se Feil.
Brukes ofte sammen med
Legg til et endepunkt
/ v1/ api/ webhooksRegistrerer en adresse og svarer med endepunktet og hemmeligheten. Det får leveranser med en gang.
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"
}
}Kropp
urlstringPåkrevd- En offentlig https-adresse, høyst 2048 tegn. Interne og lokale adresser avvises.
eventsstring[]Påkrevd- Hendelsene som skal sendes. En tom liste betyr alle, også de som kommer til senere.
Svarfelter
Endepunktet ligger under endpoint, med de samme feltene som ved List endepunkter.
Statuskoder
201
Endepunktet er opprettet.
400
invalid_webhook_endpointKroppen stemmer ikke: adressen er ikke en offentlig https-adresse, eller en hendelse finnes ikke.
En feil har alltid formen { "error": "…" }. Alle kall kan dessuten svare 401, 403, 429 og 500. Se Feil.
Brukes ofte sammen med
Endre et endepunkt
/ v1/ api/ webhooksEndrer adresse, hendelser eller status. Endepunktets id står i kroppen, ikke i stien. Send bare feltene du vil endre.
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"
}
}Kropp
idstringPåkrevd- Endepunktets id (
weh_…). urlstringValgfri- Ny adresse, med de samme kravene som ved opprettelse.
eventsstring[]Valgfri- En ny liste over hendelser. Den erstatter den gamle helt.
status"active" | "disabled"Valgfridisabledstanser leveransene uten å slette endepunktet.
Svarfelter
Det endrede endepunktet ligger under endpoint.
Statuskoder
200
Endepunktet er endret.
400
invalid_requestKroppen stemmer ikke, som regel fordi
idmangler.404
webhook_endpoint_not_foundEndepunktet finnes ikke hos bedriften din.
En feil har alltid formen { "error": "…" }. Alle kall kan dessuten svare 401, 403, 429 og 500. Se Feil.
Brukes ofte sammen med
Slett et endepunkt
/ v1/ api/ webhooks/ {id}Fjerner endepunktet for godt. Vil du bare ta en pause, setter du status til disabled i stedet.
curl -X DELETE https://api.kuvert.dk/v1/api/webhooks/weh_5e8a1c3f7b9d42e6a0c4f8b2d6e1a3c9 \
-H "Authorization: Bearer $KUVERT_API_KEY"200 OKJSON{
"deleted": true
}Stiparametere
idstringPåkrevd- Endepunktets id (
weh_…).
Svarfelter
deletedboolean- Alltid
true.
Statuskoder
200
Endepunktet er slettet.
404
webhook_endpoint_not_foundEndepunktet finnes ikke hos bedriften din.
En feil har alltid formen { "error": "…" }. Alle kall kan dessuten svare 401, 403, 429 og 500. Se Feil.
Brukes ofte sammen med
Hver hendelse sendes som en POST med en JSON-kropp i fire felter: id er leveransens egen id (whevt_…), type er hendelsen, createdAt er tidspunktet, og data er det hendelsen handler om. Tre headere følger med: content-type, kuvert-event-type og 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}}Signaturen i eksempelet er ekte. Med hemmeligheten whsec_docs-example-not-a-real-secret gir oppskriften under Signaturen nøyaktig den v1. Kjør din egen verifisering på den, med tidstoleransen slått av, siden tidspunktet ligger i fortiden.
giftcard.issued
Et gavekort er utstedt.
{
"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
Et beløp er trukket fra et kort.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "giftcard.redeemed",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"code": "KUV-7K9F-2XQD",
"amountOre": 6400,
"remainingBalanceOre": 43600
}
}order.paid
En ordre er betalt. På en billettordre følger kjøperens svar på kveldens spørsmål med i answers, for eksempel et lagnavn, hvert med spørsmålet slik det lød da det ble besvart.
{
"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
En ordre er refundert.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "order.refunded",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"orderId": "ord_7d3f0b9c1e2a45f68b4c9d0e1f2a3b5c",
"refundedOre": 50500
}
}ticket.issued
En billett er utstedt av en betalt ordre. Ordrens svar på kveldens spørsmål følger med i answers på hver billett.
{
"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
Kjøperens kortutssteder har bestridt betalingen. Ordren sine gavekort og billetter er satt på pause og kan ikke brukes.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "order.disputed",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"orderId": "ord_7d3f0b9c1e2a45f68b4c9d0e1f2a3b5c",
"frozenCards": 1,
"frozenTickets": 0
}
}order.dispute_closed
Saken er avgjort. won: true betyr at pauseringen er hevet og kortene virker igjen; false at de er annullert.
{
"id": "whevt_c81f5a2d9e3b47c6a0d4e8f1b5c9a2d7",
"type": "order.dispute_closed",
"createdAt": "2026-09-29T09:12:44.118Z",
"data": {
"orderId": "ord_7d3f0b9c1e2a45f68b4c9d0e1f2a3b5c",
"won": true,
"disputedOre": 50500
}
}booking.created
Et bord er booket av gjesten på bookingsiden, på telefon eller i døra.
{
"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
En bordreservasjon har fått nytt tidspunkt, ny dato, nytt antall, nye bord eller en ny status. Hele bookingen følger med, og status sier hvor den står.
{
"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
En bordreservasjon er avbestilt, av gjesten eller av restauranten. Bookinger som flyttes inn fra et annet system, sender ingen hendelser.
{
"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
Et arrangement er satt i salg. Hele kvelden følger med (navn, tider, sted, serie, line-up og sjangre) i samme form som listen på GET /v1/merchants/{slug}, med eventId i stedet for 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
Noe offentlig på et publisert arrangement er endret. changedFields sier hva, for eksempel lineup, eller tiers når en pris er endret.
{
"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
Et publisert arrangement er avlyst. cancelNote er beskjeden dere skrev til gjestene.
{
"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
Et publisert arrangement er satt tilbake til kladd. En kladd sender aldri hendelser.
{
"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": []
}
}Hver levering bærer en kuvert-signature-header med et tidsstempel og en HMAC:
kuvert-signature: t=1753440000,v1=6f3a…Signaturen er HMAC-SHA256 i hex over strengen tidsstempel, punktum, den rå kroppen, beregnet med endepunktets hemmelighet:
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- Kvitter med 2xx så snart du har mottatt. Legg det tunge arbeidet i en kø.
- Levering skjer etter beste evne. Et endepunkt som er nede, holder aldri tilbake et kjøp hos bedriften.
- Vi venter høyst 5 sekunder på svaret. Alt annet enn 2xx innen da teller som en mislykket levering, også en omdirigering, som vi ikke følger.
Feiler en levering, sender vi den på nytt med økende pauser (1 minutt, 5 minutter, 30 minutter, 2 timer, 6 timer, 12 timer og 24 timer), totalt 8 forsøk over knapt to døgn. Det er den samme kroppen med den samme id-en hver gang, men med en ny signatur, så tidsstempelet er ferskt. Bruk id-en til å kjenne igjen en hendelse dere allerede har tatt imot: kommer ikke svaret fram, sender vi den på nytt.
Hvordan den siste leveringen gikk, står på endepunktet: List endepunkter svarer med lastDeliveryAt, lastDeliveryOk, lastDeliveryDetail og consecutiveFailures, og det samme står under Innstillinger → Webhooks. Vi slår aldri av et endepunkt på egen hånd. Bare du kan sette det til disabled, og et endepunkt som er slått av, får heller ingen nye forsøk.
Endepunktet må være en offentlig tilgjengelig HTTPS-adresse. Interne adresser avvises med vilje, så et endepunkt ikke kan brukes til å nå inn i vårt eget nettverk.
Var siden nyttig?