OpenCourt Developers

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/v1

A 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:

ScopeGrants
signals:readThe signal history at /v1/signals.
webhook_endpoints:writeCreating, 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 currency code.
  • 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 as created_at and updated_at are in UTC. The club's timezone is on GET /v1/club.
  • customer_id identifies 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_…"
  }
}
StatustypeMeaning
400invalid_request_errorA parameter is missing or invalid. param names it.
401authentication_errorThe key is missing, malformed, revoked or expired.
403permission_errorThe key does not have the scope this endpoint needs.
403feature_not_enabledAPI access is not enabled for this club.
404not_found_errorThe resource does not exist in your club.
429rate_limit_errorToo many requests. Retry with backoff.
500api_errorSomething 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:

ResourceSignal typesScope
Customercustomer.created, customer.updated, customer.archivedcustomers:read
Subscriptionsubscription.created, subscription.updated, subscription.renewed, subscription.past_due, subscription.canceled, subscription.expiredcustomers:read
Bookingbooking.created, booking.updated, booking.canceledbookings:read
Eventevent.created, event.updatedevents:read
Participantparticipant.joined, participant.left, participant.checked_inevents:read

Support

Questions or feedback: support@getopencourt.com.

On this page