# Events

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

Four public calls give your own website the same nights the shop shows: name, times, poster, line-up, prices, series and table packages. They need no API key.

The calls live under https://api.kuvert.dk/v1/merchants/{slug}, where {slug} is the shop’s address, e.g. demo-natklubben in shop.kuvert.dk/demo-natklubben. They are public like the shop itself: no key, no account, and only what a guest can see too. A night that is still a draft is never included.

> **Important:** Call them from your server, e.g. from a WordPress plugin or a build step, and not from browser JavaScript on your own site. The API only allows Kuvert’s own domains in the browser, so a fetch from another domain is stopped by the browser.

> **Note:** Feel free to keep the answer for a minute or two, but not for hours. Prices and places change while tickets sell, so let the ticket button go to the shop, where the figures are always right.

## The calls

| Call | Returns |
| --- | --- |
| `GET /v1/merchants/{slug}` | The shop and the nights on sale, in `events`, earliest first. With `?include=canceled` cancelled nights are included, and with `?include=closed` nights whose online sale has closed but which are not over. Both can be combined: `?include=canceled,closed`. |
| `GET /v1/merchants/{slug}/events/{event}` | One night with every price, what is left, and whether it can be bought right now. {event} is the night’s slug or id. |
| `GET /v1/merchants/{slug}/series` | The regular series, each with the nights of the series that are on sale. |
| `GET /v1/merchants/{slug}/table-nights` | The coming nights with table packages, and whether each package can be booked right now. |
| `GET /v1/merchants/{slug}/events/{event}/plan` | The club’s map for one night with table packages: the rooms and every table, free or sold. It says nothing about who bought. |

## The nights

The shop’s own call carries the nights in `events`. Each night has a name, times, venue and poster, and the fields a programme is built from. Here is an excerpt of the answer with one night:

```json
{
  "events": [
    {
      "id": "evt_65694ef606f6483083d368ec040601e2",
      "slug": "klubnat-2026-10-17",
      "name": "Klubnat",
      "startsAt": "2026-10-17T21:00:00.000Z",
      "endsAt": "2026-10-18T03:00:00.000Z",
      "venue": "Natklubben, Vejle",
      "bannerUrl": null,
      "bannerWidth": null,
      "bannerHeight": null,
      "salesCloseAt": "2026-10-18T00:00:00.000Z",
      "status": "published",
      "cancelNote": null,
      "series": {
        "id": "ser_216c1567afbb42368340cb9b11146a90",
        "name": "Klubnat",
        "weekdays": [
          5,
          6
        ],
        "isRecurring": true,
        "recurrence": {
          "kind": "weekly",
          "weekdays": [
            5,
            6
          ]
        },
        "description": "Hver fredag og lørdag"
      },
      "seriesDate": "2026-10-17",
      "subtitle": "Husets DJs på to gulve",
      "lineup": [
        {
          "name": "Kasper Ø",
          "role": "warm-up",
          "time": "2026-10-17T21:00:00.000Z"
        },
        {
          "name": "Selma Rask",
          "role": "resident",
          "time": "2026-10-17T23:00:00.000Z"
        }
      ],
      "genres": [
        "House",
        "Hiphop",
        "Top 40"
      ],
      "doorsAt": "2026-10-17T21:00:00.000Z",
      "lastEntryAt": "2026-10-18T01:00:00.000Z",
      "dressCode": "Ingen sportstøj",
      "ticketsUrl": null,
      "host": {
        "slug": "demo-natklubben",
        "name": "Natklubben"
      },
      "cohosts": []
    }
  ]
}
```

| Field | Means |
| --- | --- |
| `id, slug` | The night’s id and its readable address. The night’s page in the shop is shop.kuvert.dk/{slug}/event/{slug}, with the id if the night has no slug. |
| `startsAt, endsAt` | When the night starts and ends. `endsAt` is `null` if the night does not say. |
| `salesCloseAt` | When the online sale closes. |
| `bannerUrl, bannerWidth, bannerHeight` | The poster and its size in pixels, so the page can hold its space before it has loaded. Show it whole: it is drawn to be seen uncropped. |
| `status, cancelNote` | `published` or `canceled`, and the message you wrote when the night was cancelled. |
| `series, seriesDate` | The series behind the night with name, weekdays (0 is Sunday), the rule and a ready sentence to show, e.g. «Hver fredag og lørdag», and which of the series’ days it is. `null` on a night you created yourselves. |
| `subtitle, genres, dressCode` | The subtitle, the genres and the dress code, as you wrote them. |
| `lineup` | The names in running order, each with `name`, `role` and `time`. `time` includes the date, so a set after midnight lands on the right day. |
| `doorsAt, lastEntryAt` | When the doors open, and when the last guest is let in. |
| `ticketsUrl` | Where the tickets are sold when someone else sells them, e.g. the promoter. If the night has its own prices in Kuvert, they are sold here and the field is not used. |

> **Note:** All times are in UTC. 21:00Z is 23:00 in Danish summer time and 22:00 in winter, so convert to Europe/Copenhagen before you show them.

## One night

The night’s own call gives you everything: the description, the age limit and every price in `tiers`. `onSale` says whether it can be bought right now, and `refusalCode` why not, e.g. `soldOut` or `closed`. `waitlistOpen` is `true` when the guest can join the waiting list.

```json
{
  "id": "evt_65694ef606f6483083d368ec040601e2",
  "slug": "klubnat-2026-10-17",
  "name": "Klubnat",
  "startsAt": "2026-10-17T21:00:00.000Z",
  "endsAt": "2026-10-18T03:00:00.000Z",
  "venue": "Natklubben, Vejle",
  "bannerUrl": null,
  "bannerWidth": null,
  "bannerHeight": null,
  "description": "Gratis før midnat på gæstelisten, entré ved døren derefter. Borde med flasker hele natten.",
  "ageLimit": 18,
  "tiers": [
    {
      "id": "etr_e5e5766ded1e456599d7e8925f68bff9",
      "name": "Entré",
      "description": "Spring billetkøen over. Gælder hele natten.",
      "priceOre": 10000,
      "availability": "on_sale",
      "remaining": null,
      "scarcity": null,
      "salesOpenAt": null,
      "salesCloseAt": null,
      "seatsPerTicket": 1,
      "seatsChosenByGuest": false,
      "creditPromisedOre": null,
      "tablePackage": false,
      "includedItems": [],
      "minSeats": 1,
      "arriveBy": null,
      "seller": {
        "slug": "demo-natklubben",
        "name": "Natklubben"
      }
    },
    {
      "id": "etr_773b122865f0469e81c06781c4befa63",
      "name": "Flaske",
      "description": null,
      "priceOre": 240000,
      "availability": "on_sale",
      "remaining": 10,
      "scarcity": null,
      "salesOpenAt": null,
      "salesCloseAt": null,
      "seatsPerTicket": 10,
      "seatsChosenByGuest": true,
      "creditPromisedOre": null,
      "tablePackage": true,
      "picksTable": true,
      "tableSeats": {
        "min": 4,
        "max": 10
      },
      "tablesFree": 10,
      "includedItems": [
        "1 × Absolut Vodka 70 cl",
        "6 × Red Bull",
        "Entré for hele bordet"
      ],
      "minSeats": 1,
      "arriveBy": "2026-10-17T22:30:00.000Z",
      "seller": {
        "slug": "demo-natklubben",
        "name": "Natklubben"
      }
    }
  ],
  "minQuantity": 1,
  "salesCloseAt": "2026-10-18T00:00:00.000Z",
  "pickups": [],
  "remaining": 350,
  "onSale": true,
  "refusal": null,
  "refusalCode": null,
  "refusalCount": null,
  "waitlistOpen": false,
  "questions": [],
  "status": "published",
  "cancelNote": null,
  "series": {
    "id": "ser_216c1567afbb42368340cb9b11146a90",
    "name": "Klubnat",
    "weekdays": [
      5,
      6
    ],
    "isRecurring": true,
    "recurrence": {
      "kind": "weekly",
      "weekdays": [
        5,
        6
      ]
    },
    "description": "Hver fredag og lørdag"
  },
  "seriesDate": "2026-10-17",
  "subtitle": "Husets DJs på to gulve",
  "lineup": [
    {
      "name": "Kasper Ø",
      "role": "warm-up",
      "time": "2026-10-17T21:00:00.000Z"
    },
    {
      "name": "Selma Rask",
      "role": "resident",
      "time": "2026-10-17T23:00:00.000Z"
    }
  ],
  "genres": [
    "House",
    "Hiphop",
    "Top 40"
  ],
  "doorsAt": "2026-10-17T21:00:00.000Z",
  "lastEntryAt": "2026-10-18T01:00:00.000Z",
  "dressCode": "Ingen sportstøj",
  "ticketsUrl": null,
  "host": {
    "slug": "demo-natklubben",
    "name": "Natklubben"
  },
  "cohosts": []
}
```

| Field | Means |
| --- | --- |
| `priceOre` | The price in øre: 10000 is DKK 100. |
| `availability` | `on_sale`, `not_yet` (opens later), `closed` or `sold_out`. |
| `remaining, scarcity` | How many are left at that price, and what the shop shows about it: the number (`count`), «few left» (`few`) or nothing (`null`). Show what `scarcity` says, not the number. `remaining` is `null` when the price has no count of its own. |
| `salesOpenAt, salesCloseAt, opensAfterTierId` | The price’s own sales window, and the price it waits to sell out when tickets are sold in tiers. |
| `tablePackage, picksTable, tableSeats, tablesFree` | A table package: whether the guest picks the table on the map, the smallest and largest table, and how many tables are free. |
| `includedItems, creditPromisedOre` | What comes with a package, and the bar credit if there is one. |
| `ageLimit` | The age limit at the door, or `null`. |

## Series

If you hold the same night every week, the series give it to you as one row: «Hver fredag og lørdag», and the nights of the series that are on sale. A series whose nights are all still drafts is not included.

```json
{
  "series": [
    {
      "id": "ser_216c1567afbb42368340cb9b11146a90",
      "name": "Klubnat",
      "weekdays": [
        5,
        6
      ],
      "isRecurring": true,
      "recurrence": {
        "kind": "weekly",
        "weekdays": [
          5,
          6
        ]
      },
      "description": "Hver fredag og lørdag",
      "nights": [
        {
          "id": "evt_65694ef606f6483083d368ec040601e2",
          "slug": "klubnat-2026-10-17",
          "name": "Klubnat",
          "startsAt": "2026-10-17T21:00:00.000Z",
          "seriesDate": "2026-10-17"
        },
        {
          "id": "evt_0ba0fd9fb19c42cd80f4ebc11879b462",
          "slug": "klubnat-2026-10-23",
          "name": "Klubnat",
          "startsAt": "2026-10-23T21:00:00.000Z",
          "seriesDate": "2026-10-23"
        }
      ]
    }
  ]
}
```

## Table packages

If you sell tables for your nights, this call gives the coming nights with their table packages, so the page can put «Book bord» beside each night without fetching every night’s page. `bookable` says whether the package can be booked right now.

```json
{
  "nights": [
    {
      "id": "evt_65694ef606f6483083d368ec040601e2",
      "slug": "klubnat-2026-10-17",
      "name": "Klubnat",
      "startsAt": "2026-10-17T21:00:00.000Z",
      "endsAt": "2026-10-18T03:00:00.000Z",
      "venue": "Natklubben, Vejle",
      "bannerUrl": null,
      "bannerWidth": null,
      "bannerHeight": null,
      "ageLimit": 18,
      "onSale": true,
      "packages": [
        {
          "id": "etr_773b122865f0469e81c06781c4befa63",
          "name": "Flaske",
          "description": null,
          "priceOre": 240000,
          "availability": "on_sale",
          "remaining": 10,
          "scarcity": null,
          "salesOpenAt": null,
          "salesCloseAt": null,
          "seatsPerTicket": 10,
          "seatsChosenByGuest": true,
          "creditPromisedOre": null,
          "tablePackage": true,
          "picksTable": true,
          "tableSeats": {
            "min": 4,
            "max": 10
          },
          "tablesFree": 10,
          "includedItems": [
            "1 × Absolut Vodka 70 cl",
            "6 × Red Bull",
            "Entré for hele bordet"
          ],
          "minSeats": 1,
          "arriveBy": "2026-10-17T22:30:00.000Z",
          "seller": {
            "slug": "demo-natklubben",
            "name": "Natklubben"
          },
          "bookable": true
        }
      ]
    }
  ]
}
```

## Keep the programme up to date

If your site needs to know the moment a night changes, it can subscribe to webhooks instead of asking every minute: event.published when a night goes on sale, event.updated when something public changes (with the names of the fields in `changedFields`), event.canceled and event.unpublished. Each delivery carries the whole night in the same form as the list above, with `eventId` instead of `id`. A webhook endpoint is created under Settings → Webhooks or with an API key.

## An example

The smallest thing that works: fetch the nights on your server, and build a link to each night’s page in the shop.

```javascript
const res = await fetch('https://api.kuvert.dk/v1/merchants/demo-natklubben')
const { events } = await res.json()

for (const night of events) {
  const link = `https://shop.kuvert.dk/demo-natklubben/event/${night.slug ?? night.id}`
  console.log(night.name, night.startsAt, night.status, link)
}
```

- [Webhooks and signatures](https://kuvert.dk/en/dokumentation/api/webhooks)
- [Tickets: your programme on your own website](https://kuvert.dk/en/dokumentation/billetter#hjemmeside)
- [Embedding: a buy button beside each night](https://kuvert.dk/en/dokumentation/indlejring)

## Shared nights

A night can be shared by several businesses, e.g. a promoter who sells the tickets and the club that sells the tables. The night belongs to the host, but it is in the programme of all of them and can be fetched under each of their slugs, with the same id and the same slug. It is counted once in each business’s list.

| Field | Means |
| --- | --- |
| `host` | The business the night belongs to, with `slug` and `name`. The night’s real address is shop.kuvert.dk/{host.slug}/event/{slug}, also when you fetch it under your own slug. |
| `cohosts` | The other businesses selling on the night, in the order they joined. An empty list on a night that is not shared. |
| `tiers[].seller` | Who sells the price, and who the guest pays. Use it to show your own prices on their own or to say who sells what. |
| `lineup[].merchantSlug` | The performer’s own shop on Kuvert, when the host has linked the name to it. |

GET /v1/merchants/{slug}/appearances gives the nights other businesses have booked you for, either as co-host or in the line-up, in the same form as the list above plus `seller` (the business whose page sells the night) and `appearance` (`cohost` or `lineup`). An artist’s site can then show the club nights the artist plays without anybody typing them in again. A night you co-host yourself is also on your own list above, so show only one of them, or filter on `appearance`.

Webhooks for a shared night (event.*) go to every business on the night. order.paid and ticket.issued go only to the one that sold, and carry `seller`, `host` and `cohosts`, so your system can tell your own sales from your partner’s.

- [Share a night with another business](https://kuvert.dk/en/dokumentation/del-en-aften)
