Clubtool.developers
Concepts

Bulk & idempotency

Bulk endpoints apply up to 100 operations in one request. Operations are applied independently: a failing item never rolls back the others — you get a per-item result instead.

Members: mixed batch

curl -X POST https://app.club-tool.nl/api/v1/members/bulk \
  -H "Authorization: Bearer ct_live_..." \
  -H "Idempotency-Key: import-2026-07-02-a" \
  -H "Content-Type: application/json" \
  -d '{"operations": [
    {"op": "create", "data": {"name": "Sam de Vries", "group_ids": [3]}},
    {"op": "update", "id": 91, "data": {"status": "archived"}},
    {"op": "delete", "id": 12}
  ]}'

Per-item results

{
  "results": [
    { "status": 201, "id": 137 },
    { "status": 200, "id": 91 },
    { "status": 404, "id": 12, "error": "Niet gevonden" }
  ],
  "succeeded": 2,
  "failed": 1
}

The HTTP status of the batch itself is 200 as long as the batch was processed; inspect results[i].status per item (2xx = applied).

Idempotency keys

Bulk requests require an Idempotency-Key header (any unique string, max 120 chars). That makes network retries safe:

Pick keys per logical batch, e.g. import-<date>-<chunk>. Reuse the exact same key when retrying a failed connection; mint a new key for a new batch.

Attendance: batch upsert

curl -X POST https://app.club-tool.nl/api/v1/attendance/bulk \
  -H "Authorization: Bearer ct_live_..." \
  -H "Idempotency-Key: checkin-2026-07-02-19u" \
  -H "Content-Type: application/json" \
  -d '{"records": [
    {"member_id": 41, "group_id": 3, "date": "2026-07-02", "present": true},
    {"member_id": 42, "group_id": 3, "date": "2026-07-02", "present": false}
  ]}'

Setting "present": null deletes the record (same semantics as clearing it in the app).

Ordering & consistency