Getting Started
Connect your systems to your OpenCourt club with the OpenCourt API.
The OpenCourt API is a REST API for a single club. Use it to read your customers, spaces, schedule, bookings, events, coaches, waivers, orders and transactions, and to receive signals through webhooks when something changes. Every endpoint is listed in the API Reference.
Base URL
https://api.getopencourt.com/v1A beta host with staging data is available at https://api-beta.getopencourt.com/v1.
Authentication
Every request needs an API key. A club admin creates and manages keys in the OpenCourt admin under
Settings → OpenCourt API. A key looks like oc_live_… — send the whole key as a Bearer token:
curl https://api.getopencourt.com/v1/club \
-H "Authorization: Bearer oc_live_xxxxxxxxxxxxxxxx"Each key belongs to one club and only ever sees that club's data. Keep keys on your server — never put one in a browser, a mobile app or a public repository.
Scopes
Each resource has its own <resource>:read scope, such as customers:read or orders:read. A key gets
the read scopes by default. Two scopes are opt-in — a key only has them if you ask for them when you
create it:
| Scope | Grants |
|---|---|
signals:read | The signal history at /v1/signals. |
webhook_endpoints:write | Creating, reading, updating and deleting webhook endpoints — the only write access in the API. |
A request without the scope it needs returns 403 insufficient_scope.
Pagination
List endpoints return a list envelope:
{
"object": "list",
"data": [],
"has_more": false,
"url": "/v1/customers"
}Most lists are paginated — customers, bookings, event participants, waivers, orders, transactions and
signals. On those, pass limit (1–100, default 25) to set the page size. When has_more is true, request
the next page with starting_after set to the id of the last item you received.
Spaces, coaches and webhook endpoints are not paginated: they return the full list in one response and
take no limit or starting_after. The schedule is not paginated either — it returns every item in the
date window you request. Each endpoint's parameters are in the
API Reference.
Lists return active records by default. Where an endpoint supports it, add ?include=archived to include
archived records. Retrieving a single record by id always works, archived or not.
Conventions
- Field names are
snake_case. - Money is an integer amount in the smallest currency unit (cents), next to a
currencycode. - Times are ISO 8601. Schedule times (when a booking or event starts and ends) carry the club's UTC offset,
for example
2026-06-07T18:00:00-04:00. Record timestamps such ascreated_atandupdated_atare in UTC. The club's timezone is onGET /v1/club. customer_ididentifies a customer of the club and is stable across every endpoint.
Errors
Errors use one envelope, with the HTTP status on the response:
{
"error": {
"type": "invalid_request_error",
"code": "parameter_invalid",
"message": "limit must be an integer between 1 and 100.",
"param": "limit",
"request_id": "req_…"
}
}| Status | type | Meaning |
|---|---|---|
| 400 | invalid_request_error | A parameter is missing or invalid. param names it. |
| 401 | authentication_error | The key is missing, malformed, revoked or expired. |
| 403 | permission_error | The key does not have the scope this endpoint needs. |
| 403 | feature_not_enabled | API access is not enabled for this club. |
| 404 | not_found_error | The resource does not exist in your club. |
| 429 | rate_limit_error | Too many requests. Retry with backoff. |
| 500 | api_error | Something went wrong on our side. |
Every response carries an X-Request-Id header. Include it when you contact support.
Webhooks
Register an HTTPS endpoint with POST /v1/webhook_endpoints
to receive signals — for example, when a membership changes. If your endpoint misses a delivery, read the
same signals back from GET /v1/signals.
Each delivery is a POST with the signal as its JSON body. data.object is the same object the matching
/v1 endpoint returns. Verify the svix-signature header with your endpoint's secret before you trust a
payload. Every signal type, with its payload and required scope, is in
Signal types:
| Resource | Signal types | Scope |
|---|---|---|
| Customer | customer.created, customer.updated, customer.archived | customers:read |
| Subscription | subscription.created, subscription.updated, subscription.renewed, subscription.past_due, subscription.canceled, subscription.expired | customers:read |
| Booking | booking.created, booking.updated, booking.canceled | bookings:read |
| Event | event.created, event.updated | events:read |
| Participant | participant.joined, participant.left, participant.checked_in | events:read |
Support
Questions or feedback: support@getopencourt.com.