API reference
All endpoints of the CheckCourt REST API v1
Base URL: https://app.checkcourt.de/api/v1 · Authentication via
Authorization: Bearer <key or token> · Responses are always named
JSON objects, errors always { "error": { "code", "message" } }. Basics
and error codes are in the developer overview.
Which key can do what?
Each endpoint below carries badges that show which kind of key can call it. There are two categories:
| Category | Endpoints | Rule |
|---|---|---|
| Universal | Everything club-scoped (courts, members, bookings, ...) | The scope decides, not the kind of key: personal keys carry the scopes from the member's roles, management keys their explicitly granted ones. Personal keys always need X-Tenant-Id for this. |
| Personal keys only | /me/* | Self-service with the member's identity: your own clubs, reading and creating your own bookings. |
Each endpoint has exactly one meaning: /me/* acts for the user behind the key,
everything else manages club resources by scope. POST /me/bookings books for yourself,
POST /bookings books on behalf of a member (forUserId). There are deliberately no
endpoints that only management keys can call: management keys are limited by scopes, not
by endpoints of their own. DELETE /bookings/{id} is intentionally shared, "cancel this
booking" is one meaning. Whether ownership or the bookings:cancel scope allows it is
decided at runtime. Roles are the exception: only a personal key can change them
(roles:manage).
What about apps?
App tokens call the same endpoints. A club app token (cca_…) behaves like a
management key, a member token (ccu_…) like a personal key and may also use
/me/*. In addition:
- The UI's visibility rules always apply to apps, even with confidential scopes. Hidden
bookers stay
null, andrespectPrivacyis always on for apps. - Roles (
/roles,/members/{id}/roles) and creating or changing webhook endpoints are blocked for apps.
All details in API for apps.
Your clubs
Personal keys are bound to the user: the club is specified per request via the
X-Tenant-Id header. This endpoint returns the club IDs and your roles in each club. It
does not need the header itself. With a member app token, it returns only the clubs in
which the app is connected, without roles.
get/me
Only for member app tokens (ccu_); API keys and installation tokens receive 401. Returns a pseudonymous user_id and the clubs where the app is connected. user_id is stable for your app (across clubs, reconnects and token rotation) and different for every other app, so use it as the key of the member's account in your app. It is not the CheckCourt user ID that appears in API objects, webhook payloads and extension context tokens, and it cannot be resolved to one. Name and email are not part of the response.
Example
curl "https://app.checkcourt.de/api/v1/me" \
-H "Authorization: Bearer ccu_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
user_idstringrequiredPairwise pseudonym of the member for this app: stable for your app, different for every other app.
tenantsobject[]requiredClubs where the member has your app connected and running.
idstringrequirednamestringrequired
get/me/tenantsNur Personal
Lists every club the member behind the personal API key belongs to, with the roles held in each. This is the entry point of the multi-club model: personal keys are user-bound, and every club-scoped request names its club via the X-Tenant-Id header using the ids from this endpoint. This endpoint itself needs no header. Management keys have no member identity and receive 401. Member app tokens (ccu_) get only the clubs where the app is connected, with id and name and without roles.
Example
curl "https://app.checkcourt.de/api/v1/me/tenants" \
-H "Authorization: Bearer ck_user_…"Responses
200The caller's club memberships.401Missing/invalid key, management key, or no club of the user has personal keys enabled.
200er-Schema anzeigen
tenantsobject[]idstringnamestringrolesobject[]The caller's roles in THIS club, highest first, Mitglied (member) last; a user can be Administrator in one club and only Mitglied in another.
idstringrequiredRole id, as in GET /roles
namestringrequiredsystemKey"admin" | "trainer" | "member" | "null"requiredSet for the three roles every club starts with.
adminalways holds every permission;member(Mitglied) applies to every member automatically and is never assigned.
Bookings
Read availability, book and cancel courts. Your own bookings work with personal keys
without any special scope. You only need bookings:write and bookings:cancel to access
other people's bookings. The daily listing with names (for syncing and reporting)
requires bookings:read_confidential. For anonymous occupancy under /availability,
bookings:read is enough.
get/me/bookingsNur Personal
Returns the upcoming bookings owned by the member behind the personal API key in the club addressed via X-Tenant-Id, including cancelled ones (check cancelledAt). Management keys have no member identity and receive 403 FORBIDDEN.
Example
curl "https://app.checkcourt.de/api/v1/me/bookings" \
-H "Authorization: Bearer ck_user_…"Responses
200Own upcoming bookings.401Missing or invalid API key.403Key lacks bookings:read or has no member identity.
200er-Schema anzeigen
bookingsobject[]idstringcourtIdintegercourtNamestringtypestringdatestring (date)startTimestringendTimestringtitlestring | nullcancelledAtstring | null (date-time)cancelReasonstring | null
post/me/bookingsNur Personal
Creates a booking owned by the member behind the personal API key, in the club addressed via X-Tenant-Id. The full policy engine applies (booking hours, advance window, quotas, slot raster, court locks, time conflicts); policy denials come back as 422 BUSINESS_RULE. adminOverride: true additionally requires bookings:write (admins hold it by role).
Request body
courtIdintegerrequireddatestring (date)requiredstartTimestringrequiredendTimestringrequiredtitlestringnotesstringbookingType"regular" | "training" | "mannschaft"categoryIdstringteamIdstringRequired when bookingType is
mannschaft.playersobject[]Co-players. Members are referenced by userId, guests carry a guestName (guest fees may apply).
userIdstringguestNamestringisGuestbooleanrequired
adminOverridebooleanSkips all policy attribute checks (booking hours, advance limit, quotas). Requires
bookings:write. Hard integrity checks (court lock, time conflict, past date) still apply.
Example
curl -X POST "https://app.checkcourt.de/api/v1/me/bookings" \
-H "Authorization: Bearer ck_user_…" \
-H "Content-Type: application/json" \
-d '{"courtId":1,"date":"2026-06-15","startTime":"18:00","endTime":"19:00"}'Responses
200The created booking.400Validation error (bad times, unknown forUserId, players not in this club).401Missing or invalid API key.403Key lacks bookings:write for forUserId/adminOverride, or no permission for this team.404Court or team not found.422Policy denial or time conflict. Policy denials include `canOverride`.
200er-Schema anzeigen
bookingobjectidstringBooking id (UUID).
courtIdintegertype"regular" | "training" | "mannschaft"Booking kind.
mannschaftbookings belong to a team and have no personal owner.datestring (date)startTimestringendTimestringtitlestring | nullbookedBystring | nullUser id of the booking owner.
nullfor mannschaft bookings.teamIdstring | null
get/availability
Returns the busy intervals of every court for the given day: bookings and scheduled lock windows, each reduced to start/end time and a coarse kind. No booker names, titles or categories are included, which makes this endpoint safe for public-facing occupancy widgets (e.g. a club website showing which courts are free). Free slots are everything outside the returned intervals within the club's booking hours.
Parameters
datequery · string (date)requiredCalendar day to evaluate (YYYY-MM-DD)
Example
curl "https://app.checkcourt.de/api/v1/availability?date=2026-06-15" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
datestring (date)requiredcourtsobject[]requiredcourtIdintegerrequiredcourtNamestringrequiredisLockedbooleanrequiredTrue while the court is locked entirely (independent of slots)
occupiedobject[]requiredstartTimestringrequiredTime of day as HH:MM or HH:MM:SS
endTimestringrequiredTime of day as HH:MM or HH:MM:SS
kind"booking" | "lock"requiredWhether the interval is a booking or a scheduled lock window
get/availability/slots
Discovery endpoint for assistants and booking widgets: returns the start times that are actually bookable per court on the given day, taking existing bookings, court locks, booking hours, the slot raster, duration bounds, the advance-booking window, and season/month closures into account. POST /bookings remains the authoritative gate; a returned slot can still be lost to a concurrent booking.
Parameters
datequery · string (date)requireddurationMinutesquery · integerDesired booking length. With an active slot raster, only durations that are a multiple of the raster interval yield slots.
Example
curl "https://app.checkcourt.de/api/v1/availability/slots?date=2026-06-15" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Bookable start times per court (empty array for locked/closed courts).400Missing/malformed date or durationMinutes.401Missing or invalid API key.403Key lacks bookings:read.
200er-Schema anzeigen
datestring (date)durationMinutesintegercourtsobject[]courtIdintegercourtNamestringslotsstring[]
get/bookings
The reconciliation view for integrations: every booking of the given day including booker names, titles, check-in and cancellation state, i.e. exactly the personal data the anonymous /availability endpoint strips away, hence the confidential tier. Cancelled bookings are included (check cancelledAt).
With respectPrivacy=true the listing applies the same privacy rules as the member-facing booking plan and masks the booker identity where members opted out of showing their name. App tokens always get these rules, whatever respectPrivacy says; a member app still sees its own member's bookings in full.
Parameters
datequery · string (date)requiredrespectPrivacyquery · booleanApplies the member-facing privacy rules (booking-level showName, category default): bookings whose booker chose to hide their name come back with
bookedByandbookerNameset to null. Use this for display boards and other surfaces that must not out-privilege the public booking plan, even though the key holds the confidential scope.
Example
curl "https://app.checkcourt.de/api/v1/bookings?date=2026-06-15" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200All bookings of the day.400Missing or malformed date.401Missing or invalid API key.403Key lacks bookings:read_confidential.
200er-Schema anzeigen
datestring (date)bookingsobject[]idstringcourtIdintegercourtNamestringtypestringdatestring (date)startTimestringendTimestringtitlestring | nullbookedBystring | nullUser id of the owner; null for mannschaft bookings.
bookerNamestring | nullteamIdstring | nullcheckedInAtstring | null (date-time)cancelledAtstring | null (date-time)cancelReasonstring | null
post/bookings
Club-administration endpoint: creates a booking on BEHALF of a member (forUserId is mandatory; the booking belongs to that member and consumes their quota). Booking for yourself is POST /me/bookings. The full policy engine applies; policy denials come back as 422 BUSINESS_RULE with canOverride telling bookings:write holders that a retry with adminOverride: true would succeed.
Request body
courtIdintegerrequireddatestring (date)requiredstartTimestringrequiredendTimestringrequiredtitlestringnotesstringbookingType"regular" | "training" | "mannschaft"categoryIdstringteamIdstringRequired when bookingType is
mannschaft.playersobject[]Co-players. Members are referenced by userId, guests carry a guestName (guest fees may apply).
userIdstringguestNamestringisGuestbooleanrequired
forUserIdstringrequiredThe member this booking is created FOR: it belongs to them and consumes their quota. Mandatory; booking for yourself is POST /me/bookings.
adminOverridebooleanSkips all policy attribute checks (booking hours, advance limit, quotas). Requires
bookings:write. Hard integrity checks (court lock, time conflict, past date) still apply.
Example
curl -X POST "https://app.checkcourt.de/api/v1/bookings" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"courtId":1,"date":"2026-06-15","startTime":"18:00","endTime":"19:00","forUserId":"forUserId"}'Responses
200The created booking.400Validation error (bad times, unknown forUserId, players not in this club).401Missing or invalid API key.403Key lacks bookings:write for forUserId/adminOverride, or no permission for this team.404Court or team not found.422Policy denial or time conflict. Policy denials include `canOverride`.
200er-Schema anzeigen
bookingobjectidstringBooking id (UUID).
courtIdintegertype"regular" | "training" | "mannschaft"Booking kind.
mannschaftbookings belong to a team and have no personal owner.datestring (date)startTimestringendTimestringtitlestring | nullbookedBystring | nullUser id of the booking owner.
nullfor mannschaft bookings.teamIdstring | null
delete/bookings/{id}
Cancellation runs through the policy engine (cancellation deadline); denials come back as 422 BUSINESS_RULE with canOverride for bookings:cancel holders, who may retry with ?override=true. Past bookings can never be cancelled. The booking owner is notified by email.
Parameters
idpath · stringrequiredBooking id.
overridequery · booleanSkips policy checks (deadline). Requires bookings:cancel.
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/bookings/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Booking cancelled.401Missing or invalid API key.403Not the owner and key lacks bookings:cancel.404Booking not found.422Already cancelled, in the past, or policy deadline passed.
200er-Schema anzeigen
successbooleanrequired
get/bookings/{id}/metadata
Returns the calling app's own keys on this booking and the keys of other apps that the caller declares under reads.metadata in its manifest, the producer declares under shares.metadata, and the club approved. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredBooking id
Example
curl "https://app.checkcourt.de/api/v1/bookings/{id}/metadata" \
-H "Authorization: Bearer cca_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
metadataobjectrequiredGrouped by app slug, then by key. Contains the calling app's own keys and the keys of other apps that the caller declares under
reads.metadataand the club approved.
put/bookings/{id}/metadata/{key}
Writes one of the app's own keys. The key must be declared under shares.metadata with object: "booking". A changed value sends app.metadata_changed to the apps the club connected to yours for this key. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredBooking id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Request body
valueobjectrequiredAny JSON value except null, at most 4096 bytes serialized
Example
curl -X PUT "https://app.checkcourt.de/api/v1/bookings/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…" \
-H "Content-Type: application/json" \
-d '{"value":"value"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
keystringrequiredvalueobjectrequiredupdated_atstring (date-time)required
delete/bookings/{id}/metadata/{key}
Removes the app's own value. Works for keys a newer manifest no longer declares, so an app can clean up. deleted: false when there was nothing to remove. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredBooking id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/bookings/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
deletedbooleanrequired
Courts
get/courts
Returns every court of the club, including locked and billing-locked ones, ordered by sortOrder. Use isLocked/isBillingLocked to decide whether a court is currently bookable, and activeMonths/seasonStart/seasonEnd for seasonal availability.
Example
curl "https://app.checkcourt.de/api/v1/courts" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
courtsobject[]requiredidintegerrequiredNumeric court id (stable, tenant-scoped)
tenantIdstringnamestringrequiredsurfacestringrequiredFree-text surface label, e.g. "Asche" or "Teppich"
isLockedbooleanrequiredTrue while the court is locked for booking
lockReasonstring | nullReason shown to members while locked
isBillingLockedbooleanLocked by the billing system because the subscription does not cover this court. Cannot be lifted via the API.
lockedBystring | nullUser id of whoever locked the court
lockedAtstring | null (date-time)ISO 8601 timestamp
sortOrderintegerrequiredDisplay position, ascending
bookingNoticestring | nullShort notice shown in the booking UI for this court
bookingNoticeUpdatedAtstring | null (date-time)ISO 8601 timestamp
activeMonthsinteger[]Months (1-12) in which the court is bookable. Empty means always.
seasonStartstring | nullSeason start as MM-DD; always set together with seasonEnd
seasonEndstring | nullSeason end as MM-DD
post/courts
Creates a court and appends it to the end of the sort order. The number of courts is limited by the club's subscription plan; exceeding it returns 422. Season and notice fields can be set afterwards via PATCH.
Request body
namestringrequiredsurfacestringrequired
Example
curl -X POST "https://app.checkcourt.de/api/v1/courts" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"name":"name","surface":"surface"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
courtobjectrequiredidintegerrequiredNumeric court id (stable, tenant-scoped)
tenantIdstringnamestringrequiredsurfacestringrequiredFree-text surface label, e.g. "Asche" or "Teppich"
isLockedbooleanrequiredTrue while the court is locked for booking
lockReasonstring | nullReason shown to members while locked
isBillingLockedbooleanLocked by the billing system because the subscription does not cover this court. Cannot be lifted via the API.
lockedBystring | nullUser id of whoever locked the court
lockedAtstring | null (date-time)ISO 8601 timestamp
sortOrderintegerrequiredDisplay position, ascending
bookingNoticestring | nullShort notice shown in the booking UI for this court
bookingNoticeUpdatedAtstring | null (date-time)ISO 8601 timestamp
activeMonthsinteger[]Months (1-12) in which the court is bookable. Empty means always.
seasonStartstring | nullSeason start as MM-DD; always set together with seasonEnd
seasonEndstring | nullSeason end as MM-DD
post/courts/reorder
Replaces the display order. Send ALL court ids of the club in the desired order; the array index becomes the new sortOrder.
Request body
courtIdsinteger[]required
Example
curl -X POST "https://app.checkcourt.de/api/v1/courts/reorder" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"courtIds":[]}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequired
get/courts/{id}
Returns the court plus its next bookings (capped at 20, starting today) and any scheduled lock windows. Bookings include the booker's display name, which is why this endpoint requires the confidential read scope rather than plain courts:read.
Parameters
idpath · integerrequiredNumeric court id
Example
curl "https://app.checkcourt.de/api/v1/courts/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
courtobjectrequiredidintegerrequiredNumeric court id (stable, tenant-scoped)
tenantIdstringnamestringrequiredsurfacestringrequiredFree-text surface label, e.g. "Asche" or "Teppich"
isLockedbooleanrequiredTrue while the court is locked for booking
lockReasonstring | nullReason shown to members while locked
isBillingLockedbooleanLocked by the billing system because the subscription does not cover this court. Cannot be lifted via the API.
lockedBystring | nullUser id of whoever locked the court
lockedAtstring | null (date-time)ISO 8601 timestamp
sortOrderintegerrequiredDisplay position, ascending
bookingNoticestring | nullShort notice shown in the booking UI for this court
bookingNoticeUpdatedAtstring | null (date-time)ISO 8601 timestamp
activeMonthsinteger[]Months (1-12) in which the court is bookable. Empty means always.
seasonStartstring | nullSeason start as MM-DD; always set together with seasonEnd
seasonEndstring | nullSeason end as MM-DD
upcomingBookingsobject[]requiredNext bookings from today on, capped at 20
idstringBooking id (CUID)
typestringBooking type, e.g. "regular" or "mannschaft"
datestring (date)startTimestringTime of day as HH:MM or HH:MM:SS
endTimestringTime of day as HH:MM or HH:MM:SS
titlestring | nullbookedByNamestring | nullDisplay name of the booker, or the team name for team bookings
lockRangesobject[]requiredfromDatestring (date)toDatestring (date)fromTimestringTime of day as HH:MM or HH:MM:SS
toTimestringTime of day as HH:MM or HH:MM:SS
titlestring | null
lockedByUserstring | nullDisplay name of the user who locked the court
isBillingLockedboolean
patch/courts/{id}
Partial update: omitted fields stay unchanged, explicit null clears nullable fields. seasonStart and seasonEnd must be set or cleared together. The current month cannot be removed from activeMonths while it is active, because that would strand existing bookings.
Parameters
idpath · integerrequiredNumeric court id
Request body
namestringsurfacestringbookingNoticestring | nullactiveMonthsinteger[]seasonStartstring | nullMM-DD
seasonEndstring | nullMM-DD
Example
curl -X PATCH "https://app.checkcourt.de/api/v1/courts/{id}" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
courtIdintegerrequired
delete/courts/{id}
Destructive: cancels every future booking on the court and emails the affected members a summary. Courts that are part of the current billing period cannot be deleted (422) unless the caller is a platform support user passing forceBillingActive=true.
Parameters
idpath · integerrequiredNumeric court id
forceBillingActivequery · booleanPlatform support only: delete even though the court is billed this month
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/courts/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
cancelledBookingsintegerrequiredNumber of future bookings that were cancelled
post/courts/{id}/lock
Without dates, the court is locked immediately and indefinitely until unlocked. With fromDate/toDate (both required together) a scheduled lock window is created; fromTime/toTime default to 07:00-21:00. Future bookings overlapping the lock are cancelled automatically and the bookers are notified by email. The reason is displayed to members in the booking UI.
Parameters
idpath · integerrequiredNumeric court id
Request body
reasonstringrequiredfromDatestring (date)toDatestring (date)fromTimestringTime of day as HH:MM or HH:MM:SS
toTimestringTime of day as HH:MM or HH:MM:SS
Example
curl -X POST "https://app.checkcourt.de/api/v1/courts/{id}/lock" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"reason":"reason"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
courtIdintegerrequired
post/courts/{id}/unlock
Lifts a manual lock and removes scheduled lock windows. Billing locks (isBillingLocked) are managed by the subscription system and cannot be lifted here (422).
Parameters
idpath · integerrequiredNumeric court id
Example
curl -X POST "https://app.checkcourt.de/api/v1/courts/{id}/unlock" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
courtIdintegerrequired
get/courts/{id}/metadata
Returns the calling app's own keys on this court and the keys of other apps that the caller declares under reads.metadata in its manifest, the producer declares under shares.metadata, and the club approved. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · integerrequiredNumeric court id
Example
curl "https://app.checkcourt.de/api/v1/courts/{id}/metadata" \
-H "Authorization: Bearer cca_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
metadataobjectrequiredGrouped by app slug, then by key. Contains the calling app's own keys and the keys of other apps that the caller declares under
reads.metadataand the club approved.
put/courts/{id}/metadata/{key}
Writes one of the app's own keys. The key must be declared under shares.metadata with object: "court". A changed value sends app.metadata_changed to the apps the club connected to yours for this key. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · integerrequiredNumeric court id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Request body
valueobjectrequiredAny JSON value except null, at most 4096 bytes serialized
Example
curl -X PUT "https://app.checkcourt.de/api/v1/courts/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…" \
-H "Content-Type: application/json" \
-d '{"value":"value"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
keystringrequiredvalueobjectrequiredupdated_atstring (date-time)required
delete/courts/{id}/metadata/{key}
Removes the app's own value. Works for keys a newer manifest no longer declares, so an app can clean up. deleted: false when there was nothing to remove. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · integerrequiredNumeric court id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/courts/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
deletedbooleanrequired
Members
Name lists require members:read. Contact details and other personal data require
members:read_confidential.
get/members
Returns the full member directory including contact data, 20 per page. search matches name, email, forwarding email, member number and member code (case-insensitive substring). Members awaiting admin approval are sorted first, then alphabetically by name.
Parameters
searchquery · stringSubstring search across name, email, forwarding email, member number, member code
roleIdquery · stringOnly members holding this role (id from GET /roles). The base role Mitglied (member) matches everyone.
pagequery · integer1-based page number
Example
curl "https://app.checkcourt.de/api/v1/members" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
membersobject[]requiredidstringrequiredUser id (CUID). Use this in /members/{id} paths.
namestringrequiredemailstring (email)requiredforwardingEmailstring | null (email)Set for accounts without their own mailbox: mail is sent here instead, and
emailis a synthetic addressmemberNumberstring | nullClub-assigned member number, unique per tenant
rolesobject[]requiredRoles the member holds, highest first, the base role Mitglied (member) last. Without
roles:readonly the roles the club shows next to member names (plus Mitglied) are listed.idstringrequiredRole id, as in GET /roles
namestringrequiredsystemKey"admin" | "trainer" | "member" | "null"requiredSet for the three roles every club starts with.
adminalways holds every permission;member(Mitglied) applies to every member automatically and is never assigned.
guestFeesAccumulatedintegerAmount in euro cents
emailVerifiedbooleanadminApprovalPendingbooleanTrue while a self-registered member awaits admin approval
totalintegerrequiredTotal matching members across all pages
pageintegerrequiredpageSizeintegerrequiredtotalPagesintegerrequired
post/members
Adds a member to the club. Three flows, decided by the input:
1. Email already has an account (in no other club): the account owner is invited. The membership stays pending (awaitingAcceptance: true) until they accept after their next login; until then the club cannot change the account's name, email, phone or password, and deleting the member only withdraws the invitation. sendEmail controls the invitation mail.
2. New email: a new account is created and, unless sendEmail=false, a welcome mail with a claim link is sent.
3. `noOwnEmail=true`: for members without their own mailbox. Creates an account with a synthetic address, stores the given email as forwarding address and requires a password (min 8 chars) the member uses to log in.
roleIds (optional) assigns roles besides the base role Mitglied and requires roles:manage, which only personal keys can hold: the caller must outrank each role and hold all of its scopes (403). Unknown ids fail with 404 before anything is created.
Fails with 422 when the club's member limit is reached, the member number is taken, the email already belongs to this club, or the data processing agreement (AVV) has not been signed yet.
Request body
emailstring (email)requirednamestringrequiredroleIdsstring[]Role ids from GET /roles; omit for members without further roles
sendEmailbooleandefault: trueSend the welcome email
noOwnEmailbooleandefault: falseCreate a forwarding-only account (flow 3)
passwordstringRequired when noOwnEmail=true
memberNumberstring
Example
curl -X POST "https://app.checkcourt.de/api/v1/members" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"email":"email","name":"name"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequiredawaitingAcceptancebooleanrequiredTrue when an existing account was invited and its owner still has to accept
get/members/count
Number of members of the club, including members awaiting admin approval. Returns no names or ids, so it needs only members:read, not members:read_confidential like the directory in GET /members.
Example
curl "https://app.checkcourt.de/api/v1/members/count" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
totalintegerrequired
get/members/{id}
Full member record including phone, member code and creation date.
Parameters
idpath · stringrequiredUser id of the member (the
idfield from list responses)
Example
curl "https://app.checkcourt.de/api/v1/members/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
memberobjectrequiredidstringrequiredUser id (CUID)
namestringrequiredemailstring (email)requiredforwardingEmailstring | null (email)rolesobject[]requiredRoles the member holds, highest first, the base role Mitglied (member) last. Without
roles:readonly the roles the club shows next to member names (plus Mitglied) are listed.idstringrequiredRole id, as in GET /roles
namestringrequiredsystemKey"admin" | "trainer" | "member" | "null"requiredSet for the three roles every club starts with.
adminalways holds every permission;member(Mitglied) applies to every member automatically and is never assigned.
memberNumberstring | nullmitgliedscodestring | nullShort member code used for on-site identification
phonestring | nullguestFeesAccumulatedintegerAmount in euro cents
createdAtstring (date-time)ISO 8601 timestamp
emailVerifiedbooleanadminApprovalPendingboolean
patch/members/{id}
name and email are required (send the current values to keep them); phone and memberNumber are optional. For forwarding-only accounts the email update changes the forwarding address, not the login address. If the user is also a member of another club, name/email/phone are left untouched (they are account-level) and only the member number changes. Sessions cannot edit their own account; management keys are exempt from that rule. Changing the email requires outranking the member (403): only administrators may change the email of members holding an equal or higher role; management keys act with their creator's rank but never on administrators. While the member's invitation is pending, changing name, email or phone fails with 422. Member numbers are unique per club (422 on collision). Roles are not part of this endpoint: use PUT /members/{id}/roles. Sending role fails with 400.
Parameters
idpath · stringrequiredUser id of the member (the
idfield from list responses)
Request body
namestringrequiredemailstring (email)requiredphonestringmemberNumberstring
Example
curl -X PATCH "https://app.checkcourt.de/api/v1/members/{id}" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"name":"name","email":"email"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequired
delete/members/{id}
Removes the member from the club with a full cascade: future bookings are cancelled (with email notification), open guest fees are resolved according to orphanedFeeAction, and if this was the user's only club the account is anonymized (not for a pending invitation, which only withdraws the invitation). The caller must outrank the member (403): only administrators may delete members holding an equal or higher role; management keys act with their creator's rank but never on administrators. Members who lead a team cannot be deleted until leadership is handed over, and the club's last administrator cannot be deleted (422). The response summarizes what happened.
Parameters
idpath · stringrequiredUser id of the member (the
idfield from list responses)orphanedFeeActionquery · "cancelled" | "paid"Write off open guest fees (
cancelled) or mark them as paid (paid)
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/members/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequiredcancelledBookingCountintegerresolvedFeeCountintegerresolvedFeeTotalCentsintegerAmount in euro cents
anonymizedbooleanTrue if the account was anonymized because no other membership remained
get/members/{id}/metadata
Returns the calling app's own keys on this member and the keys of other apps that the caller declares under reads.metadata in its manifest, the producer declares under shares.metadata, and the club approved. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredThe member's user id
Example
curl "https://app.checkcourt.de/api/v1/members/{id}/metadata" \
-H "Authorization: Bearer cca_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
metadataobjectrequiredGrouped by app slug, then by key. Contains the calling app's own keys and the keys of other apps that the caller declares under
reads.metadataand the club approved.
put/members/{id}/metadata/{key}
Writes one of the app's own keys. The key must be declared under shares.metadata with object: "member". A changed value sends app.metadata_changed to the apps the club connected to yours for this key. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredThe member's user id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Request body
valueobjectrequiredAny JSON value except null, at most 4096 bytes serialized
Example
curl -X PUT "https://app.checkcourt.de/api/v1/members/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…" \
-H "Content-Type: application/json" \
-d '{"value":"value"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
keystringrequiredvalueobjectrequiredupdated_atstring (date-time)required
delete/members/{id}/metadata/{key}
Removes the app's own value. Works for keys a newer manifest no longer declares, so an app can clean up. deleted: false when there was nothing to remove. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredThe member's user id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/members/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
deletedbooleanrequired
Roles
The club's roles and groups and who holds them. Reading works with roles:read, changing
only with a personal key and roles:manage. The same rules apply as in the UI: you can
only assign and revoke roles that rank below your highest role, and only for members
whose highest role ranks below yours. You can also only assign a role if you hold all of
its permissions yourself. Only administrators can change the Administrator role.
get/roles
All roles of the club, highest first, including the system roles Administrator, Trainer and Mitglied (member).
Example
curl "https://app.checkcourt.de/api/v1/roles" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
rolesobject[]requiredidstringrequirednamestringrequireddescriptionstring | nullrequiredcolorstringrequiredHex color from the role palette (see
RoleColor)positionintegerrequiredRank in the club's hierarchy; higher outranks lower. Mitglied (member) is always 0, Administrator always the highest.
systemKey"admin" | "trainer" | "member" | "null"requiredSet for the three roles every club starts with.
adminalways holds every permission;member(Mitglied) applies to every member automatically and is never assigned.isVisiblebooleanrequiredWhether the role is shown as a badge next to its members
permissionsstring[]requiredScopes granted by this role (see
GET /permissions). Administrator lists every scope.memberCountintegerrequiredMembers holding the role; for Mitglied (member) the size of the whole club
post/rolesNur Personal
Personal keys only: management keys can never hold roles:manage (403). The key acts as its member: non-administrators can only change roles that sit below their own highest role, only touch members ranked below them, and only grant permissions they hold themselves (403 otherwise). Creates a role directly above the base role Mitglied (member), either from scratch or from a template ({ "template": "kassenwart" }, no other fields). Prerequisite scopes are added automatically. Returns the new role as stored. An unknown template returns 404.
Request body
Example
curl -X POST "https://app.checkcourt.de/api/v1/roles" \
-H "Authorization: Bearer ck_user_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200The role as stored, in the same shape as an item of `GET /roles`400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
roleobjectrequiredA role of the club with its permissions
idstringrequirednamestringrequireddescriptionstring | nullrequiredcolorstringrequiredHex color from the role palette (see
RoleColor)positionintegerrequiredRank in the club's hierarchy; higher outranks lower. Mitglied (member) is always 0, Administrator always the highest.
systemKey"admin" | "trainer" | "member" | "null"requiredSet for the three roles every club starts with.
adminalways holds every permission;member(Mitglied) applies to every member automatically and is never assigned.isVisiblebooleanrequiredWhether the role is shown as a badge next to its members
permissionsstring[]requiredScopes granted by this role (see
GET /permissions). Administrator lists every scope.memberCountintegerrequiredMembers holding the role; for Mitglied (member) the size of the whole club
put/roles/reorderNur Personal
Personal keys only: management keys can never hold roles:manage (403). The key acts as its member: non-administrators can only change roles that sit below their own highest role, only touch members ranked below them, and only grant permissions they hold themselves (403 otherwise). Send every role except Administrator and Mitglied (member) exactly once, highest first (400 otherwise). Non-administrators can only reshuffle roles below their own highest role; those at or above it must keep their place (403).
Request body
orderedIdsstring[]required
Example
curl -X PUT "https://app.checkcourt.de/api/v1/roles/reorder" \
-H "Authorization: Bearer ck_user_…" \
-H "Content-Type: application/json" \
-d '{"orderedIds":[]}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequired
get/roles/{id}
The role plus everyone holding it. email and memberNumber of the members are null unless the key also holds members:read_confidential. For the base role „Mitglied“ (systemKey member), which applies to everyone, the list contains every member of the club.
Parameters
idpath · stringrequiredRole id (the
idfield fromGET /roles)
Example
curl "https://app.checkcourt.de/api/v1/roles/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
roleobjectrequiredA role of the club with its permissions
idstringrequirednamestringrequireddescriptionstring | nullrequiredcolorstringrequiredHex color from the role palette (see
RoleColor)positionintegerrequiredRank in the club's hierarchy; higher outranks lower. Mitglied (member) is always 0, Administrator always the highest.
systemKey"admin" | "trainer" | "member" | "null"requiredSet for the three roles every club starts with.
adminalways holds every permission;member(Mitglied) applies to every member automatically and is never assigned.isVisiblebooleanrequiredWhether the role is shown as a badge next to its members
permissionsstring[]requiredScopes granted by this role (see
GET /permissions). Administrator lists every scope.memberCountintegerrequiredMembers holding the role; for Mitglied (member) the size of the whole club
membersobject[]requireduserIdstringrequiredUser id (same as
idinGET /members)namestringrequiredmemberNumberstring | nullrequiredNull without
members:read_confidentialemailstring | null (email)requiredNull without
members:read_confidential
patch/roles/{id}Nur Personal
Personal keys only: management keys can never hold roles:manage (403). The key acts as its member: non-administrators can only change roles that sit below their own highest role, only touch members ranked below them, and only grant permissions they hold themselves (403 otherwise). Changes name, description, color or visibility; omitted fields stay. description: null clears it.
Parameters
idpath · stringrequiredRole id (the
idfield fromGET /roles)
Request body
namestringdescriptionstring | nullcolor"#166534" | "#f97316" | "#2563eb" | "#8b5cf6" | "#ec4899" | "#14b8a6" | "#f59e0b" | "#ef4444" | "#6366f1" | "#84cc16" | "#06b6d4" | "#d946ef"Display color of a role, one of a fixed palette
isVisibleboolean
Example
curl -X PATCH "https://app.checkcourt.de/api/v1/roles/{id}" \
-H "Authorization: Bearer ck_user_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200The role as stored, in the same shape as an item of `GET /roles`400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
roleobjectrequiredA role of the club with its permissions
idstringrequirednamestringrequireddescriptionstring | nullrequiredcolorstringrequiredHex color from the role palette (see
RoleColor)positionintegerrequiredRank in the club's hierarchy; higher outranks lower. Mitglied (member) is always 0, Administrator always the highest.
systemKey"admin" | "trainer" | "member" | "null"requiredSet for the three roles every club starts with.
adminalways holds every permission;member(Mitglied) applies to every member automatically and is never assigned.isVisiblebooleanrequiredWhether the role is shown as a badge next to its members
permissionsstring[]requiredScopes granted by this role (see
GET /permissions). Administrator lists every scope.memberCountintegerrequiredMembers holding the role; for Mitglied (member) the size of the whole club
delete/roles/{id}Nur Personal
Personal keys only: management keys can never hold roles:manage (403). The key acts as its member: non-administrators can only change roles that sit below their own highest role, only touch members ranked below them, and only grant permissions they hold themselves (403 otherwise). Deletes the role and removes it from all members. Administrator and Mitglied (member) cannot be deleted (422).
Parameters
idpath · stringrequiredRole id (the
idfield fromGET /roles)
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/roles/{id}" \
-H "Authorization: Bearer ck_user_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequired
put/roles/{id}/permissionsNur Personal
Personal keys only: management keys can never hold roles:manage (403). The key acts as its member: non-administrators can only change roles that sit below their own highest role, only touch members ranked below them, and only grant permissions they hold themselves (403 otherwise). Replaces the full scope list; prerequisite scopes are added automatically and included in the returned role. Administrator always holds everything and cannot be changed (422). Unknown scopes return 400.
Parameters
idpath · stringrequiredRole id (the
idfield fromGET /roles)
Request body
scopesstring[]required
Example
curl -X PUT "https://app.checkcourt.de/api/v1/roles/{id}/permissions" \
-H "Authorization: Bearer ck_user_…" \
-H "Content-Type: application/json" \
-d '{"scopes":[]}'Responses
200The role as stored, in the same shape as an item of `GET /roles`400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
roleobjectrequiredA role of the club with its permissions
idstringrequirednamestringrequireddescriptionstring | nullrequiredcolorstringrequiredHex color from the role palette (see
RoleColor)positionintegerrequiredRank in the club's hierarchy; higher outranks lower. Mitglied (member) is always 0, Administrator always the highest.
systemKey"admin" | "trainer" | "member" | "null"requiredSet for the three roles every club starts with.
adminalways holds every permission;member(Mitglied) applies to every member automatically and is never assigned.isVisiblebooleanrequiredWhether the role is shown as a badge next to its members
permissionsstring[]requiredScopes granted by this role (see
GET /permissions). Administrator lists every scope.memberCountintegerrequiredMembers holding the role; for Mitglied (member) the size of the whole club
post/roles/{id}/resetNur Personal
Personal keys only: management keys can never hold roles:manage (403). The key acts as its member: non-administrators can only change roles that sit below their own highest role, only touch members ranked below them, and only grant permissions they hold themselves (403 otherwise). Restores the permissions a new club starts with. Only for the base role Mitglied (member) (422 for any other role). The key's member must be able to grant every default permission.
Parameters
idpath · stringrequiredRole id (the
idfield fromGET /roles)
Example
curl -X POST "https://app.checkcourt.de/api/v1/roles/{id}/reset" \
-H "Authorization: Bearer ck_user_…"Responses
200The role as stored, in the same shape as an item of `GET /roles`400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
roleobjectrequiredA role of the club with its permissions
idstringrequirednamestringrequireddescriptionstring | nullrequiredcolorstringrequiredHex color from the role palette (see
RoleColor)positionintegerrequiredRank in the club's hierarchy; higher outranks lower. Mitglied (member) is always 0, Administrator always the highest.
systemKey"admin" | "trainer" | "member" | "null"requiredSet for the three roles every club starts with.
adminalways holds every permission;member(Mitglied) applies to every member automatically and is never assigned.isVisiblebooleanrequiredWhether the role is shown as a badge next to its members
permissionsstring[]requiredScopes granted by this role (see
GET /permissions). Administrator lists every scope.memberCountintegerrequiredMembers holding the role; for Mitglied (member) the size of the whole club
get/roles/{id}/members
Same member list as GET /roles/{id}. email and memberNumber are null unless the key also holds members:read_confidential. For the base role „Mitglied“ (systemKey member), which applies to everyone, the list contains every member of the club.
Parameters
idpath · stringrequiredRole id (the
idfield fromGET /roles)
Example
curl "https://app.checkcourt.de/api/v1/roles/{id}/members" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
membersobject[]requireduserIdstringrequiredUser id (same as
idinGET /members)namestringrequiredmemberNumberstring | nullrequiredNull without
members:read_confidentialemailstring | null (email)requiredNull without
members:read_confidential
post/roles/{id}/membersNur Personal
Personal keys only: management keys can never hold roles:manage (403). The key acts as its member: non-administrators can only change roles that sit below their own highest role, only touch members ranked below them, and only grant permissions they hold themselves (403 otherwise). Adds the role to every listed member; members who already hold it are skipped. Any id outside the club returns 404 and nothing changes. The base role Mitglied (member) cannot be assigned (422).
Parameters
idpath · stringrequiredRole id (the
idfield fromGET /roles)
Request body
userIdsstring[]requiredUser ids (the
idfield fromGET /members)
Example
curl -X POST "https://app.checkcourt.de/api/v1/roles/{id}/members" \
-H "Authorization: Bearer ck_user_…" \
-H "Content-Type: application/json" \
-d '{"userIds":[]}'Responses
200Everyone holding the role after the change, as in `GET /roles/{id}/members`400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
membersobject[]requireduserIdstringrequiredUser id (same as
idinGET /members)namestringrequiredmemberNumberstring | nullrequiredNull without
members:read_confidentialemailstring | null (email)requiredNull without
members:read_confidential
delete/roles/{id}/members/{userId}Nur Personal
Personal keys only: management keys can never hold roles:manage (403). The key acts as its member: non-administrators can only change roles that sit below their own highest role, only touch members ranked below them, and only grant permissions they hold themselves (403 otherwise). Removing a role the member does not hold is a no-op. The base role Mitglied (member) cannot be taken away, and the club's last administrator keeps the role Administrator (422).
Parameters
idpath · stringrequiredRole id (the
idfield fromGET /roles)userIdpath · stringrequiredUser id of the member (the
idfield fromGET /members)
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/roles/{id}/members/{userId}" \
-H "Authorization: Bearer ck_user_…"Responses
200Everyone holding the role after the change, as in `GET /roles/{id}/members`400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
membersobject[]requireduserIdstringrequiredUser id (same as
idinGET /members)namestringrequiredmemberNumberstring | nullrequiredNull without
members:read_confidentialemailstring | null (email)requiredNull without
members:read_confidential
get/members/{id}/roles
Every role the member holds, highest first, including the base role Mitglied (member), with color and permissions. /members lists the same roles by name only.
Parameters
idpath · stringrequiredUser id of the member (the
idfield fromGET /members)
Example
curl "https://app.checkcourt.de/api/v1/members/{id}/roles" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
rolesobject[]requiredidstringrequirednamestringrequireddescriptionstring | nullrequiredcolorstringrequiredHex color from the role palette (see
RoleColor)positionintegerrequiredRank in the club's hierarchy; higher outranks lower. Mitglied (member) is always 0, Administrator always the highest.
systemKey"admin" | "trainer" | "member" | "null"requiredSet for the three roles every club starts with.
adminalways holds every permission;member(Mitglied) applies to every member automatically and is never assigned.isVisiblebooleanrequiredWhether the role is shown as a badge next to its members
permissionsstring[]requiredScopes granted by this role (see
GET /permissions). Administrator lists every scope.
put/members/{id}/rolesNur Personal
Personal keys only: management keys can never hold roles:manage (403). The key acts as its member: non-administrators can only change roles that sit below their own highest role, only touch members ranked below them, and only grant permissions they hold themselves (403 otherwise). Sets exactly the given roles; roles not listed are taken away. The base role Mitglied (member) is implicit and ignored. Unknown role ids return 404. Taking Administrator from the club's last administrator fails with 422. This is the multi-role counterpart of the legacy role field on PATCH /members/{id}.
Parameters
idpath · stringrequiredUser id of the member (the
idfield fromGET /members)
Request body
roleIdsstring[]required
Example
curl -X PUT "https://app.checkcourt.de/api/v1/members/{id}/roles" \
-H "Authorization: Bearer ck_user_…" \
-H "Content-Type: application/json" \
-d '{"roleIds":[]}'Responses
200The member's roles after the change, as in `GET /members/{id}/roles`400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
rolesobject[]requiredidstringrequirednamestringrequireddescriptionstring | nullrequiredcolorstringrequiredHex color from the role palette (see
RoleColor)positionintegerrequiredRank in the club's hierarchy; higher outranks lower. Mitglied (member) is always 0, Administrator always the highest.
systemKey"admin" | "trainer" | "member" | "null"requiredSet for the three roles every club starts with.
adminalways holds every permission;member(Mitglied) applies to every member automatically and is never assigned.isVisiblebooleanrequiredWhether the role is shown as a badge next to its members
permissionsstring[]requiredScopes granted by this role (see
GET /permissions). Administrator lists every scope.
Permissions
The catalog of all scopes with German labels and descriptions, grouped by area.
get/permissions
All scopes with German labels and descriptions, grouped into areas with the levels the role editor offers. Areas that belong to a feature appear only while the key's club has that feature enabled, so the catalog matches what the club's role editor shows. Use it to render permission pickers and to validate the scopes of PUT /roles/{id}/permissions.
Example
curl "https://app.checkcourt.de/api/v1/permissions" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
areasobject[]requiredkeystringrequiredlabelstringrequireddescriptionstringrequiredfeatureKeystring | nullrequiredFeature the area belongs to (enabled for this club, otherwise the area is omitted); null for areas every club has
scopesobject[]requiredscopestringrequiredlabelstringrequireddescriptionstringrequiredrequiresstring[]requiredScopes this one is useless without; they are added automatically when the scope is granted
levelsobject[]requiredAscending, always starting with
nonelevel"none" | "read" | "edit" | "manage"requiredlabelstringrequireddescriptionstringrequiredscopesstring[]requiredEvery scope of the area this level grants, including those of lower levels
scopesstring[]requiredEvery scope of the returned areas, in catalog order
Teams
get/teams
Returns every team with aggregated counts (members, upcoming matches, recurring trainings) and the next scheduled match, so an overview UI needs no follow-up requests.
Example
curl "https://app.checkcourt.de/api/v1/teams" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
teamsobject[]required
post/teams
Creates an empty team. Add players and leaders afterwards via the team-members endpoint.
Request body
namestringrequiredligastringspielklasse"herren" | "damen" | "mixed" | "jugend"farbestringHex color
Example
curl -X POST "https://app.checkcourt.de/api/v1/teams" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"name":"name"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
teamIdstringrequired
get/teams/{id}
Returns the team with its roster (players and leaders), upcoming matches, one-off and recurring trainings. canManage tells whether the calling actor could use the management endpoints for this team.
Parameters
idpath · stringrequiredTeam id
Example
curl "https://app.checkcourt.de/api/v1/teams/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
teamobjectrequiredidstringrequiredTeam id (CUID)
tenantIdstringnamestringrequiredligastring | nullLeague label, free text
spielklasse"herren" | "damen" | "mixed" | "jugend" | "null"Competition class of a team
farbestring | nullHex color used to mark the team's bookings
createdAtstring (date-time)ISO 8601 timestamp
updatedAtstring (date-time)ISO 8601 timestamp
membersobject[]requiredtenantUserIdstringrequiredMembership id (tenantUser). This is NOT the user id; use it when adding/removing team members.
role"player" | "leader"requiredA person can appear twice: once as player and once as leader
userIdstringrequiredmemberNumberstring | nullnamestringrequired
matchesobject[]requiredUpcoming matches
idstringdatestring (date)startTimestringTime of day as HH:MM or HH:MM:SS
endTimestringTime of day as HH:MM or HH:MM:SS
titlestring | nullisAwaybooleanopponentstring | nulllocationstring | nullcourtNamesstring[]Home matches block one or more courts
oneOffTrainingsobject[]One-off training bookings (raw booking rows)
trainingsobject[]Recurring weekly trainings
canManagebooleanrequiredWhether the calling actor may manage this team (admin role or team leader)
patch/teams/{id}
Updates name, league, class or color. spielklasse: null clears the class.
Beyond the scope, this endpoint needs management rights for the specific team: the actor must have the admin role or be a leader of this team. Management keys carry no role and are therefore always rejected here; use a personal key of an admin or team leader.
Parameters
idpath · stringrequiredTeam id
Request body
namestringrequiredligastringspielklasse"herren" | "damen" | "mixed" | "jugend" | "null"Competition class of a team
farbestring
Example
curl -X PATCH "https://app.checkcourt.de/api/v1/teams/{id}" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"name":"name"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
teamIdstringrequired
delete/teams/{id}
Deletes the team and its roster assignments. Team bookings are kept but lose their team link.
Parameters
idpath · stringrequiredTeam id
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/teams/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
teamIdstringrequired
post/teams/{id}/members
Adds a club member to the team as player or leader. The same person can hold both roles (two separate entries). Note that tenantUserId is the membership id from the team detail response, not the user id.
Beyond the scope, this endpoint needs management rights for the specific team: the actor must have the admin role or be a leader of this team. Management keys carry no role and are therefore always rejected here; use a personal key of an admin or team leader.
Parameters
idpath · stringrequiredTeam id
Request body
tenantUserIdstringrequiredMembership id (tenantUser)
role"player" | "leader"required
Example
curl -X POST "https://app.checkcourt.de/api/v1/teams/{id}/members" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"tenantUserId":"tenantUserId","role":"player"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
teamIdstringrequired
delete/teams/{id}/members/{userId}
Removes one role assignment. Because a person can be player and leader at once, the role query parameter picks which assignment to remove.
Beyond the scope, this endpoint needs management rights for the specific team: the actor must have the admin role or be a leader of this team. Management keys carry no role and are therefore always rejected here; use a personal key of an admin or team leader.
Parameters
idpath · stringrequiredTeam id
userIdpath · stringrequiredMembership id (tenantUser) of the assignment
rolequery · "player" | "leader"requiredWhich role assignment to remove
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/teams/{id}/members/{userId}?role=player" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequired
Booking policies
get/policies
Policies form the booking rule engine: each policy has conditions (which booking attempts it applies to) and attributes (the rule values it contributes). On every booking attempt, enabled policies are evaluated in ascending priority; for each attribute the first policy that sets it wins, and the default policy provides the fallback values. This endpoint returns all non-default policies ordered by priority. Fetch the default policy via its id if needed.
Example
curl "https://app.checkcourt.de/api/v1/policies" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
policiesobject[]requiredidstringrequiredtenantIdstringnamestringrequireddescriptionstring | nullpriorityintegerrequiredLower number = evaluated earlier
isEnabledbooleanrequiredisDefaultbooleanrequiredExactly one default policy exists per tenant. It matches everything, cannot be renamed or deleted.
conditionsobjectSelects which booking attempts a policy applies to. Omitted fields match everything; all present fields must match (AND).
daysOfWeek"mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun"[]timeOfDayobjectfromstringTime of day as HH:MM or HH:MM:SS
tostringTime of day as HH:MM or HH:MM:SS
dateRangeobjectfromstring (date)tostring (date)
courtIdsinteger[]categoryIdsstring[]bookerTeamIdsstring[]bookerGroupIdsstring[]Role or group ids (GET /roles). Matches when the booker holds at least one of them. The base role Mitglied (member) is rejected because it applies to everyone.
actions"create" | "cancel" | "edit"[]
attributesobjectRule values a policy contributes for matching attempts. Policies are evaluated by ascending priority; the first policy that sets an attribute wins, the default policy fills the rest.
allowbooleanHard allow/deny for matching attempts
denyReasonstringMessage shown when allow=false
maxBookingMinutesintegerminBookingMinutesintegermaxAdvanceBookingDaysintegerHow far into the future bookings may start
maxActiveBookingsintegerCap on simultaneously active (future) bookings per user
maxBookingsPerDayPerUserintegermaxParallelBookingsintegerCap on overlapping bookings per user across courts
cancellationDeadlineHoursintegerslotStartIntervalMinutesintegerAllowed booking start raster, e.g. 30 for :00/:30
slotStartOffsetMinutesintegerbookingStartTimestringTime of day as HH:MM or HH:MM:SS
bookingEndTimestringTime of day as HH:MM or HH:MM:SS
bookingDurationStepMinutesintegerStep the booking duration must be a multiple of. 0 or absent falls back to slotStartIntervalMinutes (which only applies when the start raster is active).
minTeammatesintegerHow many teammates or guests a booking must name; 0 makes them optional. The booker does not count.
isNameablebooleanWhether bookings may carry a free-text title
createdAtstring (date-time)ISO 8601 timestamp
updatedAtstring (date-time)ISO 8601 timestamp
post/policies
Policies form the booking rule engine: each policy has conditions (which booking attempts it applies to) and attributes (the rule values it contributes). On every booking attempt, enabled policies are evaluated in ascending priority; for each attribute the first policy that sets it wins, and the default policy provides the fallback values. New policies are appended with the lowest priority (evaluated last before the default). Cross-field validation applies: time and date ranges must be ordered, min/max durations consistent.
Request body
namestringdescriptionstringisEnabledbooleandefault: trueconditionsobjectSelects which booking attempts a policy applies to. Omitted fields match everything; all present fields must match (AND).
daysOfWeek"mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun"[]timeOfDayobjectfromstringTime of day as HH:MM or HH:MM:SS
tostringTime of day as HH:MM or HH:MM:SS
dateRangeobjectfromstring (date)tostring (date)
courtIdsinteger[]categoryIdsstring[]bookerTeamIdsstring[]bookerGroupIdsstring[]Role or group ids (GET /roles). Matches when the booker holds at least one of them. The base role Mitglied (member) is rejected because it applies to everyone.
actions"create" | "cancel" | "edit"[]
attributesobjectRule values a policy contributes for matching attempts. Policies are evaluated by ascending priority; the first policy that sets an attribute wins, the default policy fills the rest.
allowbooleanHard allow/deny for matching attempts
denyReasonstringMessage shown when allow=false
maxBookingMinutesintegerminBookingMinutesintegermaxAdvanceBookingDaysintegerHow far into the future bookings may start
maxActiveBookingsintegerCap on simultaneously active (future) bookings per user
maxBookingsPerDayPerUserintegermaxParallelBookingsintegerCap on overlapping bookings per user across courts
cancellationDeadlineHoursintegerslotStartIntervalMinutesintegerAllowed booking start raster, e.g. 30 for :00/:30
slotStartOffsetMinutesintegerbookingStartTimestringTime of day as HH:MM or HH:MM:SS
bookingEndTimestringTime of day as HH:MM or HH:MM:SS
bookingDurationStepMinutesintegerStep the booking duration must be a multiple of. 0 or absent falls back to slotStartIntervalMinutes (which only applies when the start raster is active).
minTeammatesintegerHow many teammates or guests a booking must name; 0 makes them optional. The booker does not count.
isNameablebooleanWhether bookings may carry a free-text title
Example
curl -X POST "https://app.checkcourt.de/api/v1/policies" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
policyobjectrequiredidstringrequiredtenantIdstringnamestringrequireddescriptionstring | nullpriorityintegerrequiredLower number = evaluated earlier
isEnabledbooleanrequiredisDefaultbooleanrequiredExactly one default policy exists per tenant. It matches everything, cannot be renamed or deleted.
conditionsobjectSelects which booking attempts a policy applies to. Omitted fields match everything; all present fields must match (AND).
daysOfWeek"mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun"[]timeOfDayobjectfromstringTime of day as HH:MM or HH:MM:SS
tostringTime of day as HH:MM or HH:MM:SS
dateRangeobjectfromstring (date)tostring (date)
courtIdsinteger[]categoryIdsstring[]bookerTeamIdsstring[]bookerGroupIdsstring[]Role or group ids (GET /roles). Matches when the booker holds at least one of them. The base role Mitglied (member) is rejected because it applies to everyone.
actions"create" | "cancel" | "edit"[]
attributesobjectRule values a policy contributes for matching attempts. Policies are evaluated by ascending priority; the first policy that sets an attribute wins, the default policy fills the rest.
allowbooleanHard allow/deny for matching attempts
denyReasonstringMessage shown when allow=false
maxBookingMinutesintegerminBookingMinutesintegermaxAdvanceBookingDaysintegerHow far into the future bookings may start
maxActiveBookingsintegerCap on simultaneously active (future) bookings per user
maxBookingsPerDayPerUserintegermaxParallelBookingsintegerCap on overlapping bookings per user across courts
cancellationDeadlineHoursintegerslotStartIntervalMinutesintegerAllowed booking start raster, e.g. 30 for :00/:30
slotStartOffsetMinutesintegerbookingStartTimestringTime of day as HH:MM or HH:MM:SS
bookingEndTimestringTime of day as HH:MM or HH:MM:SS
bookingDurationStepMinutesintegerStep the booking duration must be a multiple of. 0 or absent falls back to slotStartIntervalMinutes (which only applies when the start raster is active).
minTeammatesintegerHow many teammates or guests a booking must name; 0 makes them optional. The booker does not count.
isNameablebooleanWhether bookings may carry a free-text title
createdAtstring (date-time)ISO 8601 timestamp
updatedAtstring (date-time)ISO 8601 timestamp
get/policies/{id}
Parameters
idpath · stringrequiredPolicy id
Example
curl "https://app.checkcourt.de/api/v1/policies/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
idstringrequiredtenantIdstringnamestringrequireddescriptionstring | nullpriorityintegerrequiredLower number = evaluated earlier
isEnabledbooleanrequiredisDefaultbooleanrequiredExactly one default policy exists per tenant. It matches everything, cannot be renamed or deleted.
conditionsobjectSelects which booking attempts a policy applies to. Omitted fields match everything; all present fields must match (AND).
daysOfWeek"mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun"[]timeOfDayobjectfromstringTime of day as HH:MM or HH:MM:SS
tostringTime of day as HH:MM or HH:MM:SS
dateRangeobjectfromstring (date)tostring (date)
courtIdsinteger[]categoryIdsstring[]bookerTeamIdsstring[]bookerGroupIdsstring[]Role or group ids (GET /roles). Matches when the booker holds at least one of them. The base role Mitglied (member) is rejected because it applies to everyone.
actions"create" | "cancel" | "edit"[]
attributesobjectRule values a policy contributes for matching attempts. Policies are evaluated by ascending priority; the first policy that sets an attribute wins, the default policy fills the rest.
allowbooleanHard allow/deny for matching attempts
denyReasonstringMessage shown when allow=false
maxBookingMinutesintegerminBookingMinutesintegermaxAdvanceBookingDaysintegerHow far into the future bookings may start
maxActiveBookingsintegerCap on simultaneously active (future) bookings per user
maxBookingsPerDayPerUserintegermaxParallelBookingsintegerCap on overlapping bookings per user across courts
cancellationDeadlineHoursintegerslotStartIntervalMinutesintegerAllowed booking start raster, e.g. 30 for :00/:30
slotStartOffsetMinutesintegerbookingStartTimestringTime of day as HH:MM or HH:MM:SS
bookingEndTimestringTime of day as HH:MM or HH:MM:SS
bookingDurationStepMinutesintegerStep the booking duration must be a multiple of. 0 or absent falls back to slotStartIntervalMinutes (which only applies when the start raster is active).
minTeammatesintegerHow many teammates or guests a booking must name; 0 makes them optional. The booker does not count.
isNameablebooleanWhether bookings may carry a free-text title
createdAtstring (date-time)ISO 8601 timestamp
updatedAtstring (date-time)ISO 8601 timestamp
patch/policies/{id}
Replaces the given fields. The default policy is special: it cannot be renamed, its conditions are immutable (it always matches everything), and its attributes can only be extended so that every attribute keeps a fallback value.
Parameters
idpath · stringrequiredPolicy id
Request body
namestringdescriptionstringisEnabledbooleandefault: trueconditionsobjectSelects which booking attempts a policy applies to. Omitted fields match everything; all present fields must match (AND).
daysOfWeek"mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun"[]timeOfDayobjectfromstringTime of day as HH:MM or HH:MM:SS
tostringTime of day as HH:MM or HH:MM:SS
dateRangeobjectfromstring (date)tostring (date)
courtIdsinteger[]categoryIdsstring[]bookerTeamIdsstring[]bookerGroupIdsstring[]Role or group ids (GET /roles). Matches when the booker holds at least one of them. The base role Mitglied (member) is rejected because it applies to everyone.
actions"create" | "cancel" | "edit"[]
attributesobjectRule values a policy contributes for matching attempts. Policies are evaluated by ascending priority; the first policy that sets an attribute wins, the default policy fills the rest.
allowbooleanHard allow/deny for matching attempts
denyReasonstringMessage shown when allow=false
maxBookingMinutesintegerminBookingMinutesintegermaxAdvanceBookingDaysintegerHow far into the future bookings may start
maxActiveBookingsintegerCap on simultaneously active (future) bookings per user
maxBookingsPerDayPerUserintegermaxParallelBookingsintegerCap on overlapping bookings per user across courts
cancellationDeadlineHoursintegerslotStartIntervalMinutesintegerAllowed booking start raster, e.g. 30 for :00/:30
slotStartOffsetMinutesintegerbookingStartTimestringTime of day as HH:MM or HH:MM:SS
bookingEndTimestringTime of day as HH:MM or HH:MM:SS
bookingDurationStepMinutesintegerStep the booking duration must be a multiple of. 0 or absent falls back to slotStartIntervalMinutes (which only applies when the start raster is active).
minTeammatesintegerHow many teammates or guests a booking must name; 0 makes them optional. The booker does not count.
isNameablebooleanWhether bookings may carry a free-text title
Example
curl -X PATCH "https://app.checkcourt.de/api/v1/policies/{id}" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
policyobjectrequiredidstringrequiredtenantIdstringnamestringrequireddescriptionstring | nullpriorityintegerrequiredLower number = evaluated earlier
isEnabledbooleanrequiredisDefaultbooleanrequiredExactly one default policy exists per tenant. It matches everything, cannot be renamed or deleted.
conditionsobjectSelects which booking attempts a policy applies to. Omitted fields match everything; all present fields must match (AND).
daysOfWeek"mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun"[]timeOfDayobjectfromstringTime of day as HH:MM or HH:MM:SS
tostringTime of day as HH:MM or HH:MM:SS
dateRangeobjectfromstring (date)tostring (date)
courtIdsinteger[]categoryIdsstring[]bookerTeamIdsstring[]bookerGroupIdsstring[]Role or group ids (GET /roles). Matches when the booker holds at least one of them. The base role Mitglied (member) is rejected because it applies to everyone.
actions"create" | "cancel" | "edit"[]
attributesobjectRule values a policy contributes for matching attempts. Policies are evaluated by ascending priority; the first policy that sets an attribute wins, the default policy fills the rest.
allowbooleanHard allow/deny for matching attempts
denyReasonstringMessage shown when allow=false
maxBookingMinutesintegerminBookingMinutesintegermaxAdvanceBookingDaysintegerHow far into the future bookings may start
maxActiveBookingsintegerCap on simultaneously active (future) bookings per user
maxBookingsPerDayPerUserintegermaxParallelBookingsintegerCap on overlapping bookings per user across courts
cancellationDeadlineHoursintegerslotStartIntervalMinutesintegerAllowed booking start raster, e.g. 30 for :00/:30
slotStartOffsetMinutesintegerbookingStartTimestringTime of day as HH:MM or HH:MM:SS
bookingEndTimestringTime of day as HH:MM or HH:MM:SS
bookingDurationStepMinutesintegerStep the booking duration must be a multiple of. 0 or absent falls back to slotStartIntervalMinutes (which only applies when the start raster is active).
minTeammatesintegerHow many teammates or guests a booking must name; 0 makes them optional. The booker does not count.
isNameablebooleanWhether bookings may carry a free-text title
createdAtstring (date-time)ISO 8601 timestamp
updatedAtstring (date-time)ISO 8601 timestamp
delete/policies/{id}
The default policy cannot be deleted (422).
Parameters
idpath · stringrequiredPolicy id
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/policies/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequiredidstringrequired
post/policies/{id}/toggle
Disabled policies are skipped during evaluation but keep their priority slot.
Parameters
idpath · stringrequiredPolicy id
Request body
enabledbooleanrequired
Example
curl -X POST "https://app.checkcourt.de/api/v1/policies/{id}/toggle" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"enabled":true}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
policyobjectrequiredidstringrequiredtenantIdstringnamestringrequireddescriptionstring | nullpriorityintegerrequiredLower number = evaluated earlier
isEnabledbooleanrequiredisDefaultbooleanrequiredExactly one default policy exists per tenant. It matches everything, cannot be renamed or deleted.
conditionsobjectSelects which booking attempts a policy applies to. Omitted fields match everything; all present fields must match (AND).
daysOfWeek"mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun"[]timeOfDayobjectfromstringTime of day as HH:MM or HH:MM:SS
tostringTime of day as HH:MM or HH:MM:SS
dateRangeobjectfromstring (date)tostring (date)
courtIdsinteger[]categoryIdsstring[]bookerTeamIdsstring[]bookerGroupIdsstring[]Role or group ids (GET /roles). Matches when the booker holds at least one of them. The base role Mitglied (member) is rejected because it applies to everyone.
actions"create" | "cancel" | "edit"[]
attributesobjectRule values a policy contributes for matching attempts. Policies are evaluated by ascending priority; the first policy that sets an attribute wins, the default policy fills the rest.
allowbooleanHard allow/deny for matching attempts
denyReasonstringMessage shown when allow=false
maxBookingMinutesintegerminBookingMinutesintegermaxAdvanceBookingDaysintegerHow far into the future bookings may start
maxActiveBookingsintegerCap on simultaneously active (future) bookings per user
maxBookingsPerDayPerUserintegermaxParallelBookingsintegerCap on overlapping bookings per user across courts
cancellationDeadlineHoursintegerslotStartIntervalMinutesintegerAllowed booking start raster, e.g. 30 for :00/:30
slotStartOffsetMinutesintegerbookingStartTimestringTime of day as HH:MM or HH:MM:SS
bookingEndTimestringTime of day as HH:MM or HH:MM:SS
bookingDurationStepMinutesintegerStep the booking duration must be a multiple of. 0 or absent falls back to slotStartIntervalMinutes (which only applies when the start raster is active).
minTeammatesintegerHow many teammates or guests a booking must name; 0 makes them optional. The booker does not count.
isNameablebooleanWhether bookings may carry a free-text title
createdAtstring (date-time)ISO 8601 timestamp
updatedAtstring (date-time)ISO 8601 timestamp
post/policies/reorder
Send all non-default policy ids in the desired evaluation order (first = highest precedence). Priorities are reassigned accordingly.
Request body
orderedIdsstring[]required
Example
curl -X POST "https://app.checkcourt.de/api/v1/policies/reorder" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"orderedIds":[]}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequired
get/booking-policies
Evaluates the whole policy engine for the calling member, including the roles and groups they hold, and returns the resolved rule values per court and per booking category, plus the categories the actor may book in. This is what the booking UI uses to know slot lengths, advance limits and quotas.
Only meaningful with personal keys: management keys have no member to evaluate for and receive 400.
Example
curl "https://app.checkcourt.de/api/v1/booking-policies" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
evaluatedAtstring (date-time)ISO 8601 timestamp
courtsobject[]courtIdintegercourtNamestringattributesobjectRule values a policy contributes for matching attempts. Policies are evaluated by ascending priority; the first policy that sets an attribute wins, the default policy fills the rest.
allowbooleanHard allow/deny for matching attempts
denyReasonstringMessage shown when allow=false
maxBookingMinutesintegerminBookingMinutesintegermaxAdvanceBookingDaysintegerHow far into the future bookings may start
maxActiveBookingsintegerCap on simultaneously active (future) bookings per user
maxBookingsPerDayPerUserintegermaxParallelBookingsintegerCap on overlapping bookings per user across courts
cancellationDeadlineHoursintegerslotStartIntervalMinutesintegerAllowed booking start raster, e.g. 30 for :00/:30
slotStartOffsetMinutesintegerbookingStartTimestringTime of day as HH:MM or HH:MM:SS
bookingEndTimestringTime of day as HH:MM or HH:MM:SS
bookingDurationStepMinutesintegerStep the booking duration must be a multiple of. 0 or absent falls back to slotStartIntervalMinutes (which only applies when the start raster is active).
minTeammatesintegerHow many teammates or guests a booking must name; 0 makes them optional. The booker does not count.
isNameablebooleanWhether bookings may carry a free-text title
perCategoryobject[]categoryIdstringcategorySlugstringcategoryNamestringattributesobjectRule values a policy contributes for matching attempts. Policies are evaluated by ascending priority; the first policy that sets an attribute wins, the default policy fills the rest.
allowbooleanHard allow/deny for matching attempts
denyReasonstringMessage shown when allow=false
maxBookingMinutesintegerminBookingMinutesintegermaxAdvanceBookingDaysintegerHow far into the future bookings may start
maxActiveBookingsintegerCap on simultaneously active (future) bookings per user
maxBookingsPerDayPerUserintegermaxParallelBookingsintegerCap on overlapping bookings per user across courts
cancellationDeadlineHoursintegerslotStartIntervalMinutesintegerAllowed booking start raster, e.g. 30 for :00/:30
slotStartOffsetMinutesintegerbookingStartTimestringTime of day as HH:MM or HH:MM:SS
bookingEndTimestringTime of day as HH:MM or HH:MM:SS
bookingDurationStepMinutesintegerStep the booking duration must be a multiple of. 0 or absent falls back to slotStartIntervalMinutes (which only applies when the start raster is active).
minTeammatesintegerHow many teammates or guests a booking must name; 0 makes them optional. The booker does not count.
isNameablebooleanWhether bookings may carry a free-text title
grantedCategoryIdsstring[]Categories the actor may book in
Booking categories
get/booking-categories
Categories classify bookings (e.g. regular play, training, match) and control visibility: isPublic decides whether other members see details, anonymizeGuests hides guest names. System categories (isSystem) are created automatically and have stable slugs.
Example
curl "https://app.checkcourt.de/api/v1/booking-categories" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
categoriesobject[]requiredidstringrequiredtenantIdstringnamestringrequiredslugstringrequiredStable identifier; system categories use fixed slugs like "regular"
colorstringrequiredHex color from the predefined palette
isSystembooleanrequiredSystem categories cannot be renamed or deleted
isPublicbooleanWhether bookings in this category show details to other members
anonymizeGuestsbooleanallowUserTogglebooleanMembers may toggle visibility per booking
allowUserToggleGuestsbooleansortOrderintegercreatedAtstring (date-time)ISO 8601 timestamp
updatedAtstring (date-time)ISO 8601 timestamp
post/booking-categories
Creates a custom category. color must come from the predefined palette; the number of categories per club is capped (422 when reached). Visibility flags can be adjusted afterwards via PATCH.
Request body
namestringrequiredcolorstringrequiredHex color from the predefined palette
Example
curl -X POST "https://app.checkcourt.de/api/v1/booking-categories" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"name":"name","color":"color"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
categoryobjectrequiredidstringrequiredtenantIdstringnamestringrequiredslugstringrequiredStable identifier; system categories use fixed slugs like "regular"
colorstringrequiredHex color from the predefined palette
isSystembooleanrequiredSystem categories cannot be renamed or deleted
isPublicbooleanWhether bookings in this category show details to other members
anonymizeGuestsbooleanallowUserTogglebooleanMembers may toggle visibility per booking
allowUserToggleGuestsbooleansortOrderintegercreatedAtstring (date-time)ISO 8601 timestamp
updatedAtstring (date-time)ISO 8601 timestamp
patch/booking-categories/{id}
Partial update. System categories cannot be renamed (422), but their visibility flags and color may be changed.
Parameters
idpath · stringrequiredCategory id
Request body
namestringcolorstringisPublicbooleananonymizeGuestsbooleanallowUserTogglebooleanallowUserToggleGuestsbooleansortOrderinteger
Example
curl -X PATCH "https://app.checkcourt.de/api/v1/booking-categories/{id}" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
categoryobjectrequiredidstringrequiredtenantIdstringnamestringrequiredslugstringrequiredStable identifier; system categories use fixed slugs like "regular"
colorstringrequiredHex color from the predefined palette
isSystembooleanrequiredSystem categories cannot be renamed or deleted
isPublicbooleanWhether bookings in this category show details to other members
anonymizeGuestsbooleanallowUserTogglebooleanMembers may toggle visibility per booking
allowUserToggleGuestsbooleansortOrderintegercreatedAtstring (date-time)ISO 8601 timestamp
updatedAtstring (date-time)ISO 8601 timestamp
delete/booking-categories/{id}
System categories cannot be deleted (422). Existing bookings of a deleted category are reassigned to the default ("regular") category.
Parameters
idpath · stringrequiredCategory id
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/booking-categories/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequired
Guest fees
get/guest-fees
History of guest fees that have been marked as paid, newest settlement first. search matches the guest's name and the owing member's name or email. Use userId to see one member's history.
Parameters
searchquery · stringSubstring search in guest name, member name, member email
userIdquery · stringOnly fees owed by this member (user id)
pagequery · integer1-based page number
pageSizequery · integerEntries per page
Example
curl "https://app.checkcourt.de/api/v1/guest-fees" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
entriesobject[]requiredidstringguestNamestringfeeAmountintegerAmount in euro cents
settledAtstring (date-time)ISO 8601 timestamp
createdAtstring (date-time)ISO 8601 timestamp
memberIdstringUser id of the member who owes/paid the fee
memberNamestringmemberEmailstring (email)bookingDatestring (date)
totalintegerrequiredpageintegerrequiredpageSizeintegerrequiredtotalPagesintegerrequired
get/guest-fees/open
All unsettled guest fees one member currently owes, including the booking context (date, time, court) each fee originated from.
Parameters
userIdquery · stringrequiredUser id of the member
Example
curl "https://app.checkcourt.de/api/v1/guest-fees/open?userId=userId" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
entriesobject[]requiredidstringguestNamestringfeeAmountintegerAmount in euro cents
createdAtstring (date-time)ISO 8601 timestamp
bookingDatestring (date)bookingStartTimestringTime of day as HH:MM or HH:MM:SS
bookingEndTimestringTime of day as HH:MM or HH:MM:SS
courtNamestring
get/guest-fees/members
Distinct members that have at least one settled guest fee. Intended to populate filter dropdowns for the history view.
Example
curl "https://app.checkcourt.de/api/v1/guest-fees/members" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
membersobject[]requiredidstringrequirednamestringrequired
delete/guest-fees/{entryId}
Moves a fee entry back from "paid" to "open", e.g. after marking the wrong entry. Only entries currently marked as paid can be reverted (400 otherwise).
Parameters
entryIdpath · stringrequiredGuest fee entry id
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/guest-fees/{entryId}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequired
post/guest-fees/reset
Marks open fees as paid. Provide exactly ONE of the two parameters: targetUserId settles all open fees of one member (typical after they paid in person); untilDate settles all open fees of the whole club created on or before that date (typical after a billing cut-off).
Request body
targetUserIdstringUser id; settles this member's open fees
untilDatestring (date)Settles all open fees created on or before this date
Example
curl -X POST "https://app.checkcourt.de/api/v1/guest-fees/reset" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequired
Invite links
get/invite-links
All invite links of the club with usage stats. The public join URL for a link is /join/{code} on the web app host.
Example
curl "https://app.checkcourt.de/api/v1/invite-links" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
inviteLinksobject[]requiredidintegerrequiredNumeric link id
codestringrequired12-character alphanumeric code; the public join URL is /join/{code}
notestring | nullInternal note, e.g. where the link was published
maxUsesinteger | nullnull = unlimited
usesCountintegerrequiredcreatedAtstring (date-time)ISO 8601 timestamp
lastUsedAtstring | null (date-time)ISO 8601 timestamp
createdByIdstring | nullcreatedByNamestring | nullrequiresAdminApprovalbooleanrequiredIf true, members joining via this link stay in "pending" state until an admin approves them
post/invite-links
Creates a shareable self-signup link. maxUses limits how many members can join through it (null = unlimited). With requiresAdminApproval=true, members joining via this link stay in a pending state and cannot book until an admin approves them. Requires the club's signed data processing agreement (AVV), otherwise 422.
Request body
notestringInternal note, e.g. where the link is published
maxUsesinteger | nullrequiresAdminApprovalbooleandefault: false
Example
curl -X POST "https://app.checkcourt.de/api/v1/invite-links" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
inviteLinkobjectrequiredidintegerrequiredNumeric link id
codestringrequired12-character alphanumeric code; the public join URL is /join/{code}
notestring | nullInternal note, e.g. where the link was published
maxUsesinteger | nullnull = unlimited
usesCountintegerrequiredcreatedAtstring (date-time)ISO 8601 timestamp
lastUsedAtstring | null (date-time)ISO 8601 timestamp
createdByIdstring | nullcreatedByNamestring | nullrequiresAdminApprovalbooleanrequiredIf true, members joining via this link stay in "pending" state until an admin approves them
patch/invite-links/{id}
At least one of the two fields must be present. The code and usage counters of a link cannot be changed; create a new link instead.
Parameters
idpath · integerrequiredNumeric invite link id
Request body
notestringrequiresAdminApprovalboolean
Example
curl -X PATCH "https://app.checkcourt.de/api/v1/invite-links/{id}" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequiredidintegerrequired
delete/invite-links/{id}
The link stops working immediately. Members who already joined through it are unaffected.
Parameters
idpath · integerrequiredNumeric invite link id
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/invite-links/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequiredidintegerrequired
Settings
get/settings
Returns the whitelisted club settings (dashboard announcement, check-in, guest fees, timezone). The club name is not a setting: it lives on the tenant row, see PATCH /tenant. Keys without a stored value are omitted from the response. All values are strings; see the Settings schema for the semantic type of each key.
Example
curl "https://app.checkcourt.de/api/v1/settings" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
settingsobjectrequiredWhitelisted club settings. The club name is not here: it lives on the tenant row, see
PATCH /tenant. The backing store is a string key-value table, so every value is a string:checkin_enabledandguest_fees_enabledare "true"/"false",checkin_deadline_minutesandguest_fee_centsare integers encoded as strings,timezoneis an IANA zone like "Europe/Berlin".announcement_textstringBanner text shown on the dashboard; empty string hides the banner
announcement_linkstringOptional URL the announcement banner links to
checkin_deadline_minutesstringInteger as string; minutes after booking start until check-in expires
checkin_enabled"true" | "false"guest_fee_centsstringInteger as string; fee per guest in cents
guest_fees_enabled"true" | "false"timezonestring
patch/settings
Merge semantics: only the keys you send are written, others stay unchanged. Keys outside the whitelist are silently ignored, values are capped at 2000 characters. The response reports how many settings actually changed.
Request body
announcement_textstringBanner text shown on the dashboard; empty string hides the banner
announcement_linkstringOptional URL the announcement banner links to
checkin_deadline_minutesstringInteger as string; minutes after booking start until check-in expires
checkin_enabled"true" | "false"guest_fee_centsstringInteger as string; fee per guest in cents
guest_fees_enabled"true" | "false"timezonestring
Example
curl -X PATCH "https://app.checkcourt.de/api/v1/settings" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
updatedintegerrequiredNumber of settings whose value changed
Club
get/tenant
Id, display name and short code of the club the API key belongs to. Useful as a connectivity check and to label data in integrations.
Example
curl "https://app.checkcourt.de/api/v1/tenant" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
tenantobjectrequiredidstringrequirednamestringrequiredClub name
slugstringrequiredShort club code (uppercase letters and digits), used e.g. in member codes
patch/tenant
Send name, slug, or both; at least one is required. name is the club's display name (trimmed, 2-120 chars) and is the single source for the name shown across the product. The slug appears in member codes and on-site displays. Slug input is normalized to uppercase A-Z and 0-9, then length-checked (3-8 chars) and checked for uniqueness across all clubs (422 if taken).
Request body
namestringslugstring
Example
curl -X PATCH "https://app.checkcourt.de/api/v1/tenant" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
namestringThe normalized name that was stored, if it was sent
slugstringThe normalized slug that was stored, if it was sent
Endpoints for apps
Only with app tokens, never with API keys:
GET /app/installation(club app tokencca_…): the installation the token was issued for, with the club's settings. More in API for apps.…/metadataon bookings, courts and members, andPOST /app/events(club app token): data and events shared with other apps, see Connections between apps.POST /app/notifications(withnotifications:send): notify a member, see Notifications.
get/app/installation
Only for club app installation tokens (cca_). Returns the installation the token was minted for, including the club's settings from the manifest's settingsSchema. Read it after app.installed, app.upgraded and app.settings_updated; the event payloads carry ids only. Member app tokens (ccu_) and API keys receive 403: member installations have no settings, a member app reads its club's settings with the club installation token.
Example
curl "https://app.checkcourt.de/api/v1/app/installation" \
-H "Authorization: Bearer cca_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
installation_idstringrequiredapp_idstringrequiredversionintegerrequiredVersion of the app the installation currently runs on
status"active" | "pending_upgrade" | "paused" | "uninstalled"requiredtenant_idstringrequiredThe club the installation belongs to
scopesstring[]requiredScopes the token holds right now
settingsobjectrequiredValues the club entered in the form generated from the manifest's
settingsSchema, defaults filled in.{}when the app has no settings.installed_atstring (date-time)required
get/bookings/{id}/metadata
Returns the calling app's own keys on this booking and the keys of other apps that the caller declares under reads.metadata in its manifest, the producer declares under shares.metadata, and the club approved. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredBooking id
Example
curl "https://app.checkcourt.de/api/v1/bookings/{id}/metadata" \
-H "Authorization: Bearer cca_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
metadataobjectrequiredGrouped by app slug, then by key. Contains the calling app's own keys and the keys of other apps that the caller declares under
reads.metadataand the club approved.
put/bookings/{id}/metadata/{key}
Writes one of the app's own keys. The key must be declared under shares.metadata with object: "booking". A changed value sends app.metadata_changed to the apps the club connected to yours for this key. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredBooking id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Request body
valueobjectrequiredAny JSON value except null, at most 4096 bytes serialized
Example
curl -X PUT "https://app.checkcourt.de/api/v1/bookings/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…" \
-H "Content-Type: application/json" \
-d '{"value":"value"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
keystringrequiredvalueobjectrequiredupdated_atstring (date-time)required
delete/bookings/{id}/metadata/{key}
Removes the app's own value. Works for keys a newer manifest no longer declares, so an app can clean up. deleted: false when there was nothing to remove. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredBooking id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/bookings/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
deletedbooleanrequired
get/courts/{id}/metadata
Returns the calling app's own keys on this court and the keys of other apps that the caller declares under reads.metadata in its manifest, the producer declares under shares.metadata, and the club approved. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · integerrequiredNumeric court id
Example
curl "https://app.checkcourt.de/api/v1/courts/{id}/metadata" \
-H "Authorization: Bearer cca_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
metadataobjectrequiredGrouped by app slug, then by key. Contains the calling app's own keys and the keys of other apps that the caller declares under
reads.metadataand the club approved.
put/courts/{id}/metadata/{key}
Writes one of the app's own keys. The key must be declared under shares.metadata with object: "court". A changed value sends app.metadata_changed to the apps the club connected to yours for this key. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · integerrequiredNumeric court id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Request body
valueobjectrequiredAny JSON value except null, at most 4096 bytes serialized
Example
curl -X PUT "https://app.checkcourt.de/api/v1/courts/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…" \
-H "Content-Type: application/json" \
-d '{"value":"value"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
keystringrequiredvalueobjectrequiredupdated_atstring (date-time)required
delete/courts/{id}/metadata/{key}
Removes the app's own value. Works for keys a newer manifest no longer declares, so an app can clean up. deleted: false when there was nothing to remove. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · integerrequiredNumeric court id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/courts/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
deletedbooleanrequired
get/members/{id}/metadata
Returns the calling app's own keys on this member and the keys of other apps that the caller declares under reads.metadata in its manifest, the producer declares under shares.metadata, and the club approved. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredThe member's user id
Example
curl "https://app.checkcourt.de/api/v1/members/{id}/metadata" \
-H "Authorization: Bearer cca_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
metadataobjectrequiredGrouped by app slug, then by key. Contains the calling app's own keys and the keys of other apps that the caller declares under
reads.metadataand the club approved.
put/members/{id}/metadata/{key}
Writes one of the app's own keys. The key must be declared under shares.metadata with object: "member". A changed value sends app.metadata_changed to the apps the club connected to yours for this key. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredThe member's user id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Request body
valueobjectrequiredAny JSON value except null, at most 4096 bytes serialized
Example
curl -X PUT "https://app.checkcourt.de/api/v1/members/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…" \
-H "Content-Type: application/json" \
-d '{"value":"value"}'Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
objectobjectrequiredtype"booking" | "court" | "member"requiredidstringrequired
keystringrequiredvalueobjectrequiredupdated_atstring (date-time)required
delete/members/{id}/metadata/{key}
Removes the app's own value. Works for keys a newer manifest no longer declares, so an app can clean up. deleted: false when there was nothing to remove. Only for club app installation tokens (cca_); member app tokens and API keys receive 403. Data between apps flows only along connections the club approved under Admin, Apps, Verbindungen.
Parameters
idpath · stringrequiredThe member's user id
keypath · stringrequiredA key your app declares under
shares.metadatafor this object type
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/members/{id}/metadata/{key}" \
-H "Authorization: Bearer cca_…"Responses
200Success400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
deletedbooleanrequired
post/app/events
Only for club app installation tokens (cca_). The name must be declared under emits in the manifest. CheckCourt delivers the event as app.<your app slug>.<name> through the regular signed webhooks, only to club installations that declare it under subscribes and that the club connected to yours. At most 60 events per minute per installation (429 with Retry-After beyond).
Request body
namestringrequiredAn event the app declares under
emitsin its manifestdataobjectAt most 4096 bytes serialized. Validated against the event's
schemaif the manifest declares one. Defaults to{}.subjectobjectOptional club object the event is about; becomes
objectin the delivery. Needs the read scope of that object type. Without it,objectis the sending installation.type"booking" | "court" | "member"requiredidstringrequired
Example
curl -X POST "https://app.checkcourt.de/api/v1/app/events" \
-H "Authorization: Bearer cca_…" \
-H "Content-Type: application/json" \
-d '{"name":"name"}'Responses
200Accepted into the outbox400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
idstringrequiredevt_…, equal toCheckCourt-Event-Idof every deliverytypestringrequiredapp.<your app slug>.<name>
post/app/notifications
CheckCourt delivers a short message to one member: in their CheckCourt inbox and, if they allow it, by email. Your app never learns the member's contact data.
recipient accepts only ids your app legitimately received:
- the CheckCourt user id, if the installation holds members:read;
- the per-installation pseudonym (psn_...) from an extension context token;
- the pairwise id (usr_...) from GET /api/v1/me of a member who connected your app.
A member app token may only notify its own member. Unknown ids, ids of other clubs, pending or removed members and ids your app may not use all return the same 404.
url is either a path inside CheckCourt (/booking?date=2026-05-01, not /api/...) or an https link on an origin your app registered (a redirect URI or the listing's connect URL).
Limits: 60 per minute and 1,000 per day per installation, 10 per day per member and app (429 with Retry-After). CheckCourt sends at most 3 emails per member and app per day; further messages only reach the inbox. With the same idempotency_key within 24 hours, the first notification's id comes back and nothing is sent again.
The response never reveals whether the member muted your app. If the club paused your app's notifications, the request fails with 422 BUSINESS_RULE.
Request body
recipientstringrequiredCheckCourt user id (needs
members:read), extension pseudonympsn_...or pairwise idusr_...titlestringrequiredOne line of plain text
bodystringrequiredPlain text; line breaks are kept, HTML is shown as text
urlstringPath inside CheckCourt or https link on an origin your app registered
categorystringYour own label for the kind of message, stored for later filtering
idempotency_keystringRepeat a request safely: the same key within 24 hours returns the first notification
Example
curl -X POST "https://app.checkcourt.de/api/v1/app/notifications" \
-H "Authorization: Bearer cca_…" \
-H "Content-Type: application/json" \
-d '{"recipient":"psn_4hQ2v0bD1xZk8yWm3nT7aLc9eRf","title":"Deine Ballmaschine ist bereit","body":"Ab 17:30 Uhr auf Platz 3."}'Responses
200Accepted for delivery400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
idstringrequiredacceptedbooleanrequiredAlways true; whether and how the member sees it stays private
Webhooks
Create and manage the club's webhook endpoints, send test events and read the delivery
log. The Webhooks page explains the format, signature and
event catalog. As long as the webhooks feature is off for the club, these endpoints
respond with 422 BUSINESS_RULE.
get/webhooks
All endpoints of the club, oldest first. Secrets are never included. Returns 422 BUSINESS_RULE while the club has webhooks switched off.
Example
curl "https://app.checkcourt.de/api/v1/webhooks" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
webhooksobject[]requiredidstringrequiredPrefixed
we_urlstring (uri)requiredHTTPS URL that receives the events
descriptionstring | nullrequiredeventTypes"booking.created" | "booking.updated" | "booking.cancelled" | "booking.checked_in" | "member.joined" | "member.updated" | "member.left" | "court.locked" | "court.unlocked"[]requiredstatus"active" | "disabled"requireddisabledReason"manual" | "consecutive_failures" | "null"requiredWhy the endpoint is disabled; null while active
failingSincestring | null (date-time)requiredStart of the current run of failed attempts; null while deliveries succeed
createdAtstring (date-time)requiredupdatedAtstring (date-time)required
post/webhooks
Registers an HTTPS endpoint (plain http only for localhost outside production). The response is the only place the signing secret appears. You can only subscribe to events whose scope (see the event table in the introduction) you hold (403 otherwise). A club can have at most 10 endpoints (422). Returns 422 BUSINESS_RULE while the club has webhooks switched off.
Request body
urlstring (uri)requireddescriptionstring | nulleventTypes"booking.created" | "booking.updated" | "booking.cancelled" | "booking.checked_in" | "member.joined" | "member.updated" | "member.left" | "court.locked" | "court.unlocked"[]required
Example
curl -X POST "https://app.checkcourt.de/api/v1/webhooks" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{"url":"url","eventTypes":[]}'Responses
200The endpoint and its signing secret400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
webhookobjectrequiredA webhook endpoint. The signing secret is never part of this object; it is returned once by create and rotate-secret.
idstringrequiredPrefixed
we_urlstring (uri)requiredHTTPS URL that receives the events
descriptionstring | nullrequiredeventTypes"booking.created" | "booking.updated" | "booking.cancelled" | "booking.checked_in" | "member.joined" | "member.updated" | "member.left" | "court.locked" | "court.unlocked"[]requiredstatus"active" | "disabled"requireddisabledReason"manual" | "consecutive_failures" | "null"requiredWhy the endpoint is disabled; null while active
failingSincestring | null (date-time)requiredStart of the current run of failed attempts; null while deliveries succeed
createdAtstring (date-time)requiredupdatedAtstring (date-time)required
secretstringrequiredSigning secret (
whsec_...). Shown only in this response; store it now.
get/webhooks/{id}
A single endpoint. The secret is never included. Returns 422 BUSINESS_RULE while the club has webhooks switched off.
Parameters
idpath · stringrequiredWebhook id (the
idfield fromGET /webhooks)
Example
curl "https://app.checkcourt.de/api/v1/webhooks/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
webhookobjectrequiredA webhook endpoint. The signing secret is never part of this object; it is returned once by create and rotate-secret.
idstringrequiredPrefixed
we_urlstring (uri)requiredHTTPS URL that receives the events
descriptionstring | nullrequiredeventTypes"booking.created" | "booking.updated" | "booking.cancelled" | "booking.checked_in" | "member.joined" | "member.updated" | "member.left" | "court.locked" | "court.unlocked"[]requiredstatus"active" | "disabled"requireddisabledReason"manual" | "consecutive_failures" | "null"requiredWhy the endpoint is disabled; null while active
failingSincestring | null (date-time)requiredStart of the current run of failed attempts; null while deliveries succeed
createdAtstring (date-time)requiredupdatedAtstring (date-time)required
patch/webhooks/{id}
Partial update. enabled: false disables the endpoint (disabledReason: manual) and fails its pending deliveries; enabled: true re-activates a disabled endpoint and resets its failure run. Replacing eventTypes needs the scope of every listed event. Returns 422 BUSINESS_RULE while the club has webhooks switched off.
Parameters
idpath · stringrequiredWebhook id (the
idfield fromGET /webhooks)
Request body
urlstring (uri)descriptionstring | nulleventTypes"booking.created" | "booking.updated" | "booking.cancelled" | "booking.checked_in" | "member.joined" | "member.updated" | "member.left" | "court.locked" | "court.unlocked"[]enabledboolean
Example
curl -X PATCH "https://app.checkcourt.de/api/v1/webhooks/{id}" \
-H "Authorization: Bearer ck_mgmt_…" \
-H "Content-Type: application/json" \
-d '{}'Responses
200The endpoint as stored400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
webhookobjectrequiredA webhook endpoint. The signing secret is never part of this object; it is returned once by create and rotate-secret.
idstringrequiredPrefixed
we_urlstring (uri)requiredHTTPS URL that receives the events
descriptionstring | nullrequiredeventTypes"booking.created" | "booking.updated" | "booking.cancelled" | "booking.checked_in" | "member.joined" | "member.updated" | "member.left" | "court.locked" | "court.unlocked"[]requiredstatus"active" | "disabled"requireddisabledReason"manual" | "consecutive_failures" | "null"requiredWhy the endpoint is disabled; null while active
failingSincestring | null (date-time)requiredStart of the current run of failed attempts; null while deliveries succeed
createdAtstring (date-time)requiredupdatedAtstring (date-time)required
delete/webhooks/{id}
Deletes the endpoint together with its delivery log. Returns 422 BUSINESS_RULE while the club has webhooks switched off.
Parameters
idpath · stringrequiredWebhook id (the
idfield fromGET /webhooks)
Example
curl -X DELETE "https://app.checkcourt.de/api/v1/webhooks/{id}" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200Success401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
successbooleanrequired
post/webhooks/{id}/rotate-secret
Generates a new secret and returns it once. The old secret stops working immediately; pending retries are signed with the new one. Returns 422 BUSINESS_RULE while the club has webhooks switched off.
Parameters
idpath · stringrequiredWebhook id (the
idfield fromGET /webhooks)
Example
curl -X POST "https://app.checkcourt.de/api/v1/webhooks/{id}/rotate-secret" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200The new secret401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
idstringrequiredsecretstringrequiredSigning secret (
whsec_...). Shown only in this response; store it now.
post/webhooks/{id}/test
Queues a webhook.test event for this endpoint only. It is sent by the next worker run, usually within a minute, and then shows up in the delivery log. Disabled endpoints return 422. Returns 422 BUSINESS_RULE while the club has webhooks switched off.
Parameters
idpath · stringrequiredWebhook id (the
idfield fromGET /webhooks)
Example
curl -X POST "https://app.checkcourt.de/api/v1/webhooks/{id}/test" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200The queued event and delivery401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
eventIdstringrequireddeliveryIdstringrequired
get/webhooks/{id}/deliveries
Newest first, cursor-paginated: pass nextCursor from the previous page as cursor until it is null. Deliveries are kept for 30 days. Returns 422 BUSINESS_RULE while the club has webhooks switched off.
Parameters
idpath · stringrequiredWebhook id (the
idfield fromGET /webhooks)limitquery · integercursorquery · stringnextCursorof the previous page
Example
curl "https://app.checkcourt.de/api/v1/webhooks/{id}/deliveries" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200One page of deliveries400Request is syntactically or semantically invalid401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
deliveriesobject[]requiredidstringrequiredPrefixed
whd_webhookIdstringrequiredeventIdstringrequiredSame value as the
CheckCourt-Event-Idheader and the body'sideventTypestringrequiredA
WebhookEventTypeorwebhook.testeventCreatedAtstring (date-time)requiredstatus"pending" | "succeeded" | "failed"requiredfailedis final: all attempts are used up or the endpoint was disabledattemptsintegerrequirednextAttemptAtstring | null (date-time)requiredlastAttemptAtstring | null (date-time)requiredlastResponseStatusinteger | nullrequiredHTTP status of the latest attempt; null when no response arrived
lastResponseBodystring | nullrequiredFirst 2 KB of the latest response body
lastErrorstring | nullrequiredNetwork or validation error of the latest attempt
createdAtstring (date-time)required
nextCursorstring | nullrequired
post/webhook-deliveries/{id}/redeliver
Queues the delivery again, whatever its current status, with the same event id. It is sent by the next worker run, usually within a minute. Returns 422 when the endpoint is disabled. Returns 422 BUSINESS_RULE while the club has webhooks switched off.
Parameters
idpath · stringrequiredDelivery id (the
idfield fromGET /webhooks/{id}/deliveries)
Example
curl -X POST "https://app.checkcourt.de/api/v1/webhook-deliveries/{id}/redeliver" \
-H "Authorization: Bearer ck_mgmt_…"Responses
200The delivery, now pending401Not authenticated: header missing, key unknown/revoked/expired, or the API key feature is disabled for this club403The key lacks the required scope, team management rights are missing, or a role change exceeds the rank of the key's member404Resource does not exist in this club422Request is valid but violates a business rule (limit reached, duplicate, protected resource)429More than 60 requests per minute from one IP, or more than 120 per minute with one credential
200er-Schema anzeigen
deliveryobjectrequiredOne event sent (or to be sent) to one endpoint, with the outcome of the latest attempt
idstringrequiredPrefixed
whd_webhookIdstringrequiredeventIdstringrequiredSame value as the
CheckCourt-Event-Idheader and the body'sideventTypestringrequiredA
WebhookEventTypeorwebhook.testeventCreatedAtstring (date-time)requiredstatus"pending" | "succeeded" | "failed"requiredfailedis final: all attempts are used up or the endpoint was disabledattemptsintegerrequirednextAttemptAtstring | null (date-time)requiredlastAttemptAtstring | null (date-time)requiredlastResponseStatusinteger | nullrequiredHTTP status of the latest attempt; null when no response arrived
lastResponseBodystring | nullrequiredFirst 2 KB of the latest response body
lastErrorstring | nullrequiredNetwork or validation error of the latest attempt
createdAtstring (date-time)required