Skip to main content

Build your own bookingkit reports with an AI assistant

Step-by-step guide for vendors on how to use an AI assistant (e.g. Claude) and the bookingkit API v3 to build fully custom CSV reports on their own computer.

Written by Björn Blankenhagen

Applies to: BusinessKit and above · API v3 · Mac and Windows

⚠️ API usage note: Building and running reports this way consumes your bookingkit API quota. A monthly order report typically uses a few hundred to a few thousand requests per run. You have a monthly budget of 500,000 requests and will receive warning emails at 50 %, 80 %, 90 % and 100 %.

What you get

A report of your bookingkit orders — exactly the columns you need, in exactly the order you need them — as a CSV file on your own computer that opens in Excel. You describe the report in plain language; an AI assistant writes and runs the code for you.

Once it is set up, producing next month's report takes one sentence.

This is the right tool if you need the same report every month with a fixed column layout, you need more data than the standard export gives you, or your report has to combine several things (for example revenue and participant numbers per cost centre).

This is not the right tool if you just want to look something up once — use the standard exports in bookingkit for that.

Why not Google Sheets? Reports built as Google Apps Script hit Google's limits: scripts are stopped after a few minutes and Google throttles the number of requests they may make. A report covering a whole month of orders needs several hundred requests and regularly runs into this. Running on your own computer removes both limits, and it can remember previously fetched data so repeat runs are much faster.

Before you start

1. Check your plan. API access requires a BusinessKit account or higher.

2. Request API credentials. There is no self-service screen for this — contact bookingkit support via chat and ask for API v3 credentials. Tell them:

  • you want read-only reporting access;

  • the scopes you need: orders_read_owned (or orders_read_all if you are a marketplace) and calendar_read;

  • that you would also like sandbox credentials for testing.

You will receive a Client ID and a Client Secret. Keep them somewhere safe — they give access to your booking data.

3. Install an AI assistant that can work with files on your computer. Any of these works:

A chat window in a browser is not enough on its own, because it cannot run the report for you. If that is all you have, the assistant can still write the script and you run it yourself — but the setup below is much easier.

Step 1 — Make a folder

Create an empty folder on your computer, for example bookingkit-reports in your Documents. Open your AI assistant in that folder.

In Claude Code: open Terminal, type cd (with a space), drag the folder onto the Terminal window, press Enter, then type claude.

Step 2 — Paste the setup prompt

Copy everything in the box below and paste it into the assistant. You only ever do this once.

I want to build a custom report from the bookingkit API v3 in this folder.
I am not a programmer, so please explain each step in plain language and do
not ask me to edit any code.

Please open this page and follow the "Agent instructions" section on it
exactly:
https://help.bookingkit.com/en/articles/16734302-build-your-own-bookingkit-reports-with-an-ai-assistant

If you cannot open web pages, tell me and I will paste those instructions in
myself.

Start by setting up the folder and telling me what you need from me.

The assistant will check what is installed on your computer, set up the folder, and create a file for your credentials.

If your assistant cannot open links, copy the Agent instructions section from the end of this article and paste it in instead. It is the same content.

Step 3 — Add your credentials

The assistant creates a file called credentials.txt with the values left empty. Open it in a text editor, fill in the Client ID and Client Secret you got from support, and save it.

Do not paste your Client Secret into the chat. Anything you type into an AI assistant is sent to that company's servers. Putting it in the file instead keeps it on your computer — the script reads it directly.

Leave environment=sandbox while you are testing. Change it to production once the numbers look right.

Step 4 — Describe your report

Tell the assistant what you want. Copy this template and fill it in — the more precise you are, the less back-and-forth:

Report name:        e.g. Monthly cost centre report
Date basis:        service date (when the activity happens)
                   OR booking date (when the order was placed)
Date range:        e.g. always the last complete calendar month
Which orders:      accepted only, or including cancellations
One row per:       order (recommended), ticket, or participant
Columns, in order: e.g. booking code, booking date, service date, event name,
                   participants, revenue gross, revenue net, payment method
Filters:           e.g. only events containing "Castle tour"
Exclude:           e.g. any event containing "Kombiticket"
VAT:               e.g. 0% for cost centre X, otherwise use the API value
Output:            e.g. out/cost-centre-report_2026-07.csv
Excel language:    German or English

Not sure about date basis? Accounting reports almost always want the service date — the month the activity actually took place, not the month the customer booked it.

The assistant will fetch the data, write the CSV, and show you a summary.

Step 5 — Check the numbers

Before you rely on the report, compare it against your standard bookingkit export for the same month. Check the number of orders and the total revenue.

If they do not match, say so — the assistant knows the usual causes and can fix them. The common ones are cancelled bookings being included or excluded when they shouldn't be, combination tickets counted twice, or gross and net being mixed up.

Every month after this

Open the folder, start your assistant, and say:

run the monthly order report for last month

That is all. The assistant wrote down your report specification the first time, so it already knows the columns, filters and format. Come back to this article only when you want to change what the report contains.

Troubleshooting

What you see

What it means

What to do

429 with error_code 42901

The script asked for a second access token while one was still valid. Only one per hour is allowed.

Tell the assistant to reuse the cached token instead of requesting a new one.

429 with error_code 42910

Your monthly limit of 500,000 API requests is used up.

Waiting will not help before the 1st of next month. Contact support to raise the limit.

401 Unauthorized

Wrong credentials, or the token expired.

Check credentials.txt for typos and stray spaces. Confirm with support that your scopes include orders_read_owned.

403 or empty results on production

Your credentials may be sandbox-only, or the scope is missing.

Check environment= in credentials.txt, then ask support to confirm your scopes.

The event name column is empty

Request events are missing from the event list.

Tell the assistant: "pass type=ALL when fetching vendor events".

Cancellations are missing

The API returns only accepted orders by default.

Tell the assistant you want status=ALL.

Revenue is too high

Combination tickets are counted twice, or cancelled participants are included.

Ask for a combination-ticket exclusion, and for both cancellation levels to be checked.

Totals are slightly off

Gross and net are mixed up. Ticket prices are gross — net has to be calculated from the VAT rate.

Ask the assistant to re-check the gross/net conversion.

Excel shows ü instead of ü

The file was saved without a byte order mark.

Ask for the CSV to be written as UTF-8 with BOM.

Excel puts everything in one column

Your Excel expects a different separator.

Tell the assistant which Excel language you use.

Numbers cannot be summed in Excel

They were written as text.

Ask for plain numbers with a decimal comma and no currency symbol.

The script stops halfway

A request failed. This is deliberate — a half-finished report is worse than none.

Ask the assistant to show the error.

Good to know

  • Everything stays on your computer. The script talks only to bookingkit. The report file never leaves your machine.

  • Reports are read-only. Nothing the assistant builds here can change or cancel a booking.

  • Test in the sandbox first. bookingkit warns that operations against the production environment carry real costs.

  • You have a monthly request budget of 500,000 API requests. A monthly order report uses a few hundred to a few thousand, so this is generous — but you will get warning emails at 50 %, 80 %, 90 % and 100 %.

Getting help

If the assistant gets stuck, ask it to explain what it tried, and share that with bookingkit support along with the report specification from Step 4.

Reference documentation:


Agent Instructions

The following instructions are for AI assistants building and running reports on behalf of bookingkit vendors.

# Bookingkit report agent — standing instructions

You are helping a bookingkit customer build and run a custom report from the
bookingkit API v3 on their own computer. Assume the person you are talking to is
**not a programmer**. Explain what you are doing in plain language, one step at a
time, and never ask them to edit code.

## 0. Hard rules

1. **Read-only.** Use only `GET` requests. Never `POST`, `PATCH` or `DELETE`
   against the API, except the one `POST /oauth/token` needed to authenticate.
   A reporting task never modifies bookings.
2. **Never print, echo, log or repeat the client secret**, and never write it
   into the report script, a commit, or your replies. Read it from the
   credentials file at runtime only.
3. **Sandbox first.** If the customer has sandbox credentials, use
   `https://api-sandbox.bookingkit.de` until the report looks right. bookingkit
   warns that operations against production carry real costs.
4. **Fail loudly.** Never write an output file from an incomplete fetch. If any
   request fails after retries, stop with a clear error naming the failed URL.
   A truncated report that looks complete is the worst possible outcome — the
   customer will book wrong numbers on it.
5. **Do not invent field names.** If a field you expect is missing, check the
   spec (below) and tell the customer rather than guessing.

## 1. Read these first

- **API spec (authoritative):**
  https://developers.bookingkit.com/redocusaurus/apiv3.yaml
  This is **Swagger 2.0**, not OpenAPI 3 — expect `host` + `basePath` and
  `#/definitions/...`, not a `servers` block. Fetch it and use it as the source
  of truth for every endpoint, query parameter and response field.
- **Rate limits:** https://developers.bookingkit.com/docs/api/api_v3/rate-limit
- **Authentication:** https://developers.bookingkit.com/docs/api/api_v3/authentication

There is no `llms.txt` on the portal. Ignore the `from` / `to` parameters shown
in the portal's Best Practices page — **they do not exist**; the real parameters
are `start_date` / `end_date` (see §6).

## 2. Set up the runtime

Check what is already installed, in this order, and use the first one you find:

1. `python3 --version`
2. `python --version` (must be 3.x)
3. `node --version` (must be 18 or newer, for built-in `fetch`)

**Use the standard library only.** Do not run `pip install` or `npm install` —
a dependency that fails to install is the most common way this goes wrong for a
non-technical user. Python's `urllib.request`, `json`, `csv`, `datetime` and
`concurrent.futures` are enough; so are Node's `fetch` and `node:fs`.

If none of the three is available, walk the customer through installing Python,
one instruction at a time:

- **macOS:** the official installer from https://www.python.org/downloads/macos/
  (or `brew install python` if they already have Homebrew).
- **Windows:** `winget install Python.Python.3.12` in Terminal, or the installer
  from https://www.python.org/downloads/windows/. Tell them to tick
  **"Add python.exe to PATH"**.

## 3. Create the working folder

In the folder the customer chose, create:

```
AGENTS.md                  <- these instructions plus their report spec
credentials.txt            <- you create it, empty, for the customer to fill in
report.py                  <- or report.js
cache/                     <- cached API responses
out/                       <- finished CSV files
.gitignore                 <- must list credentials.txt and cache/
```

`credentials.txt`:

```
# Fill in the values you received from bookingkit support, then save this file.
# Do not share it with anyone.
client_id=
client_secret=
vendor_id=
# environment: sandbox or production
environment=sandbox
```

Create this file yourself with the values left empty, then ask the customer to
open it **in their text editor**, fill in the values and save it. Do **not**
offer to do it for them and do not ask them to paste the secret into the chat —
that would send it to the AI provider.

Write these standing instructions into `AGENTS.md`, then append the customer's
report specification as you settle it. This is what lets the next run be a single
sentence instead of a re-paste.

## 4. Authentication

- Token endpoint: `POST https://api.bookingkit.de/oauth/token`
  (sandbox: `https://api-sandbox.bookingkit.de/oauth/token`).
  Note it is **not** under `/v3`.
- Body, form-encoded (`application/x-www-form-urlencoded`):
  `grant_type=client_credentials&client_id=...&client_secret=...`
- Response: `access_token`, `token_type: Bearer`, `expires_in: 3600`, `scope`.
- API base: `https://api.bookingkit.de/v3`
  (sandbox: `https://api-sandbox.bookingkit.de/v3`).
- Send `Authorization: Bearer ` and `Accept: application/json`.
  A `?access_token=` query parameter also works but is discouraged — **never
  send both**.

**Cache the token to `cache/token.json` together with its expiry timestamp, and
reuse it across runs.** Only **one active token per client per hour** exists.
Requesting a second one while the first is alive returns HTTP 429 with
`error_code 42901` — `"please reuse your oauth tokens"`. Refresh only when the
cached token is expired or has under ~60 seconds left. There are no refresh
tokens; an expired token gives 401.

## 5. Fetching: pagination, politeness, caching

**Pagination** is offset-based: `limit` and `offset`. The spec's default `limit`
is `100000`, so always set it explicitly — use `limit=500`. Request pages until a
page comes back with fewer than `limit` items. Never treat an HTTP error as
"end of data" — that is exactly the silent-truncation bug in rule 4.

Do **not** use `with_count`; the spec itself advises against it because it slows
every request. For very large ranges, slice the date window into months rather
than paging deep with large offsets (the Best Practices page warns against deep
offsets combined with sorting).

**Politeness and retries.** The API documents no per-second limit and sends no
`Retry-After`, so impose your own behaviour:

- At most **4–6 concurrent requests**.
- Retry on 5xx and on 429 with exponential backoff (e.g. 2s, 4s, 8s, 16s).
- On 429, read `error_code`: **42901** means you asked for a second token —
  reuse the cached one, do not spam the token endpoint. **42910** means the
  client's **monthly quota of 500,000 requests** is exhausted; stop immediately
  and tell the customer to contact bookingkit support. Backoff will not help.
- Print the total number of requests made at the end of the run, so the customer
  can see their quota usage.

**Cache to disk under `cache/`**, keyed by URL: `/dates/{id}`, `/events/{id}` and
per-order `payments`. These are the expensive per-item lookups, and caching them
is the main reason a local script beats Google Apps Script — re-running the same
month should cost almost nothing. Cache negative results too, so a missing record
is not re-fetched on every run.

## 6. Endpoints for an order report

| Purpose | Endpoint |
|---|---|
| All events of the vendor (for names) | `GET /v3/vendors/{vendor_id}/events?type=ALL` |
| The orders | `GET /v3/vendors/{vendor_id}/orders` |
| Payment method per order | `GET /v3/orders/{id}/payments` |
| Service date of a ticket | `GET /v3/dates/{id}` |
| Single event fallback | `GET /v3/events/{id}` |

Useful `GET /orders` parameters: `start_date`, `end_date` (when the order was
**placed**), `start_event_date`, `end_event_date` (by **service** date),
`status`, `fields`, `order`, `delta_since`, `limit`, `offset`.

All timestamps must match `YYYY-MM-DDThh:mm:ssZ` — UTC, with seconds and a
trailing `Z`. `2026-08-01` alone will be rejected.

## 7. Correctness gotchas — read before writing any code

None of the following can be derived from the spec. Each one has produced a wrong
report in practice.

1. **`type=ALL` on vendor events.** `GET /vendors/{id}/events` defaults to
   `type=BOOKING`. Without `type=ALL`, request events ("Anfrage-Events") are
   missing and the event-name column comes back silently empty.
2. **`status` defaults to `ACCEPTED`.** `GET /orders` silently omits
   cancellations unless you pass `status=ALL`. Ask the customer whether they want
   cancellations. Note the filter enum (`ACCEPTED, CANCELED, PENDING, DECLINED,
   OPEN, ALL`) and the order payload's own `status` enum (which includes
   `RESERVED`) are not identical.
3. **Ticket money is gross, and arrives as a string.** Mixing gross and net up
   is the single most common error:

   | Value | Unit | Gross or net |
   |---|---|---|
   | `tickets[].participants[].price_value` | euros, as a string (`"35.00"`) | **gross** → net = gross / (1 + vat/100) |

4. **Two independent cancellation levels.** Check both
   `tickets[].status == "CANCELED"` and
   `tickets[].participants[].status == "CANCELED"`. A live ticket can hold
   cancelled participants. Missing either inflates participant counts and
   revenue.
5. **`participants[].vat` is not reliable.** VAT-exempt products have been seen
   returning 19 %. Let the customer configure an override rate, and log every
   case where the API rate differs from the configured one so they can fix the
   price category in bookingkit.
6. **The service date is not in the order payload.** Only `date_id` is (some
   payloads use `event_date_id` — try both). Resolve it with `GET /dates/{id}`,
   which also returns `event_id`.
7. **Resolving an event name needs a fallback chain**, in this order: the vendor
   event map → `tickets[].event_title` if present → `GET /dates/{date_id}` to get
   `event_id` → `GET /events/{event_id}`. Price-list events and request events
   are why the chain exists.
8. **Payment method:** use `payments[].name` (the readable "PayPal", "Stripe"
   the customer knows from the standard export), **not** `payment_method`, which
   only holds a coarse type (`ThirdParty` / `Cash` / `MobileApp`). Payment values
   can be negative — those are refunds.
9. **Combination tickets double-count.** If the customer sells "Kombitickets",
   their revenue is already contained in the single events. Offer a keyword
   exclusion list.
10. **Booking date vs service date.** `start_date`/`end_date` filter by when the
    order was placed; `start_event_date`/`end_event_date` filter by when the
    service happens. Accounting reports usually want the service date. **Ask —
    do not assume.** Separately, `GET /dates` defaults to `available=true`, which
    hides dates with no free slot; pass `available=false` when reporting.
11. **Pick one timezone deliberately.** Querying in UTC but formatting in local
    time makes rows near a month boundary appear outside the requested range.
12. **Round consistently.** Round each money column to 2 decimals, then sum the
    *rounded* values for totals, so the printed column visibly adds up.

## 8. Writing the CSV

Default to German Excel, since most bookingkit vendors use it:

- Encoding **UTF-8 with BOM** (`utf-8-sig` in Python) — without the BOM, Excel
  mangles umlauts.
- Field separator **`;`**.
- Decimal **comma** (`1234,56`), no thousands separator.
- Dates as `DD.MM.YYYY`, timestamps as `DD.MM.YYYY HH:MM`.
- Write money as plain numbers, never with a currency symbol, so the columns can
  be summed.

Ask once whether they use German or English Excel; for English, use `,` as the
separator and `.` as the decimal mark.

Keep the **column order fixed** and identical on every run. This is a contract
with whoever imports the file — never reorder or insert a column in the middle
without being asked. Write the file to
`out/_.csv`.

## 9. Verify before you hand it over

Print a short summary after every run:

- the resolved date range and which date basis was used (booking or service);
- number of orders, tickets and participants;
- total revenue (gross and net);
- number of API requests made;
- every warning collected (VAT mismatches, unresolved event names, orders
  spanning several cost centres).

Then tell the customer explicitly: **compare these totals against the standard
bookingkit export for the same month before using the report.** If they do not
match, the difference is almost always one of §7.2 (missing cancellations),
§7.4 (cancellation levels), §7.9 (combination tickets) or §7.3 (gross/net).

## 10. Finish by making the next run trivial

Append to `AGENTS.md`: the vendor id, the date basis, the chosen columns in
order, the filters and exclusions, the Excel locale, and anything you learned
about this customer's data. Then tell them that from now on they only need to
open this folder and say, for example:

> run the monthly order report for last month

and that they should come back to the help article only if they want to change
what the report contains.
Did this answer your question?