# API

<https://kuvert.dk/dokumentation/api>

Partner-API'et lader dit eget system udstede, slå op, indløse og fortryde gavekort med en API-nøgle. Jeres hjemmeside kan desuden hente arrangementer, serier, priser og bordpakker uden nøgle — det står under Billetter › Programmet på jeres egen hjemmeside — og webhooks sender besked om gavekort, ordrer, billetter, bordbookinger og arrangementer. Det er almindelig HTTP med JSON — der er ingen SDK, du skal installere.

## Base-URL

```
https://api.kuvert.dk
```

## Godkendelse

Alle kald godkendes med en API-nøgle i `Authorization`-headeren som et bearer-token.

```bash
curl https://api.kuvert.dk/v1/api/giftcards \
  -H "Authorization: Bearer pk_live_..."
```

Nøgler oprettes under Indstillinger → API-nøgler. Kun ejeren kan oprette og tilbagekalde dem. Der findes to præfikser, og du vælger ikke selv mellem dem:

| Præfiks | Bruges til |
| --- | --- |
| pk_test_ | Afprøvning. Rører ikke rigtige penge. |
| pk_live_ | Drift. Arbejder på rigtige kort og saldi. |

Din butik sælger i præcis ét Stripe-miljø, og nøglen får det miljø. En demobutik får en pk_test_-nøgle; en butik, der sælger for rigtige penge, får en pk_live_. Nøgler fra det andet miljø afvises på første kald — derfor er der ingen knap, der kan vælge forkert.

> **Vigtigt:** Nøglen vises kun én gang, når den oprettes — vi gemmer kun et hash af den. Mister du den, tilbagekalder du den og opretter en ny. Læg den aldrig i kode, der sendes til en browser.

Alle kald er bundet til nøglens forretning. Du kan ikke se eller røre en anden forretnings kort, og en nøgle virker kun, mens forretningen er aktiv — en forretning, der endnu ikke har fuldført Stripe, eller som er suspenderet, får 401.

## Svarformat

Svar er JSON. Et gavekort ligger under nøglen giftCard, en liste under giftCards.

Beløb er altid heltal i øre, og tidspunkter er ISO 8601 i UTC. Hvert svar bærer en x-request-id-header — skriv den med, når du kontakter os om et bestemt kald, så kan vi finde netop det.

Lister er ikke sidedelte: GET /v1/api/giftcards svarer med alle forretningens kort på én gang, nyeste først.

## Fejl

En fejl har altid samme form: et objekt med ét felt kaldet error.

```json
{ "error": "unauthorized" }
```

| Status | error | Betyder |
| --- | --- | --- |
| 400 | invalid_issue_request | Kroppen matcher ikke det, udstedelse kræver. |
| 400 | invalid_redeem_request | Kroppen matcher ikke det, indløsning kræver. |
| 400 | bad_request | Kaldet mangler noget grundlæggende, typisk koden i stien. |
| 400 | not_a_gift_card | Koden i stien er en billetkode (TKT-…). Den hører til check-in, ikke til et gavekort — svaret er 400 og ikke 404, så du ikke går og leder efter et kort, der aldrig har eksisteret. |
| 401 | unauthorized | Nøglen mangler, er tilbagekaldt, eller forretningen er ikke aktiv. |
| 404 | gift_card_not_found | Koden findes ikke på denne forretning. |
| 409 | idempotency_key_conflict | Nøglen er brugt på et ANDET kort. En genbrugt nøgle gentager kun præcis det kald, den blev brugt til — den kan aldrig lykkes på et nyt kort. |
| 409 | cash_claim_cannot_be_denied | noCashClaim kan ikke sættes på et SOLGT kort. Et kort, kunden har betalt for, bærer retten til at få restbeløbet udbetalt kontant, og den ret kan et flag ikke fjerne — udsted som `b2b` eller `comp`. |
| 404 | merchant_not_found | Nøglen er gyldig, men forretningen bag den findes ikke længere. Sjældent, og ikke noget en klient kan rette — kontakt os. |
| 404 | order_not_found | Gensend fandt kortet, men ikke den ordre, det kom fra. Et kort udstedt i hånden har ingen ordre og kan derfor ikke gensendes ad den vej. |
| 409 | card_has_no_delivery | Kortet har aldrig været sendt af os — det blev udleveret over disken. Der er ingen levering at gentage. |
| 422 | invalid_recipient | `recipient` i kroppen på gensend er ikke en emailadresse, vi kan sende til. Udelad feltet for at sende til den adresse, kortet allerede står med. |
| 502 | delivery_failed | Vi nåede vores udbyder, og den afviste forsendelsen. Kortet fejler ingenting, og intet er trukket — prøv igen, eller kontrollér adressen. |
| 403 | key_environment_mismatch | Nøglen er gyldig, men fra det andet Stripe-miljø end det, din butik sælger i — en pk_test_ på en butik, der sælger for rigtige penge, eller omvendt. 403 og ikke 401: at logge ind igen hjælper ikke, du skal bruge den anden nøgle. |
| 404 | not_found | Stien findes ikke. Kontrollér metode og sti — en POST til en sti, der kun tager GET, svarer også sådan. |
| 429 | rate_limited | For mange kald for tæt. Retry-After-headeren siger, hvor mange sekunder der går, før du må igen — se Grænser. |
| 500 | internal_error | En uventet fejl hos os. Intet i svaret afslører hvilken; skriv x-request-id-headeren med til os, så finder vi det. |

Indløsning har sine egne svar oveni — kortets tilstand, gyldigheden, forsinkelsen og saldoen. De står samlet i tabellen under Indløs på Gavekort-siden. Ét af dem går igen: card_inactive svarer Fortryd en indløsning også med, men på en smallere gate — der er det kun et annulleret eller frosset kort, og den har sin egen tabel længere nede.

> **Bemærk:** Fejlfeltet hedder altid error — aldrig message. Skriv din fejlhåndtering efter det.

## Grænser

Du kan lave 240 kald pr. 60 sekunder mod /v1/api, talt i faste vinduer. Går du over, svarer vi 429 rate_limited med en Retry-After-header, der siger, hvor mange sekunder der er tilbage af vinduet.

> **Bemærk:** Grænsen tæller pr. IP-adresse, ikke pr. nøgle. Står flere kasser bag samme offentlige IP, deler de de 240 kald.

## Alle endepunkter

- `POST /v1/api/giftcards`
- `GET /v1/api/giftcards`
- `GET /v1/api/giftcards/{code}`
- `POST /v1/api/giftcards/{code}/redeem`
- `POST /v1/api/redemptions/{id}/reverse`
- `POST /v1/api/giftcards/{code}/resend`
- `POST /v1/api/giftcards/{code}/cancel`
- `POST /v1/api/orders/{id}/refund`
- `GET /v1/api/webhooks`
- `POST /v1/api/webhooks`
- `PATCH /v1/api/webhooks`
- `DELETE /v1/api/webhooks/{id}`
