{"activeVersionTag":"latest","latestAvailableVersionTag":"latest","collection":{"info":{"_postman_id":"dc6108a3-3a5d-4f19-9e8c-ebf35d48af76","name":"Documentation - Lightspeed Golf Partner API (V2)","description":"# Lightspeed Golf Partner API\n\nThis is the integration API for Lightspeed Golf (formerly Chronogolf). It lets approved partners read a club's tee sheet, pricing, and customers, and book, modify, check in, and pay for rounds programmatically.\n\nThese docs are written for the developer building the integration. They favor concrete, copy-pasteable examples and call out the mistakes that actually trip people up (see the \"Gotchas & Troubleshooting\" section).\n\n## Two versions, and which to use\n\n|  | V1 | V2 |\n| --- | --- | --- |\n| Base path | `/partner_api/v1/...` | `/partner_api/v2/...` |\n| Status | **Deprecated.** Existing integrations only; will be retired | **Use this.** Where all features land |\n| IDs | Integer Rails ids (`85169`) | UUIDs (`eaf0448b-7789-460a-a75d-47c66bac39fa`) |\n| Format | Flat JSON | JSON:API (`data` / `included` / `meta`) |\n| Errors | `{error:{message}, errors:[{type,message}]}` | `{errors:[{status,code,detail}]}` |\n| Pagination | `?page=&per_page=` | `?page[number]=&page[size]=` |\n\n**New integrations must use V2.** V1 is deprecated: it still answers for existing integrators, but it gets no new endpoints, fields, or fixes, and it will be retired. The same OAuth token works for both versions. V1 and V2 ids are **not** interchangeable: a V1 integer id will 404 on a V2 endpoint and vice-versa.\n\nEverything in this folder is V2. the \"V1 Reference (Deprecated)\" section exists only to help existing V1 integrators migrate; it is not a guide for new development.\n\n## Base URLs\n\n| Environment | Host |\n| --- | --- |\n| Production | `https://partner-api.chronogolf.com` |\n| Staging | `https://partner-staging.chronogolf.com` |\n\nTwo things to know:\n\n- Use these hosts for both the OAuth token endpoint and all API calls. Your OAuth application is bound to the environment it was issued for, production credentials only work against production, staging only against staging.\n    \n- **Staging is a shared, disposable environment.** Its data can be reset or wiped without notice, so don't rely on a staging record persisting. Staging and production credentials are separate (you generally need both).\n    \n\nEvery example in these docs uses the staging host so you can run them as-is with staging credentials.\n\n## 60-second quickstart (V2)\n\n``` bash\n# 1. Exchange your refresh token for an access token (valid 2 hours).\n#    The response gives you a NEW refresh token. Store it. The old one is now dead. See authentication.md.\ncurl -s -X POST \"https://partner-staging.chronogolf.com/oauth/token\" \\\n  -d \"grant_type=refresh_token\" \\\n  -d \"refresh_token=YOUR_REFRESH_TOKEN\" \\\n  -d \"client_id=YOUR_CLIENT_ID\" \\\n  -d \"client_secret=YOUR_CLIENT_SECRET\"\n# 2. Find the organizations (clubs) your integrator is connected to.\ncurl -s \"https://partner-staging.chronogolf.com/partner_api/v2/organizations\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Accept: application/vnd.api+json\"\n# 3. Read a course's tee sheet for a date, with prices for a player type.\n#    Note the [] on player_types. This matters. See gotchas.md.\ncurl -gs \"https://partner-staging.chronogolf.com/partner_api/v2/organizations/ORG_UUID/teetimes?filter[date]=2026-05-29&filter[course]=COURSE_UUID&custom_params[player_types][]=PLAYER_TYPE_UUID&custom_params[with_pricing]=true\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Accept: application/vnd.api+json\"\n\n ```\n\nBooking a round is a 3-step flow, not one call. See the \"Reservations & Booking\" section.\n\n## How the data model is shaped\n\n```\nIntegrator (your credentials)\n  └─ Integrator::Connection  (one per club or management company you're granted)\n       └─ Organization\n            ├─ Club              (a golf facility)\n            │    ├─ Course       (a playable course; a club can have several)\n            │    ├─ AffiliationType / \"player_type\"  (Member, Public, etc.)\n            │    ├─ Product       (green fees, carts, kits, extras)\n            │    ├─ Teetime       (a bookable slot on a course)\n            │    ├─ Customership  (a golfer's membership at this club)\n            │    └─ Reservation   (a booking) → Round (one per player)\n            └─ ManagementCompany  (an MCO that owns several clubs)\n\n ```\n\n### Relationships in plain terms\n\nThe tree shows the hierarchy; here's what the important links actually mean and how many of each to expect.\n\n**Organization ↔ customer (this one trips people up).** A \"customer\" in this API is a **Customership**, which is the link between a _golfer_ (a User) and _one club_. So:\n\n- A customer always belongs to **exactly one organization** (club). There's no such thing as a club-less customer.\n    \n- One club has **many** customerships; that's its golfer base.\n    \n- The same person who plays at two of your clubs has **two separate customerships** (two customer UUIDs), one per club, sharing a single underlying User. Their `email`/name match, but each club's record is independent, with its own player types, accounts, and tags.\n    \n- That's why `GET .../customers` is scoped to the organization in the URL, and why a management-company URL spans the customerships of all its clubs. See the \"Customers\" section.\n    \n\n**Club → course → tee time.** A club has many **courses**; each course has many **tee times** (the bookable slots). A tee time belongs to exactly one course, and a course to exactly one club. Tee-sheet reads are always per-course.\n\n**Reservation → rounds (one round per player).** A **reservation** is a single booking on one tee time. It has **1 to 4 rounds**, one per golfer in the group. A round is one player's spot. Each round carries:\n\n- a **player type** (the rate/affiliation it's booked at),\n    \n- either a **customership** (an account golfer) **or** a **guest** (just a name, no account),\n    \n- and its charges as line items: a **green fee**, optionally a **cart**, plus any **kits** and **extras**.\n    \n\nSo \"does this round have a cart?\" comes down to whether the round has a cart line: in the API the round's `rates.cart_fee` is a number if it does and `null` if it doesn't (carts are also summarized at the reservation level as `cart_count`). Same idea for kits: if a green fee is sold inside a kit, it shows under `kit_fee` and the standalone `green_fee`/`cart_fee` go `null`. See the \"Reservations & Booking\" section (\"Round shape and `kit_fee`\").\n\n**Linked reservations.** When one booking can't fit in a single tee time (a big group, or consecutive slots booked together), it becomes several reservations tied to an origin reservation and exposed as `linked_reservations`. Cancelling a reservation also cancels its linked ones.\n\n**Player types are per-organization, sometimes inherited.** A player type (affiliation type) belongs to an organization. A club's player types include its own **plus** any inherited from its management company. A golfer's customership can hold several affiliations; the one that applies at booking sets the rate.\n\n**Products and price overrides.** A product (green fee, cart, kit, extra) belongs to a club and is offered on one or more courses. A price override ties together a product + course + player type over a time window, so all three must belong to the same club. See the \"Club Setup & Pricing\" section.\n\n**Tour operator (optional).** A reservation can be attributed to a tour operator. It's the one relationship whose id is an integer, not a UUID.\n\n### Scoping\n\nYour integrator is connected to one or more organizations. **Every list endpoint is automatically scoped to your connected clubs**, so you only ever see your own data. If `/organizations` returns one club, then `page[number]=2` being empty is correct, not a bug.\n\nAn organization can be a **Club** or a **ManagementCompany (MCO)**. The URL `/v2/organizations/:organization_id/...` accepts either. Some endpoints behave differently for an MCO (courses and tee times return an empty list; customers span all clubs under the MCO). See the \"Conventions (read this first)\" section (\"Organizations: Club vs Management Company\").\n\n## Understanding IDs and UUIDs\n\nAlmost every V2 identifier is a **UUID**: a 36-character string like `a0baaa6a-7a74-44ef-bbd3-d689729c5a67` (32 hex digits in `8-4-4-4-12` groups). We use UUIDs because they're stable, not guessable or enumerable, and don't collide across clubs. V1 used plain integer ids (`1234`) instead; the two are **not interchangeable**.\n\nA few things to keep straight:\n\n- **Every resource has its own UUID.** A reservation, each of its rounds, the tee time, the customer, the course, are all separate UUIDs. Passing a round's UUID to a reservation endpoint (or vice versa) returns 404. Track which is which.\n    \n- **In JSON:API, ids show up in two places.** The resource's own id is `data.id`. A link to another resource is `data.relationships..data.id`. Both are UUIDs (with the exceptions below).\n    \n- **Customership UUID ≠ User UUID.** A \"customer\" in this API is a _customership_ (one golfer at one club), and its UUID is what the customer endpoints use. The same person at two clubs has two customership UUIDs but one underlying User UUID. The User UUID is only exposed (as `user_uuid`) if your connection is specifically granted it. See the \"Customers\" section (\"What each customer field means\").\n    \n- **External / retail IDs are different again.** A golfer's id in a connected POS (their \"retail ID\") is not a Lightspeed UUID; it's exposed separately in the customer's `integrators` array and `house_account_id`. See the \"Customers\" section (\"Retail and external IDs (reconciling a golfer with your system or the POS)\").\n    \n- **Two non-UUID exceptions:** the `tour_operator` relationship id is an **integer**, and `booking_reference` (e.g. `9M0B-5S0N`) is a human-friendly label, **not** an id you can look a reservation up by (it's filter-only).\n    \n\n## ID glossary\n\nA single booking touches several different identifiers. Mixing them up is the most common source of 404s.\n\n| Identifier | Example | Where it comes from | Where it's used |\n| --- | --- | --- | --- |\n| Organization UUID | `a0baaa6a-7a74-44ef-bbd3-d689729c5a67` | `GET /organizations` | The `:organization_id` in every V2 URL |\n| Course UUID | `5c9b3b32-0044-4b94-aa95-1b7fc7a202d9` | `GET .../courses` | `filter[course]`, product/override relationships |\n| Player type UUID | `aaa2e85e-5e4d-436a-a647-ac8c38b9d9a2` | `GET .../player_types` | `custom_params[player_types][]`, round relationships |\n| Teetime UUID | `f7d5a2ea-7744-4cce-bf30-3a2ffad84963` | `GET .../teetimes` | `reservation_request` teetime relationship |\n| Reservation request id | `a1c3994c-a291-454d-b38e-79e32288ed4b` | `POST .../reservation_requests` | steps 2 and 3 of the booking flow |\n| Reservation UUID | `eaf0448b-7789-460a-a75d-47c66bac39fa` | `POST .../reservations` / index | `GET/PUT/DELETE .../reservations/:id` |\n| Round UUID | `bce4c27e-6a77-4790-bb60-d1f5dfdecae0` | inside a reservation's `rounds` | check-ins, payments, round edits |\n| Booking reference | `9M0B-5S0N` | reservation attribute | `filter[booking_reference]` only. Not a primary key |\n| Customership UUID | `544f27c7-4969-4e6c-a178-54c3320b2126` | `GET .../customers` | customer endpoints, round relationships |\n\nThe booking reference is human-friendly and shows up in the club's UI, but you cannot `GET /reservations/9M0B-5S0N`. Use the UUID, or filter by `filter[booking_reference]`.\n\n## Endpoint reference\n\n- the \"Authentication\" section — OAuth, the single-use refresh token, token lifetime\n    \n- the \"Conventions (read this first)\" section — content type, routing, pagination, filters, includes, errors, scoping\n    \n- the \"Tee Times\" section — reading the tee sheet and prices\n    \n- the \"Reservations & Booking\" section — the 3-step booking flow, updates, cancels, rounds, check-in, payment\n    \n- the \"Customers\" section — reading and pushing golfer records\n    \n- the \"Club Setup & Pricing\" section — club setup and pricing: organizations, courses, player types, products, price overrides\n    \n- the \"Reports\" section — aging report\n    \n- the \"Common Errors Reference\" section — every error message the API returns, and why it happens\n    \n- the \"Gotchas & Troubleshooting\" section — the consolidated list of things that bite people\n    \n- the \"V1 Reference (Deprecated)\" section — the frozen V1 surface\n    \n\n## Support and provisioning\n\nThere's no self-service developer portal yet. Credentials are provisioned by hand.\n\n- To get credentials (staging or production), or to add a club to an existing integration, email [<b>golf.api@lightspeedhq.com</b>](https://mailto:golf.api@lightspeedhq.com).\n    \n- Staging and production credentials are **separate**. You usually need both.\n    \n- When we connect a new club to your integrator, it can take **up to 5 minutes** to appear in `/organizations` (the connection list is cached). See the \"Gotchas & Troubleshooting\" section.\n    \n\nThe documented rate limit is 200 requests per minute. There are no webhooks today; the recommended pattern for staying in sync is polling with `filter[updated_since]`. See the \"Conventions (read this first)\" section (\"Polling and rate limits\").","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","isPublicCollection":false,"owner":"21108","team":3056,"collectionId":"dc6108a3-3a5d-4f19-9e8c-ebf35d48af76","publishedId":"2sBXwnsrwx","public":true,"publicUrl":"https://partner-api.docs.chronogolf.com","privateUrl":"https://go.postman.co/documentation/21108-dc6108a3-3a5d-4f19-9e8c-ebf35d48af76","customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"documentationLayout":"classic-double-column","customisation":{"metaTags":[{"name":"description","value":""},{"name":"title","value":""}],"appearance":{"default":"light","themes":[{"name":"dark","logo":null,"colors":{"top-bar":"212121","right-sidebar":"303030","highlight":"FF6C37"}},{"name":"light","logo":null,"colors":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"}}]}},"version":"8.11.5","publishDate":"2026-05-29T18:50:29.000Z","activeVersionTag":"latest","documentationTheme":"light","metaTags":{"title":"","description":""},"logos":{"logoLight":null,"logoDark":null}},"statusCode":200},"environments":[],"user":{"authenticated":false,"permissions":{"publish":false}},"run":{"button":{"js":"https://run.pstmn.io/button.js","css":"https://run.pstmn.io/button.css"}},"web":"https://www.getpostman.com/","team":{"logo":"https://res.cloudinary.com/postman/image/upload/t_team_logo_pubdoc/v1/team/954743c403658dc8f236d8051122de3718ef1a6e0cecaeac70e1477108725394","favicon":"https://res.cloudinary.com/postman/image/upload/v1594915796/team/lvtddztgavggskdt5d35.ico"},"isEnvFetchError":false,"languages":"[{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"HttpClient\"},{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"RestSharp\"},{\"key\":\"curl\",\"label\":\"cURL\",\"variant\":\"cURL\"},{\"key\":\"dart\",\"label\":\"Dart\",\"variant\":\"http\"},{\"key\":\"go\",\"label\":\"Go\",\"variant\":\"Native\"},{\"key\":\"http\",\"label\":\"HTTP\",\"variant\":\"HTTP\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"OkHttp\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"Unirest\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"Fetch\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"jQuery\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"XHR\"},{\"key\":\"c\",\"label\":\"C\",\"variant\":\"libcurl\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Axios\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Native\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Request\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Unirest\"},{\"key\":\"objective-c\",\"label\":\"Objective-C\",\"variant\":\"NSURLSession\"},{\"key\":\"ocaml\",\"label\":\"OCaml\",\"variant\":\"Cohttp\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"cURL\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"Guzzle\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"HTTP_Request2\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"pecl_http\"},{\"key\":\"powershell\",\"label\":\"PowerShell\",\"variant\":\"RestMethod\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"http.client\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"Requests\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"httr\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"RCurl\"},{\"key\":\"ruby\",\"label\":\"Ruby\",\"variant\":\"Net::HTTP\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"Httpie\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"wget\"},{\"key\":\"swift\",\"label\":\"Swift\",\"variant\":\"URLSession\"}]","languageSettings":[{"key":"csharp","label":"C#","variant":"HttpClient"},{"key":"csharp","label":"C#","variant":"RestSharp"},{"key":"curl","label":"cURL","variant":"cURL"},{"key":"dart","label":"Dart","variant":"http"},{"key":"go","label":"Go","variant":"Native"},{"key":"http","label":"HTTP","variant":"HTTP"},{"key":"java","label":"Java","variant":"OkHttp"},{"key":"java","label":"Java","variant":"Unirest"},{"key":"javascript","label":"JavaScript","variant":"Fetch"},{"key":"javascript","label":"JavaScript","variant":"jQuery"},{"key":"javascript","label":"JavaScript","variant":"XHR"},{"key":"c","label":"C","variant":"libcurl"},{"key":"nodejs","label":"NodeJs","variant":"Axios"},{"key":"nodejs","label":"NodeJs","variant":"Native"},{"key":"nodejs","label":"NodeJs","variant":"Request"},{"key":"nodejs","label":"NodeJs","variant":"Unirest"},{"key":"objective-c","label":"Objective-C","variant":"NSURLSession"},{"key":"ocaml","label":"OCaml","variant":"Cohttp"},{"key":"php","label":"PHP","variant":"cURL"},{"key":"php","label":"PHP","variant":"Guzzle"},{"key":"php","label":"PHP","variant":"HTTP_Request2"},{"key":"php","label":"PHP","variant":"pecl_http"},{"key":"powershell","label":"PowerShell","variant":"RestMethod"},{"key":"python","label":"Python","variant":"http.client"},{"key":"python","label":"Python","variant":"Requests"},{"key":"r","label":"R","variant":"httr"},{"key":"r","label":"R","variant":"RCurl"},{"key":"ruby","label":"Ruby","variant":"Net::HTTP"},{"key":"shell","label":"Shell","variant":"Httpie"},{"key":"shell","label":"Shell","variant":"wget"},{"key":"swift","label":"Swift","variant":"URLSession"}],"languageOptions":[{"label":"C# - HttpClient","value":"csharp - HttpClient - C#"},{"label":"C# - RestSharp","value":"csharp - RestSharp - C#"},{"label":"cURL - cURL","value":"curl - cURL - cURL"},{"label":"Dart - http","value":"dart - http - Dart"},{"label":"Go - Native","value":"go - Native - Go"},{"label":"HTTP - HTTP","value":"http - HTTP - HTTP"},{"label":"Java - OkHttp","value":"java - OkHttp - Java"},{"label":"Java - Unirest","value":"java - Unirest - Java"},{"label":"JavaScript - Fetch","value":"javascript - Fetch - JavaScript"},{"label":"JavaScript - jQuery","value":"javascript - jQuery - JavaScript"},{"label":"JavaScript - XHR","value":"javascript - XHR - JavaScript"},{"label":"C - libcurl","value":"c - libcurl - C"},{"label":"NodeJs - Axios","value":"nodejs - Axios - NodeJs"},{"label":"NodeJs - Native","value":"nodejs - Native - NodeJs"},{"label":"NodeJs - Request","value":"nodejs - Request - NodeJs"},{"label":"NodeJs - Unirest","value":"nodejs - Unirest - NodeJs"},{"label":"Objective-C - NSURLSession","value":"objective-c - NSURLSession - Objective-C"},{"label":"OCaml - Cohttp","value":"ocaml - Cohttp - OCaml"},{"label":"PHP - cURL","value":"php - cURL - PHP"},{"label":"PHP - Guzzle","value":"php - Guzzle - PHP"},{"label":"PHP - HTTP_Request2","value":"php - HTTP_Request2 - PHP"},{"label":"PHP - pecl_http","value":"php - pecl_http - PHP"},{"label":"PowerShell - RestMethod","value":"powershell - RestMethod - PowerShell"},{"label":"Python - http.client","value":"python - http.client - Python"},{"label":"Python - Requests","value":"python - Requests - Python"},{"label":"R - httr","value":"r - httr - R"},{"label":"R - RCurl","value":"r - RCurl - R"},{"label":"Ruby - Net::HTTP","value":"ruby - Net::HTTP - Ruby"},{"label":"Shell - Httpie","value":"shell - Httpie - Shell"},{"label":"Shell - wget","value":"shell - wget - Shell"},{"label":"Swift - URLSession","value":"swift - URLSession - Swift"}],"layoutOptions":[{"value":"classic-single-column","label":"Single Column"},{"value":"classic-double-column","label":"Double Column"}],"versionOptions":[],"environmentOptions":[{"value":"0","label":"No Environment"}],"canonicalUrl":"https://partner-api.docs.chronogolf.com/view/metadata/2sBXwnsrwx"}