# OpenCourt Help Center # For facility operators Everything for the people who run the club, organized the way the admin panel is. Start with the collection that matches what you are setting up, or search for the screen you are on. * [Community](/help/facility-operators/community/community-groups-and-chats) — Groups and chats that keep players connected. * [Court Bookings](/help/facility-operators/court-bookings) — The schedule itself, reservations, blocking off time, dependent spaces, resources. * [Events & Programs](/help/facility-operators/events-and-programs) — One-off and recurring events, leagues, divisions, tags, waitlists, roster communication. * [Pricing & Discounts](/help/facility-operators/pricing-and-discounts) — Pricing models, upsells, promo codes, refunds. * [Skill Rating](/help/facility-operators/skill-ratings/skill-rating-create-balanced-and-competitive-games) — Ratings, DUPR and WPR, and events gated by rating. * [Memberships and Rule Sets](/help/facility-operators/memberships-and-rule-sets/memberships-and-rule-sets-overview) — Membership plans, pricing, rule sets, family and group memberships. * [Customers & Families](/help/facility-operators/customers-and-families/adding-a-new-customer) — Adding customers, selling memberships, families, freezes, upgrades, club credit. * [Coach Booking](/help/facility-operators/coaches/coach-booking-overview) — Coach profiles, lessons, court restrictions, the coach schedule. * [Booking & Guest Passes](/help/facility-operators/booking-and-guest-passes/what-are-booking-passes) — Pass templates, allocation rules, guest passes, redemption. * [Waivers](/help/facility-operators/waivers/signing-a-waiver-at-the-front-desk) — Signing online or at the front desk, checking who has signed. * [POS & Products](/help/facility-operators/point-of-sale/purchasing-pos-terminal) — Products, categories, and the POS terminal. * [Ad and Conversion Tracking](/help/facility-operators/ad-and-conversion-tracking) — Meta, Google Analytics 4, and Google Ads conversion tracking. * [Access Controls & Smart Locks](/help/facility-operators/access-controls) — Smart locks, door codes, in-app unlock, and troubleshooting. * [BayControl](/help/facility-operators/bay-control) — Locking a golf simulator bay when nobody has booked it, and opening it for the customer who did. * [Check-In & QR Scanner](/help/facility-operators/check-in) — The front-desk QR scanner and self check-in. * [Website Integrations](/help/facility-operators/website-and-integrations/embedding-opencourt-into-your-website) — Embedding OpenCourt in your website and the lobby TV schedule. * [Emails & Notifications](/help/facility-operators/emails-and-notifications) — Your sender name, From address, reply-to, and sending from your own domain. * [Payment Processing & Payouts](/help/facility-operators/payments-and-payouts/opencourt-stripe-integration) — Stripe, settlement, payouts, and "incomplete" payments. * [Reports](/help/facility-operators/reports/understanding-the-revenue-report) — Revenue and itemized reports, bookkeeping. * [Branded App](/help/facility-operators/branded-app/overview-how-your-branded-app-is-published) — Your own white-label app on the App Store and Google Play. Still stuck? Email [support@getopencourt.com](mailto:support@getopencourt.com) and include your club's name. # For players Guides for customers of a club that runs on OpenCourt. If your question is about a club's own rules or prices, the club is the right place to ask; this section covers how the app works. * **Getting Started** — Your account and the app. *(coming soon)* * **Booking a Court** — Booking and managing your court time. *(coming soon)* * **Events & Programs** — Joining events and programs. *(coming soon)* * **Your Membership** — Your membership and what it includes. *(coming soon)* * [Your Family](/help/players/customers-and-families) — Adding family members to your membership. * **Passes & Guests** — Your passes and bringing guests. *(coming soon)* * **Booking a Coach** — Booking a lesson. *(coming soon)* * **Waivers** — Signing your club's waiver. *(coming soon)* * [Access Controls](/help/players/access-controls/unlock-a-door-from-the-app) — Getting through the door with a code or the app. * [Simulator Bays](/help/players/simulator-bays) — Unlocking a golf simulator bay you booked. * [Checking In](/help/players/check-in) — Your check-in QR code and self check-in. * **Your Skill Rating** — Your rating and what it does. *(coming soon)* * **Community** — Groups and chats. *(coming soon)* Still stuck? Contact your club, or email [support@getopencourt.com](mailto:support@getopencourt.com). # Ad and conversion tracking in OpenCourt (overview) Ad and conversion tracking reports your real **bookings, memberships, and store sales** back to your advertising platforms, so the money you spend on ads optimizes toward customers who actually pay — not just clicks. It works with **Meta** (Facebook and Instagram ads), **Google Analytics 4**, and **Google Ads**. **Ad and Conversion Tracking is in public beta.** To turn it on for your club, email **[support@getopencourt.com](mailto:support@getopencourt.com)** or your OpenCourt customer success manager. Why it's worth setting up [#why-its-worth-setting-up] * **Optimize ads toward revenue.** Meta and Google can only send you good customers if they know which ad clicks turned into real purchases. This feature tells them. * **It doesn't get erased by ad blockers or iPhone privacy settings.** OpenCourt reports your sales **from its own servers** (Meta's Conversions API), not only from a browser tag — so the data keeps flowing where browser-only pixels go dark. See [What OpenCourt sends — and what it never sends](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked). * **See which "type" of sale each ad drove** — a new membership vs a court booking vs a store order — so you can optimize and report on each separately. See [Understand your conversion data](/help/facility-operators/ad-and-conversion-tracking/understand-your-conversion-data). Two ways to set it up [#two-ways-to-set-it-up] **Do it yourself.** Follow the setup guide for each platform you use: * [Set up Meta conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-meta-conversion-tracking) *(\~15 min)* * [Set up Google Analytics 4](/help/facility-operators/ad-and-conversion-tracking/set-up-ga4-conversion-tracking) *(\~5 min)* * [Set up Google Ads](/help/facility-operators/ad-and-conversion-tracking/set-up-google-ads-conversion-tracking) *(\~15 min)* **Hand it to your marketing agency.** If an agency runs your ads, you can forward this collection to them — they'll recognize every step. Two things to know: * Your agency needs access to your club's **Meta Business account** and/or **Google account** (not to OpenCourt). * One person still needs OpenCourt **admin** access to paste the IDs/tokens into **Settings → Ad and Conversion Tracking** and accept the data-sharing disclosure — that part takes two minutes and can't be done from outside OpenCourt. Is my customers' data safe? [#is-my-customers-data-safe] Short version: OpenCourt only sends what ad platforms need to match a sale to an ad — **never any card or payment details** — and it **hashes** identifying fields like email and phone before they leave. You remain responsible for your own privacy disclosures (privacy policy, cookie/consent banner). The full, plain-English breakdown is in [What OpenCourt sends — and what it never sends](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked). The rest of this section [#the-rest-of-this-section] | Guide | What it covers | | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | [Set up Meta conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-meta-conversion-tracking) | Create your Meta dataset, get the Dataset ID + Conversions API token, connect it in OpenCourt. | | [Set up Google Analytics 4](/help/facility-operators/ad-and-conversion-tracking/set-up-ga4-conversion-tracking) | Add your GA4 Measurement ID to track page views and purchases in Analytics. | | [Set up Google Ads](/help/facility-operators/ad-and-conversion-tracking/set-up-google-ads-conversion-tracking) | Connect Google Ads and map each order type to a conversion action. | | [Send a test event](/help/facility-operators/ad-and-conversion-tracking/send-a-test-event) | Prove the connection works before real money depends on it. | | [What OpenCourt sends — and what it never sends](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked) | The events, the data, hashing, and your privacy responsibilities. | | [Understand your conversion data](/help/facility-operators/ad-and-conversion-tracking/understand-your-conversion-data) | How your sales appear in Meta, and how to split them by type. | | [Monitor and troubleshoot](/help/facility-operators/ad-and-conversion-tracking/monitor-and-troubleshoot) | Your in-OpenCourt health dashboard and fixes for common problems. | {/* Maintainers: overview for the Ad and Conversion Tracking collection (PR #2935, az/meta-pixel). Feature is flag-gated (public beta) — the beta note above is the single enablement instruction; keep it verbatim across the setup articles. GA4 + Google Ads are documented as released per product decision, though both are still behind the superadmin gate at time of writing — revisit the beta note when the gate is lifted. */} # Monitor and troubleshoot Everything you need to keep conversion tracking healthy lives inside OpenCourt — no log-diving in Meta or Google required. **Ad and Conversion Tracking is in public beta.** To turn it on for your club, email **[support@getopencourt.com](mailto:support@getopencourt.com)** or your OpenCourt customer success manager. Your dashboard in OpenCourt [#your-dashboard-in-opencourt] Open **Admin → Settings → Ad and Conversion Tracking**. Each connected provider has a **health card** showing: * **Sent / Failed (7d)** — how many events went out and how many failed over the last 7 days. * **Last sent** — when the most recent event went out. * A red **"Sending is failing"** banner when recent sends are failing — this is your one signal that something needs attention. No banner, and Sent is climbing? You're healthy. **Nothing tells you when sending breaks — you have to look.** There is no alert email today. The banner only appears to someone who opens this page. If you are spending on ads, check it weekly. Two limits make an old problem easy to miss: the health card counts the **last 7 days** only, and the events table holds the most recent **200** events. A failure that started a few weeks ago may have already scrolled out of both. Below that, the **Recent conversion events** table lists every event OpenCourt captured. **Click any row** to open a detail slideout with the **exact request and response payload** and a **Copy** button. **Agencies:** the slideout payload is the ground truth of what was sent and what the platform answered. Copy it straight into a support ticket — it answers "what exactly did you send?" without any back-and-forth. OpenCourt's Conversion tracking health card and the Recent conversion events table Status glossary [#status-glossary] | Status | Meaning | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Sent** | The event was delivered and the platform accepted it. | | **Failed** | Delivery was attempted but the platform rejected it — see the slideout for the response. On a `Refund` row it can also mean the refund is still on its way, or could not be sent (for example, tracking was turned off or the Meta token was removed). The slideout's note says which. | | **Not sending yet** | The event was **captured but not sent yet**. This is **normal, not an error** — it's expected until your Conversions API token is live. Once your token is saved and working, new events send for real. | Symptom → fix [#symptom--fix] Test events don't appear in Meta [#test-events-dont-appear-in-meta] Meta's Test Events tab is a **live listener** — open it first, keep it open, then send, and allow up to a minute. And the test code in OpenCourt must match the one in Meta's tab **exactly** — a wrong code still reports "Sent" but lands the events in a bucket you can't see. Full walkthrough: [Send a test event](/help/facility-operators/ad-and-conversion-tracking/send-a-test-event). The red "Sending is failing" banner is up [#the-red-sending-is-failing-banner-is-up] The banner names the fix for each provider that is failing. The most common ones: * **Meta — your Conversions API token is invalid or expired.** Generate a new token in Meta and paste it into OpenCourt — the steps are in [Set up Meta conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-meta-conversion-tracking). * **Google Ads — turn on enhanced conversions.** In Google Ads, go to **Goals → Conversions → Settings**, accept the customer data terms, and turn on enhanced conversions. Then click **Check setup** on the Google Ads card to confirm. * **Google Ads — reconnect.** OpenCourt lost its permission to upload: the Google account that connected lost access to your Google Ads account, or someone removed OpenCourt's access in that Google account's settings. Click **Reconnect Google Ads** and sign in with a Google account that has access — the steps are in [Set up Google Ads conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-google-ads-conversion-tracking). Sales that failed before you fixed the problem are not sent again automatically. If you want them re-sent, email **[support@getopencourt.com](mailto:support@getopencourt.com)** — the sooner the better. Click any failed row in **Recent conversion events** to see the fix again and the platform's full response. A Custom Conversion stopped counting bookings [#a-custom-conversion-stopped-counting-bookings] Court, bay and lane bookings used to report as `content_category` **`event`**. They now report as **`space`**, and the old `court` value is gone. If you built a Custom Conversion on `content_category` equals `event`, it still counts league, clinic, tournament and open-play registrations — but no longer counts bookings. Add a second Custom Conversion on `content_category` equals `space` to count them again. If you want one conversion covering both, build the rule so it matches `event` **or** `space` — not a rule that requires both, which nothing can satisfy. The recipe is in [Understand your conversion data](/help/facility-operators/ad-and-conversion-tracking/understand-your-conversion-data#recipe-break-out-sale-types-with-custom-conversions). **Rules built on `content_name` break harder.** Bookings and player fees used to report the fee label — "Player Fee", "Court Booking". They now report the real name ("Indoor Bocce Courts", "Women's Beginner League"), so a rule matching `content_name` *contains* `Player Fee` stops matching entirely rather than counting less. Expect a step change in volume around the release date. That is the rename, not a drop in real bookings. Google Ads: bookings stopped uploading [#google-ads-bookings-stopped-uploading] Different symptom, worse outcome. Google Ads credits a sale to the conversion action you mapped against its order type. If your map still has a `court` row, nothing will ever match it — move that resource name to the `space` row in **Settings → Ad and Conversion Tracking → Google Ads**. And make sure the `default` row is filled: an order type with no mapping, and any charge OpenCourt cannot classify, falls back to `default`. With `default` empty those conversions are never uploaded, and no error is shown. A sale is missing [#a-sale-is-missing] Was it rung up by staff at the front desk (an **admin or POS sale**)? Those are **excluded by design** — ad platforms only need the sales your ads could have driven, and a front-desk sale isn't one. See [What OpenCourt sends — and what it never sends](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked). Two events for one membership sale [#two-events-for-one-membership-sale] Expected. A new membership fires **Purchase + Subscribe** — Meta uses one for revenue and one for subscription optimization. See [Understand your conversion data](/help/facility-operators/ad-and-conversion-tracking/understand-your-conversion-data). Value looks lower than the price [#value-looks-lower-than-the-price] Also by design: OpenCourt reports **net cash captured**, not the list price. Discounts, credits, and partial payments all reduce the reported value — so your ad platforms optimize toward real revenue. See [What OpenCourt sends — and what it never sends](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked). Reported revenue is higher than what you actually kept [#reported-revenue-is-higher-than-what-you-actually-kept] **Check Meta for `Refund` events.** Meta keeps the original purchase value after a refund; OpenCourt sends a separate `Refund` event worth the amount returned. Subtract it with a custom metric in Ads Manager. Google Ads and GA4 are never updated for a refund, and a refund to account credit is not sent anywhere — reconcile those against OpenCourt's own reports. See [What OpenCourt sends — and what it never sends](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked). Each refund or lost chargeback gets its own row in the events table: `Refund`, then `Refund #2`, and so on. A row **Skipped** as *already\_reversed* means Meta already had the right total, so nothing more was sent. One skipped as *purchase\_not\_delivered* means the original sale had not reached Meta yet; OpenCourt retries it automatically once the sale arrives. A free booking shows up as a Lead — or not at all [#a-free-booking-shows-up-as-a-lead--or-not-at-all] Expected. A sale that captures **$0** — fully covered by a pass, account credit, or a 100% promo code, or a free event — reports a `Lead` with a value of 0, never a `Purchase`, and only when it is the customer's first order at your club. A member's or returning customer's free booking sends nothing. In Google Ads a Lead is sent only when the **free\_booking** row is mapped. See [Free bookings are reported as a Lead](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked#free-bookings-are-reported-as-a-lead). A sale from the app isn't credited to an ad [#a-sale-from-the-app-isnt-credited-to-an-ad] In-app purchases are reported server-side, so the revenue arrives — but the browser pixel does not run inside the OpenCourt app, so those sales often land without a link to a specific ad. Send your ad traffic to the web booking pages instead. See [Purchases made in the OpenCourt app](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked). {/* Maintainers: written for PR #2935 (az/meta-pixel). "Not sending yet" is the customer-facing label for the pre-token internal state — always use this label, never the internal name. Health card fields (Sent/Failed 7d, Last sent, "Sending is failing" banner) and the detail slideout mirror the in-app observability surface — keep in sync if the UI changes. One screenshot TODO above (health card + events table). */} # Send a test event Send a fake purchase from OpenCourt to Meta and watch it arrive — so you know the connection works **before** real ad money depends on it. *(About 5 minutes.)* **Ad and Conversion Tracking is in public beta.** To turn it on for your club, email **[support@getopencourt.com](mailto:support@getopencourt.com)** or your OpenCourt customer success manager. **What you'll need:** your **Dataset ID** and **Conversions API token** already saved in OpenCourt (from [Set up Meta conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-meta-conversion-tracking)), plus access to your club's **Meta Events Manager**. This test console is **Meta-only**. GA4 verifies through its own **Realtime** and **DebugView** reports, and Google Ads through each conversion action's diagnostics — no OpenCourt test console needed for those. Step 1 — Get your test event code from Meta [#step-1--get-your-test-event-code-from-meta] 1. In **Meta Events Manager**, open your dataset and go to the **Test Events** tab. 2. Select the channel **Website** — the tab shows a `test_event_code` (something like `TEST12345`). 3. Copy it. Meta's Test Events tab with the Website channel selected, showing the test event code 4. In **OpenCourt → Admin → Settings → Ad and Conversion Tracking**, paste the code into the Meta card's **Test event code** field and click **Save**. The test event code only touches **test events** — it never affects your live tracking, so it's safe to leave set after you're done. Step 2 — Send and watch [#step-2--send-and-watch] **Meta's Test Events tab is a live listener, not a log.** Open the Test Events tab in Meta **first** and keep it open, **then** click Send test event in OpenCourt. Events can take up to a minute to appear. The code in OpenCourt must match the code in Meta's Test Events tab **exactly.** A wrong code still reports "Sent," but the events land in a bucket you can't see. 1. **Open Meta's Test Events tab** (Events Manager → your dataset → **Test Events** → channel **Website**) and **keep it open.** 2. **In OpenCourt**, go to the Meta card's **Send a test event** area, pick a scenario from the dropdown, and click **Send test event**. (The **View in Meta Events Manager** link next to the button jumps you straight to the right place in Meta.) 3. **Watch the Test Events tab** — your events should appear within about a minute, listed under your test code. OpenCourt reports honestly after sending — something like: *"Sent 2 events to Meta with test code TEST12345. Meta accepted them — that doesn't mean they're visible yet. Open your Meta Events Manager → Test Events tab and check they appear under that same code."* "Accepted" only means Meta received the request — Meta counts events as received **whether or not the code matched** — so seeing them in the Test Events tab is the actual proof. What each scenario sends [#what-each-scenario-sends] | Scenario | Events fired | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Membership purchase | **Purchase + Subscribe** — you'll see two events; this is correct. See [Understand your conversion data](/help/facility-operators/ad-and-conversion-tracking/understand-your-conversion-data). | | Presale membership | Purchase only — a deposit is not yet a recurring membership | | Pass package | Purchase | | Subscription / package | Purchase + Subscribe | | Product purchase | Purchase | | Space booking (host) | Purchase | | Join a booking (Open Game) | Purchase | | Join an event | Purchase | | League registration | Purchase | | Free booking (Lead) | **Lead** only — value 0, never a Purchase. A real free order sends it only for a new customer. See [Free bookings are reported as a Lead](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked#free-bookings-are-reported-as-a-lead). | | Refund (reverses a sale) | **Refund** only — worth the amount returned (here $10 of a $24 booking), not the sale. It is what OpenCourt sends Meta when a sale is refunded or a chargeback is lost. | Nothing appeared? [#nothing-appeared] Head to [Monitor and troubleshoot](/help/facility-operators/ad-and-conversion-tracking/monitor-and-troubleshoot) — it covers the two usual suspects (tab not open first, code mismatch) and everything else. {/* Maintainers: written for PR #2935 (az/meta-pixel) from the in-app Meta test console (scenario dropdown + Send test event + View in Meta Events Manager deep link; uses the stored Test event code). Scenario→event mapping mirrors the console config — keep in sync if scenarios change. One screenshot TODO above (Meta Test Events tab); consider a second of OpenCourt's Send a test event area when available. */} # Set up Google Analytics 4 conversion tracking Connect your Google Analytics 4 property to OpenCourt so your club's page views, checkouts, and purchases show up in your GA4 reports. *About 5 minutes.* **Ad and Conversion Tracking is in public beta.** To turn it on for your club, email **[support@getopencourt.com](mailto:support@getopencourt.com)** or your OpenCourt customer success manager. Once you save your Measurement ID, OpenCourt automatically fires GA4 `page_view`, `begin_checkout`, and `purchase` events (with items) — and `generate_lead` instead of `purchase` for a new customer's free ($0) booking — from your club's pages using Google's gtag.js. Everything runs in the browser — there's no server key or API setup. What you'll need [#what-youll-need] * A **Google Analytics 4 property** with a web data stream * **OpenCourt admin** access for your club Working with a marketing agency? They can grab the Measurement ID from your GA4 property and hand it to you (or anyone with OpenCourt admin access) to paste into OpenCourt — that's the whole setup. Steps [#steps] 1. **Copy your Measurement ID from Google Analytics.** In GA4, go to **Admin → Data Streams**, open your web stream, and copy the **Measurement ID** — it looks like `G-XXXXXXXXXX`. 2. **Paste it into OpenCourt.** Go to **Admin → Settings → Ad and Conversion Tracking**, find the **Google Analytics 4** card, paste the ID into **Measurement ID**, and save. The first time you turn on any tracking provider, OpenCourt shows a data-sharing disclosure. Review and accept it to continue. Verify it's working [#verify-its-working] Open your club's page in a browser, then check **GA4 → Reports → Realtime** (or DebugView). You should see a `page_view` within a minute or two. Make a test purchase and you should see a `purchase` event too. For a full test walkthrough, see [Send a test event](/help/facility-operators/ad-and-conversion-tracking/send-a-test-event). Related articles [#related-articles] * [What OpenCourt sends](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked) — the exact events and fields * [Monitor and troubleshoot](/help/facility-operators/ad-and-conversion-tracking/monitor-and-troubleshoot) — if events aren't showing up * [Set up Google Ads conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-google-ads-conversion-tracking) — report sales to Google Ads for campaign optimization {/* Maintainers: drafted from PR #2935 product facts (GA4 card = single Measurement ID field, gtag.js browser-side, auto page_view/begin_checkout/purchase). Deliberately kept light on Google's exact UI paths (their menus move); screenshots intentionally omitted for now. Re-confirm the GA4 Admin path if a reader reports it drifted. */} # Set up Google Ads conversion tracking Connect your Google Ads account so OpenCourt reports your real sales — memberships, court bookings, events, store orders — back to Google Ads, letting Google optimize your campaigns toward customers who actually pay. *About 15 minutes.* **Ad and Conversion Tracking is in public beta.** To turn it on for your club, email **[support@getopencourt.com](mailto:support@getopencourt.com)** or your OpenCourt customer success manager. OpenCourt uploads every online sale to Google Ads from our servers. Google then credits the sales it can match to one of your ads — by the Google click id (gclid) when the customer arrived from an ad, and by the customer's hashed email and phone otherwise. You'll connect your account and tell OpenCourt which **conversion action** to credit for each order type. What you'll need [#what-youll-need] * A **Google Ads account** and its **10-digit account ID** (shown in the top corner of Google Ads) * Permission to **create conversion actions** and **change conversion settings** in that account * **OpenCourt admin** access for your club This splits cleanly with a marketing agency: they create the conversion actions in Google Ads and click **Connect Google Ads** to authorize the upload; you (or they, with OpenCourt admin access) paste the resource names into OpenCourt. Steps [#steps] 1. **In Google Ads, create the conversion actions you want to track.** Create at minimum one general conversion action, and optionally one per sale type (memberships, space bookings, events, store). Choose **Import** and track **conversions from clicks**, so the action's source shows as *Import from clicks* — not a website-tag conversion — because OpenCourt uploads them from our servers. 2. **Turn on enhanced conversions.** In Google Ads, go to **Goals → Conversions → Settings**. Accept the **customer data terms**, and turn on **enhanced conversions** (Google may show it as *enhanced conversions for leads*). You don't need to change the method dropdown next to it. Without this step, Google rejects every sale from a customer who didn't click an ad, which is most of them. 3. **Find each conversion action's resource name.** It has the form `customers//conversionActions/`, for example `customers/1234567890/conversionActions/456`. Note it down for each action you created. (If you can't locate the resource name, your marketing agency or Google Ads support can provide it.) 4. **Enter your account IDs in OpenCourt.** Go to **Admin → Settings → Ad and Conversion Tracking** and open the **Google Ads** card. Enter your **Google Ads account ID** — the 10-digit number, with no dashes. If a manager (MCC) account runs your ads, also fill in the **Agency / manager account ID**; otherwise leave it blank. 5. **Click Connect Google Ads.** Sign in with a Google account that has access to your Google Ads account — and to the manager account, if you entered one. Google asks you to let OpenCourt **see, edit, create, import, or delete your customer data in Google Ads**. That is Google's standard wording for the permission its Data Manager API needs, and it is how OpenCourt uploads conversions. OpenCourt uses it only to upload your sales to the conversion actions you map below. 6. **Map conversion actions to order types.** In **Conversion actions by order type**, paste the resource name from step 3 next to each order type you want to track — membership, membership\_renewal, membership\_presale, pass\_package, space, add\_on, event, day\_fee, coach\_lesson, store, gift\_card. Fill the **default** row as the catch-all for any type without its own mapping. Save. The **free\_booking** row is different: it is for a new customer's free ($0) booking, which OpenCourt reports as a `Lead` with a value of 0. Map it to a **separate** conversion action if you want to track a free-first-session offer, or leave it empty to not send free bookings at all. It never falls back to `default`, so a $0 conversion can never land in a revenue action. **Fill in `default`, even if you map every other row.** Any order type you leave blank falls back to `default` — and so does any charge OpenCourt cannot classify. If `default` is empty, those conversions are never uploaded to Google Ads, and nothing warns you. {/* MD028: separates two consecutive alert blockquotes; a bare blank line merges them. */} **Court and bay bookings are `space`, not `court`.** The old `court` row is retired. If you set one up before September 2026, move its resource name to `space` — the `court` row is no longer sent and will never be credited. The first time you turn on any tracking provider, OpenCourt shows a data-sharing disclosure. Review and accept it to continue. OpenCourt sends all of your online sales, including sales from customers who didn't click an ad — Google requires this. Google Ads credits only the sales it can link to an ad, so sales from other channels won't appear as conversions, and that's expected. Verify it's working [#verify-its-working] After you connect and after each save, OpenCourt runs a **setup check** on the Google Ads card. It asks Google to check a test conversion for each conversion action you mapped — nothing is recorded in your Google Ads account. You can run it again at any time with **Check setup**. * **Ready** — Google will accept your conversions. * **Not ready** — the card names the fix for each conversion action, for example "Turn on enhanced conversions and accept the customer data terms". Fix it in Google Ads, then click **Check setup** again. Uploaded conversions can take some time to appear in Google Ads reporting. For each conversion action, Google Ads shows the upload status under **Goals → Conversions → \[your action] → Diagnostics**. Related articles [#related-articles] * [What OpenCourt sends](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked) — the exact events and fields * [Monitor and troubleshoot](/help/facility-operators/ad-and-conversion-tracking/monitor-and-troubleshoot) — if conversions aren't showing up * [Set up GA4 conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-ga4-conversion-tracking) — see the same activity in Google Analytics {/* Maintainers: drafted from PR #2935 product facts (Google Ads card = account ID no-dashes + optional manager/MCC ID + OAuth Connect button + per-order-type conversion action resource names with default fallback; offline click conversions matched by gclid). Kept deliberately light on Google's exact UI paths (their menus move) and screenshots are intentionally omitted for now. The conversion-action resource-name step is the one worth verifying with a real account when convenient. Updated for PR #3292 (OC-3338): uploads moved to the Data Manager API (scope `datamanager`, no developer token); pre-2026-09-24 tokens carry only `adwords` and must reconnect. Do not mention Google's unverified-app screen — Google verified the app on 2026-09-24. Updated again 2026-09-24: added the enhanced-conversions + customer-data-terms step (Pickle Alley's uploads failed without it), 'Import from clicks', the setup check and its Ready / Not ready states; OpenCourt uploads every online sale, not only click-id sales. */} # Set up Meta conversion tracking Connect your club to **Meta** (Facebook and Instagram ads) so every booking, membership, and store sale reports back to your ads. *(About 15 minutes. You do Part 1 in Meta and Part 2 in OpenCourt.)* **Ad and Conversion Tracking is in public beta.** To turn it on for your club, email **[support@getopencourt.com](mailto:support@getopencourt.com)** or your OpenCourt customer success manager. **What you'll need:** admin access to your club's **Meta Business account** (Events Manager) and OpenCourt **admin** access. If a marketing agency runs your Meta ads, forward them this page. They can do **Part 1** on their own — you only need to paste the two values into OpenCourt in **Part 2**. Part 1 — Create your dataset in Meta [#part-1--create-your-dataset-in-meta] You'll end Part 1 with two things to copy: a **Dataset ID** and a **Conversions API access token**. **1. Open Events Manager and click "Connect data".** Meta Events Manager sidebar with Connect data selected **2. Choose "Web" as the data source, then continue.** Connect a new data source dialog with Web selected **3. Name your dataset** (e.g. your club name) and **keep the "Conversions API" option checked**, then create it. Create a new dataset dialog with a name and the Conversions API checkbox **4. Close the "Connect your web data" screen — don't install anything.** Meta offers to install a pixel here (add code to your site, email a developer, or set up with a partner). You don't need any of it — OpenCourt sends the events for you. Click the **X** in the top-right corner (or **Close**) to dismiss the screen. Connect your web data screen offering Set up Meta Pixel or Set up Conversions API **5. Open "Datasets" in the sidebar** and select the dataset you just created. Datasets list in the Events Manager sidebar **6. Go to the dataset's "Settings" tab and copy the "Dataset ID".** It's a 15–16 digit number. Dataset Settings tab showing the Dataset ID field Meta renamed **Pixel ID → Dataset ID** (and **Data sources → Datasets**). They're the same thing — if a guide or your agency says "Pixel ID," they mean this Dataset ID. **7. In the "Conversions API" section, generate an access token.** Choose **"Set up with Dataset Quality API"** — it's Meta's recommended option, takes the same number of clicks, and lets OpenCourt read your Meta-side match quality later. Conversions API section with Set up with Dataset Quality API and Generate access token **8. Copy the access token when Meta shows it.** Meta shows the token **only once**. Copy it now and paste it into OpenCourt in the next step — if you lose it, you'll have to generate a new one. Meta's dialog showing the generated Conversions API token, with a reminder to copy it now (the token itself is hidden here) Part 2 — Connect it in OpenCourt [#part-2--connect-it-in-opencourt] **9. Open OpenCourt → Settings → Ad and Conversion Tracking.** In the **Meta Pixel** card: * Paste your **Dataset ID** into the *Dataset ID* field. * Paste your **Conversions API token** into the *Conversions API token* field. * Leave *Test event code* empty for now — you'll use it in the next guide. **10. Click "Save".** The first time you turn on any provider, OpenCourt shows a short **data-sharing disclosure** — read it and confirm. (What that disclosure covers, in plain English, is in [What OpenCourt sends — and what it never sends](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked).) OpenCourt's Meta Pixel settings card with the Dataset ID filled in and the Conversions API token stored That's it — Meta will start receiving your page views and purchases. Before you rely on it, prove the connection with a quick test. **Next:** [Send a test event](/help/facility-operators/ad-and-conversion-tracking/send-a-test-event) to confirm Meta is receiving your events correctly. {/* Maintainers: Part 1 steps + screenshots mirror _assets/marketing/README.md (captured 2026-08-08, current renamed Meta UI). If Meta renames again, re-shoot and update. The "Set up WITH Dataset Quality API" recommendation is documented with rationale in that README. The token-dialog screenshot is REDACTED — the live token is blacked out; never publish a working token. setup-meta-pixel-install-options*.webp are no longer referenced — kept in _assets for possible later use. */} # Understand your conversion data in Meta OpenCourt reports every online sale to Meta as a single `Purchase` event — the sale *type* lives in the event parameters, not the event name. This page shows you how to read those parameters, break out sale types for optimization and reporting, and set the right expectations when comparing Meta's numbers to OpenCourt's. **Ad and Conversion Tracking is in public beta.** To turn it on for your club, email **[support@getopencourt.com](mailto:support@getopencourt.com)** or your OpenCourt customer success manager. One event name, typed by parameters [#one-event-name-typed-by-parameters] In Meta's event list you'll see `Purchase` for everything — a court booking, a store order, a membership. What kind of sale it was is carried in the parameters: | Parameter | Example value | | -------------------- | --------------------------------------------------------------------------------- | | Event name | `Purchase` | | `value` | `89.00` — net cash captured, after promos/passes/credits | | `content_category` | `"membership"` | | `content_name` | `"Unlimited Monthly"` | | `event_type` | Not sent on a membership. On a league registration this row would read `"League"` | | `content_categories` | `"membership"` — every category in the order, comma-joined | | `is_new_customer` | `true` | **`content_name` is the name of what was bought.** For memberships and store orders it is the plan or product name, as above. For a program registration it is the event's own name ("Women's Beginner League"). Court and bay bookings are a special case: they have no event name of their own, so they are named after your **space category** if you use them ("Indoor Bocce Courts"), otherwise the space itself ("Bay 9"). **A new customer's free booking fires `Lead`, not `Purchase`.** A first order that cost the customer $0 (pass, account credit, 100% promo code, free event) reports a `Lead` with a value of 0 and the same parameters as below. Build a Custom Conversion on `Lead` to optimize a free-first-session campaign; purchase campaigns are not affected. **A new membership fires both `Purchase` and `Subscribe` — this is expected.** Meta doesn't double-count: both events share one event ID, and Meta deduplicates per event name. `Subscribe` is your "new member acquired" signal; renewals fire `Purchase` only. The content_category buckets [#the-content_category-buckets] | Bucket | Meaning | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `membership` | A first-time membership purchase (also fires `Subscribe`) | | `membership_renewal` | A recurring membership renewal payment | | `space` | A court, bay or lane booking — someone reserving your space | | `event` | A registration for something on your schedule: open play, a player fee, a league, clinic, tournament or program. `event_type` says which | | `day_fee` | A day-pass / entry fee | | `add_on` | An equipment rental added to a booking (a ball machine, a paddle) | | `membership_presale` | A deposit taken before the club opens. Kept separate from `membership` so an acquisition campaign can exclude deposits | | `pass_package` | A punch card or lesson bundle bought up front | | `store` | An online store / pro-shop order | | `coach_lesson` | A coaching lesson purchase | | `gift_card` | A gift card purchase | An order that mixes types is filed under the most important one: a membership sale that also books a court is a `membership`, and a booking with a ball-machine rental attached is a `space`. Every individual line still carries its own type inside `contents`. Telling programs apart with event_type [#telling-programs-apart-with-event_type] Anything attached to your schedule carries `event_type`, which is one of `OpenPlay`, `OpenGame`, `League`, `Clinic`, `Tournament`, `Lesson` or `Other`. This is what you filter on to build a leagues-only or clinics-only conversion. `event_type` is **not** limited to the `event` bucket. A `space` booking carries `OpenGame` and a coach lesson carries `Lesson`. If you want bookings only, filter on `content_category` equals `space` — filtering on `event_type` would mix bookings in with open-play registrations. The same list is summarized, alongside every other field OpenCourt sends, in [What OpenCourt sends to Meta and Google](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked). Recipe: break out sale types with Custom Conversions [#recipe-break-out-sale-types-with-custom-conversions] Meta optimizes and reports per event *name*, so to target or report on a specific sale type, create a Custom Conversion filtered on `content_category`: 1. In Meta Events Manager, open **Custom Conversions** and create a new one. 2. Choose your OpenCourt data source and select the `Purchase` event. 3. Add a rule on the `content_category` parameter — for example: * **"New memberships"** — `content_category` equals `membership` * **"Court and bay bookings"** — `content_category` equals `space` * **"League signups"** — `content_category` equals `event` **and** `event_type` equals `League` (swap in `Clinic` or `Tournament` for those). Both halves matter: `event_type` travels with anything on your schedule, so a court booked *for* a league session carries `League` too — the `event` bucket is what narrows it to an actual registration * **"Anything booked on the schedule"** — `content_categories` *contains* `space` **or** `event`. A rule on `content_category` equals both can never match, because one order carries one value * **"Any order that included a membership"** — `content_categories` *contains* `membership`. Use this whenever a cart can mix types; `content_category` only ever names the single most important one * **"One specific program"** — `content_ids` contains the event's ID, which you can copy from the event's URL in your admin panel 4. Name and save it. It's now available as an optimization goal in ad sets and as a column in reporting. Create one Custom Conversion per revenue line you actually buy ads for. A campaign optimized on "New memberships" learns from membership buyers only — renewal payments and store orders won't muddy the signal. Attribution expectations [#attribution-expectations] These are server-side events, matched to ad clicks through hashed identifiers and click IDs — so expect Meta's attributed conversion counts to differ from OpenCourt's revenue reports. Some purchasers won't match to a Meta account, some sales were never ad-driven, and Meta only counts conversions inside its attribution windows. A gap between the two numbers is normal; use OpenCourt as the source of truth for revenue and Meta for *relative* performance between campaigns, ad sets, and creatives. Also remember that admin and front-desk (POS) sales are excluded by design — see [What OpenCourt sends](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked) for the full data policy. Related articles [#related-articles] * [Set up Meta conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-meta-conversion-tracking) * [Send a test event](/help/facility-operators/ad-and-conversion-tracking/send-a-test-event) * [What OpenCourt sends to Meta and Google](/help/facility-operators/ad-and-conversion-tracking/what-gets-tracked) — the full field list and privacy policy * [Monitor and troubleshoot](/help/facility-operators/ad-and-conversion-tracking/monitor-and-troubleshoot) {/* Maintainers: The content_category bucket table here is the canonical "how to use" copy; the privacy-angle summary lives in what-gets-tracked.md — keep both in sync when buckets change. The Purchase+Subscribe dedup callout is verbatim-standardized. The Custom Conversions recipe is kept light on Meta's exact UI path (their menus move); screenshot intentionally omitted for now. */} # What OpenCourt sends to Meta and Google — and what it never sends When you turn on Ad and Conversion Tracking, OpenCourt reports your online sales to the ad platforms you connect — so Meta and Google can see which ads actually bring in paying customers. This page explains exactly what gets sent, and just as importantly, what never does. **Ad and Conversion Tracking is in public beta.** To turn it on for your club, email **[support@getopencourt.com](mailto:support@getopencourt.com)** or your OpenCourt customer success manager. The events OpenCourt reports [#the-events-opencourt-reports] Six things get reported, and only these: * **A page view** — someone visited your club's booking pages (sent from their browser). * **A checkout started** — someone reached the payment step (sent from their browser). * **A purchase** — someone actually paid for something online. This one is sent **from OpenCourt's servers** using Meta's Conversions API, not from the customer's browser — so it still arrives even when ad blockers or iOS privacy settings block browser tracking. Your purchase data is the signal that matters most, and server-side delivery is what keeps it reliable. * **A new membership** — when someone buys their *first* membership, a `Subscribe` event is sent **alongside** the purchase (never instead of it). Renewals, bookings, events, and store sales report a purchase only. So does a **[presale deposit](/help/facility-operators/memberships-and-rule-sets/run-a-membership-presale)**: a deposit is not a recurring membership yet, so it reports `Purchase` only — `Subscribe` fires later, if and when the presale converts. * **A new customer's free booking** — their first online order at your club, when it cost them $0, reports a `Lead` with a value of 0, **never** a `Purchase`. See [Free bookings are reported as a Lead](#free-bookings-are-reported-as-a-lead) below. * **A refund** — sent to Meta only, when you refund a reported sale from OpenCourt (to the card or in person), or lose a chargeback on it. See [Refunds are reported to Meta](#refunds-are-reported-to-meta) below. Google gets the same picture: GA4 receives `page_view`, `begin_checkout`, `purchase`, and `generate_lead` for a free booking. Google Ads receives the purchases as offline click conversions, and free bookings too if you map them (see below). The value reported is what the customer actually paid [#the-value-reported-is-what-the-customer-actually-paid] Each purchase carries the **net cash captured** — the amount after any promo code, pass, or account credit — never the list price. That keeps your ad optimization honest: the platforms learn to find customers based on real revenue, not inflated sticker prices. Front-desk sales are excluded — on purpose [#front-desk-sales-are-excluded--on-purpose] Sales your staff ring up at the front desk (admin and POS sales) are **never sent**. A walk-in who paid at the counter didn't come from an online ad, and mixing that revenue in would teach the ad platforms the wrong lessons about which ads work. Only online sales — the ones an ad could plausibly have driven — are reported. Free bookings are reported as a Lead [#free-bookings-are-reported-as-a-lead] If a customer covers a booking entirely with a pass, account credit, or a 100% promo code, no cash is captured. Free events work the same way. When that free order is the customer's **first** order at your club, OpenCourt reports it as a **`Lead`** event with a value of 0 — with the same `content_category`, `content_name` and customer matching as a purchase. A free order from an **existing** customer sends nothing. Most free bookings come from members and pass holders booking against something they already bought — at the clubs where this launched, nearly all of them. Reporting those as leads would teach the ad platforms to find people who are already your customers, and `Lead` is the same event your own website may fire for contact forms. A free order is **never** reported as a $0 `Purchase`. A zero-value purchase would drag down your reported return on ad spend and teach the ad platforms to find people who never pay. Because `Lead` is a separate event, nothing changes for a campaign that optimizes for purchases. If you run a free-first-session offer, build a Custom Conversion on `Lead` (filter on `content_category` to narrow it, for example `space`) and optimize that campaign for it. * **Meta** receives the `Lead` automatically. * **GA4** receives it as `generate_lead`. * **Google Ads** receives it only if you map the **free\_booking** row to its own conversion action — see [Set up Google Ads conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-google-ads-conversion-tracking). Leave that row empty and free bookings are not sent to Google Ads. They never fall back to your `default` action. Refunds are reported to Meta [#refunds-are-reported-to-meta] When you refund a sale from OpenCourt — to the customer's card or in person, in full or in part — or lose a chargeback on it, OpenCourt tells Meta, so you can measure the revenue you actually kept: * **Meta** cannot change a purchase it has already recorded. Instead, OpenCourt sends a separate **`Refund`** event worth the amount returned, labeled like the original sale. To see revenue after refunds in Ads Manager, create a custom metric: purchase value minus `Refund` value. OpenCourt never subtracts more than it reported: if a customer disputes a sale you already refunded, no second `Refund` is sent. * **Google Ads** is **not** updated. Google's conversion upload service can only add conversions; it cannot remove or re-value one. Reconcile Google Ads revenue against OpenCourt's own reports. * **GA4** is not updated either. Purchases reach GA4 from the customer's browser, and a refund has no browser session to report from. Only sales OpenCourt reported to Meta are covered. A refund to **account credit** does not change anything: no cash left your club, and OpenCourt reports cash. A refund issued directly in your Stripe dashboard is not sent either — refund from OpenCourt. Refunds made before September 28, 2026 are not sent. Refunds made on or after that date are, including any made in the days before this update reached your account. Purchases made in the OpenCourt app [#purchases-made-in-the-opencourt-app] The OpenCourt customer app runs the same booking site inside a native shell. Inside the app, OpenCourt deliberately does **not** run the browser pixel — firing a tracking pixel inside an iOS app without Apple's tracking permission prompt is a policy exposure we will not take on your behalf. **In-app purchases are still reported.** They go out server-side through the Conversions API exactly like web purchases, so no sale is lost. What is missing is the browser-side context: the page views, the checkout-started events, and the click identifiers Meta uses to tie a sale back to one specific ad. So an in-app purchase is more likely to be counted as revenue without being credited to the ad that caused it. **Point your ad traffic at the web booking pages.** First-time visitors land there anyway, and that is where attribution is strongest. The app is where your existing customers rebook. What travels with each purchase [#what-travels-with-each-purchase] A few extra fields go along with every purchase so the platforms (and you) can tell sale types apart: | Field | What it contains | | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `content_category` | Which kind of sale it was — one of: `space` (a court, bay or lane booking), `event`, `day_fee`, `add_on` (equipment rental), `membership`, `membership_presale` (a deposit taken before the club opens), `pass_package` (a punch card or lesson bundle), `membership_renewal`, `store`, `coach_lesson`, `gift_card`. A charge OpenCourt cannot classify — a few legacy and manually entered charge types — is sent **without** this field rather than in a catch-all bucket | | `content_categories` | **Every** category in the order, comma-joined in the same order as above (`membership,space`). `content_category` can only name one thing, so use this one with a *contains* rule to build "any order that included a membership" | | `event_type` | Present whenever the purchase is attached to something on your schedule: `OpenPlay`, `OpenGame`, `League`, `Clinic`, `Tournament`, `Lesson` or `Other`. This includes `space` bookings (usually `OpenGame`) and coach lessons (`Lesson`) — it is not limited to the `event` category | | `content_name` | The name of what was bought — the event's name, the membership plan, the product, the rented equipment, or (for a booking) your own space category or space name. When none of those can be resolved it falls back to the charge's own description, so this field is never empty | | `content_ids` / `contents` / `num_items` | The itemized contents of the order | | `is_new_customer` | Whether this was the person's first purchase at your club | | `membership_status` | A coarse label only: member, non-member, or lapsed | **Bookings and events are two different categories.** A court, bay or lane booking reports as `space`. Registrations for open play, leagues, clinics, tournaments and other programs report as `event`, with `event_type` naming which kind. To build a "leagues only" custom conversion, filter on `event_type` equals `League`. Customer identifiers are hashed before they leave OpenCourt [#customer-identifiers-are-hashed-before-they-leave-opencourt] For an ad platform to credit a sale to an ad, it needs to recognize *which of its users* made the purchase. OpenCourt handles this with **one-way hashing (SHA-256)**: the customer's email, phone number, and a per-club customer ID are scrambled into a fingerprint before sending. Meta or Google can match that fingerprint against the hashes of their own logged-in users — but the raw email and phone number are never transmitted, and a hash can't be reversed back into them. You get accurate ad attribution without handing over your customer list in readable form. **No card or payment details are ever sent.** OpenCourt only sends what ad platforms need to match a sale to an ad. Your responsibilities [#your-responsibilities] When you turn the feature on, OpenCourt shows a data-sharing disclosure that you accept — that covers OpenCourt's side of the sending. Your club remains responsible for its **own** privacy disclosures: your privacy policy should mention ad-platform data sharing, and your region may require a cookie or consent banner (for example under GDPR in Europe or CPRA in California). This isn't legal advice — if you're unsure what your region requires, check with a legal advisor. Related articles [#related-articles] * [Set up Meta conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-meta-conversion-tracking) * [Set up GA4 conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-ga4-conversion-tracking) * [Set up Google Ads conversion tracking](/help/facility-operators/ad-and-conversion-tracking/set-up-google-ads-conversion-tracking) * [Understand your conversion data](/help/facility-operators/ad-and-conversion-tracking/understand-your-conversion-data) — how to use these fields for reporting and optimization * [Monitor and troubleshoot](/help/facility-operators/ad-and-conversion-tracking/monitor-and-troubleshoot) {/* Maintainers: This is the canonical trust/privacy page for the collection. If the event set (Purchase / Subscribe / Lead / Refund), enrichment fields, hashing behavior, or the admin/POS exclusion changes, update here first and check understand-your-conversion-data.md for consistency. The two verbatim callouts (beta note, no-card note) are standardized across the collection — keep wording in sync. */} # Access codes & scheduling This page explains **when** a booking's door code is active, and the two ways OpenCourt can schedule codes onto your locks. Most clubs never need to change anything here — but if you run a high volume of bookings, or use locks that store only a limited number of codes, it's worth understanding. When is a door code active? [#when-is-a-door-code-active] Every booking's code is **time-limited**, no matter which provider or scheduling mode you use. By default: * It becomes active **30 minutes before** the reservation start time. * It expires **10 minutes after** the reservation end time. * Outside that window the code does nothing — a customer can't get in early or stay late on it. The window follows each lock's access rule, so you can change it per lock: the **min before / min after** values on the lock's *During reservations only* rule set both the unlock window and the code window. See [Set who can unlock doors from the app](/help/facility-operators/access-controls/set-who-can-unlock-doors). The exact window for every issued code is shown in the **Access Codes** table (**Valid From** / **Valid Until**). Scheduling modes: when the code reaches the lock [#scheduling-modes-when-the-code-reaches-the-lock] "Scheduling" is about *when the code is pushed onto the physical lock*, which matters because most locks can only store a limited number of codes at once. | | **Native Scheduling** (default) | **Just-in-Time Scheduling** | | -------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------- | | Code pushed to the lock | **72 hours** before it activates | **60 minutes** before it activates | | Reliability | Highest — the code is on the lock well ahead of time | High — but the lock must be online shortly before each booking | | Codes stored on the lock at once | More (can hit the lock's limit) | Far fewer | | Best for | Clubs with fewer upcoming bookings | High-volume clubs, or locks with small code capacity | Until a code is pushed to the hardware, the Access Codes table may not show its final PIN yet — that's normal. The PIN appears once the lock accepts it. Some keypad locks store only a few dozen codes at a time — **Lockly** models are the ones clubs hit this with most often. A busy club booking many spaces days in advance can exceed that on Native Scheduling, and the lock starts rejecting new codes. How do I switch scheduling modes? [#how-do-i-switch-scheduling-modes] Go to **Settings → Access Controls** and find the **Access Code Scheduling** section. Two options: * **Native Scheduling** — "Codes are pushed to the lock 72 hours before activation. More reliable, but some devices have low code capacity limits which can cause issues with high reservation volume." * **Just-in-Time Scheduling** — "Codes are pushed to the lock 60 minutes before activation. Avoids device capacity limits, but requires the lock to have internet connectivity before each reservation." The Access Code Scheduling section with its two scheduling mode options, Native Scheduling and Just-in-Time Scheduling. Scheduling controls **when the code is pushed to the lock**, not when it works. The active window — how long before a booking a code starts working and how long after it stops — comes from each lock's access rule (30 min before / 10 min after by default). See [Set who can unlock doors from the app](/help/facility-operators/access-controls/set-who-can-unlock-doors). The Access Code Scheduling section appears for **Seam**-connected clubs. Other providers push codes their own way — for example, RemoteLock pushes the code and waits for the lock to confirm it, retrying if the lock is asleep. Until it confirms, the code shows as **pending**; on capacity-limited locks it may hold a code back until closer to the booking. That's normal, not a fault. When to switch to Just-in-Time [#when-to-switch-to-just-in-time] Consider Just-in-Time Scheduling if any of these is true: * Customers report codes that **don't work**, or your team sees `access_code.failed_to_set_on_device` errors. * Your club has a **high volume of upcoming bookings** relative to what your lock can store. * You use **Lockly** locks (or others with limited code capacity). * Your locks have **reliable internet** (Just-in-Time needs the lock online about an hour before each booking). If something goes wrong [#if-something-goes-wrong] * **Codes intermittently fail on a busy lock** — you're likely hitting the lock's code-storage limit on Native Scheduling. Switch to Just-in-Time in **Access Code Scheduling**. * **A code didn't work right at the booking start** — codes activate **30 minutes before** the start by default, and on Just-in-Time the lock must have been online about an hour earlier to receive it. Check the lock's connectivity, then work through [A customer can't get in](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in). * **A far-future booking shows no PIN yet** — expected. The PIN appears once the code is pushed to the lock (about 72 hours ahead on Native, about an hour ahead on Just-in-Time). Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Door codes day-to-day](/help/facility-operators/access-controls/door-codes-day-to-day) * [Set who can unlock doors from the app](/help/facility-operators/access-controls/set-who-can-unlock-doors) * [Connect a Seam lock to your club](/help/facility-operators/access-controls/connect-a-seam-lock) * [A customer can't get in — troubleshoot door access](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in) # Which locks work with OpenCourt OpenCourt gives each booking its own **door code** and, on supported hardware, an **in-app unlock** button — through four provider integrations: **Seam**, **RemoteLock**, **Rhombus**, and **UniFi Access**. The lock you have decides which provider you use, and a club runs **one provider at a time**. This page tells you what OpenCourt can do with each brand, so you can check a model before you commit to it. Not sure about a model? Send the exact model to [support@getopencourt.com](mailto:support@getopencourt.com) and we'll confirm what your lock or access control system supports — **PIN door codes**, **in-app unlock**, **NFC/BLE proximity keys**, or a combination. The two multi-brand platforms [#the-two-multi-brand-platforms] **Seam** and **RemoteLock** each sit in front of dozens of lock brands. Neither is better than the other for OpenCourt — both connect the same way (sign in to the account you already have), and both give you door codes and in-app unlock. **If you already have an account with one of them, use that one.** Each platform keeps its own list of exactly which models it supports, and those lists change. We link to them rather than copying them, so you're always reading the current one. RemoteLock [#remotelock] Its own hardware — **33Lock IntelliBolt / IntelliLever / IntelliMortise** (formerly branded openEDGE) — has WiFi built in with no separate gateway. It also manages third-party locks from **Schlage**, **Yale**, **Kwikset**, **August**, **igloohome**, **KeyInCode**, **Alfred**, **Dormakaba**, **McGrath**, **PROLOK**, **TrueSecure**, **UniKey**, **KoreLock** and **TTLock**, plus hubs (**Aeotec**, **SmartThings**) and wired access-control doors — electric strikes and maglocks on **Mercury** panels, **HID** readers (RPK40, Signo), **Allegion**, **ZKTeco Atlas** and **Rosslare**. Check a model on [RemoteLock's device finder](https://remotelock.com/find-your-device). Seam [#seam] Covers **August**, **Yale**, **Schlage**, **Kwikset**, **Lockly**, **TTLock** (and rebrands such as Sifely), **igloohome**, **Nuki**, **Wyze**, **2N**, **Akiles**, **Tedee**, **4SUITES**, **33 Lock**, **SmartThings** (as the hub path for Z-Wave locks), **PTI Storlogix** and **Latch**. It also reaches professional access-control systems — **Salto KS**, **Brivo**, and **Avigilon Alta** (formerly Openpath). Check a model on [Seam's supported devices list](https://docs.seam.co/latest/device-and-system-integration-guides). **Being listed by Seam is not the same as working with OpenCourt.** Seam's public list and API include brands at **beta** stage, and beta integrations aren't available to us — only ones marked live. On top of that, some live ones give OpenCourt nothing to work with (see the section further down). **Always send us the exact model before you buy**, rather than reading a brand name off Seam's site and assuming. The two single-system integrations [#the-two-single-system-integrations] Pick these when you already run that system at your venue. * **Rhombus** — doors you manage at console.rhombus.com. **Unlock-only**: it never issues door codes. See [Connect Rhombus](/help/facility-operators/access-controls/connect-rhombus). * **UniFi Access** — Ubiquiti gear on your own console. See [Connect UniFi Access](/help/facility-operators/access-controls/connect-unifi). With UniFi Access, four things have to be true, and each one catches someone out: * **A console that can actually run UniFi Access.** Not all UniFi consoles can, and the one you already own may not. As of August 2026 that list is the Dream Machine range, **UDR7**, **UDR**, **UCG-Max**, **UCG-Fiber**, **CloudKey+** and the NVRs. Ubiquiti keeps the current list at [UniFi Consoles with UniFi Access Support](https://help.ui.com/hc/en-us/articles/22230509487639-UniFi-Consoles-with-UniFi-Access-Support). If yours can't, adding a **CloudKey+** or an NVR beside it is usually cheaper than replacing the gateway. * **A PIN-capable reader.** Only some UniFi readers accept codes, and the names are close enough to be dangerous. **G6 Pro Entry** takes a PIN; **G6 Entry** doesn't. **Access Ultra** doesn't either, despite being the all-in-one that looks like the obvious single-door pick. Full table in [Connect UniFi Access](/help/facility-operators/access-controls/connect-unifi). * **An Access Control Hub behind that reader.** A camera-style reader wired to the network but not to a hub adopts into UniFi *Protect* and never appears in UniFi *Access* — so OpenCourt can't see the door at all, and no reader can unlock anything without a hub. A **Door Hub Mini** covers a single door. * **A console reachable from the internet** (Cloudflare Tunnel or a port-forward on 12445). **And one thing that must NOT be true: the console must not be enrolled in UniFi Identity Enterprise** (UID Enterprise). That mode switches off the local Access API, and OpenCourt cannot connect to such a console at all — there is no workaround. Standard UniFi Access is what you want, so raise it with your installer **before** the console is set up. Professional access-control systems [#professional-access-control-systems] If you already run a commercial access-control system, what OpenCourt can do with it varies: | System | Door codes | In-app unlock | NFC/BLE proximity keys | | ------------------------------------- | ---------------------- | ------------- | --------------------------------------------------------------------------------------------------- | | **Salto KS** | Yes | Yes | Yes — see [Salto KS mobile access](/help/facility-operators/access-controls/salto-ks-mobile-access) | | **Brivo** | Yes, on keypad readers | — | — | | **Avigilon Alta** (formerly Openpath) | — | Yes | — | | **Rhombus** | — | Yes | — | | **UniFi Access** | Yes | Yes | — | **Integration coming soon:** [Kisi](/help/facility-operators/access-controls/kisi-access-control) — we're building this one directly. Supported by the platform, but not usable by OpenCourt [#supported-by-the-platform-but-not-usable-by-opencourt] A few systems appear on Seam's list yet give OpenCourt nothing to work with. If you run one of these, the connection will succeed but no door codes or in-app unlock will appear: * **ASSA ABLOY Visionline** and **ASSA ABLOY Credential Services** — card and mobile credentials only. * **Salto ProAccess Space** — card and mobile credentials only. (Salto **KS** is different, and does work — see [Salto KS mobile access](/help/facility-operators/access-controls/salto-ks-mobile-access).) * **dormakaba Oracode** — precomputed offline codes, not pushed online, so OpenCourt can't issue per-booking PINs. Talk to us before planning around any of these. Both platforms also connect **thermostats** (ecobee, Honeywell/Resideo on RemoteLock; those plus Nest and others on Seam), and Seam adds **noise sensors**. OpenCourt doesn't use either today — [**thermostat control**](/help/facility-operators/access-controls/thermostat-control) is something we're working on, so if that's interesting for your club, let us know and we'll keep you posted. What decides whether a lock works [#what-decides-whether-a-lock-works] Three things, and they're the same on every platform: * **A keypad**, if you want booking door codes. No keypad, no code — there's nowhere to type it. * **Being online.** OpenCourt can only reach a lock the platform can reach. Some models need the brand's own bridge or hub to get there; the vendor's product page will say. * **Enough code storage**, if you book heavily. Locks vary in how many codes they hold at once. If yours is tight, switch that lock to [Just-in-Time scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling) — that's exactly what it's for. Beyond that, ask the vendor or your installer. We can't tell you which model is better built or how many codes it holds, and we'd rather say so than guess. What clubs commonly connect [#what-clubs-commonly-connect] For context rather than as a recommendation — these are the shapes we see most often: * **1–3 doors, standard entry** — a WiFi keypad deadbolt or lever from either platform's list. No hub needed, quick to connect. * **Commercial doors or high booking volume** — RemoteLock's own 33Lock hardware, or UniFi Access with a PIN-capable reader on an Access Control Hub. * **Gates, maglocks, or wired entry** — a wired system, such as RemoteLock's ACS kits or UniFi Access. ⚠️ Non-door entry points (vehicle gates, intercoms, turnstiles) vary a lot in what they expose — **check the specific model with us before planning around one**, rather than assuming it behaves like a door. "I have nothing yet — what do I actually buy?" [#i-have-nothing-yet--what-do-i-actually-buy] The most common question on sales and onboarding calls. The honest answer: **start with an installer, not with a product page.** Any access-control or security installation firm will help you more than this article can. They'll look at your actual doors — how they're hung, whether they're fire-rated, where power and network already run, what your local egress code requires — and none of that is visible from here. Where to find one: * **Any local access-control or security installer.** The most useful option for most clubs, and the one to start with. Tell them you need booking codes on the door and that the system has to expose an API. * **For UniFi** — use Ubiquiti's own directory of certified installers at [installers.ui.com](https://installers.ui.com/). Worth insisting on someone who has done **UniFi Access** specifically, not only UniFi networking; they're different products and the second doesn't imply the first. * **For RemoteLock** — email [partnersales@remotelock.com](mailto:partnersales@remotelock.com) with the subject **"OpenCourt customer - help choosing lock"**. They'll help you pick hardware and point you at a dealer in your area. **Then send us the model they recommend**, before anything is ordered. Confirming it works with OpenCourt takes us a minute and is worth doing every time, because the same brand often sells both a supported and an unsupported variant of what looks like one product. What to expect that conversation to cover [#what-to-expect-that-conversation-to-cover] Not a shopping list — you're not buying this yourself — but knowing the shape of it makes the conversation much easier. For **one staffed door with member-only hours** (a small indoor golf or racquet venue, the most common case), a wired system is three things: | | What it does | Roughly | | -------------------------- | ---------------------------------------------------------------- | -------------------------------------------- | | Access Control Hub | Drives the lock, and is what makes the door visible to OpenCourt | one per door — the Mini covers a single door | | PIN-capable reader | Where the member types their booking code | one per door | | Electric strike or maglock | The actual lock | one per door | Three items, not one. **The mistake people make is buying only the reader** — it's the visible part, so it feels like the product, but without a hub it can't open anything and OpenCourt can't see it. There is a simpler path for a single door: if you already run a smart lock at home (August, Yale, Schlage, Kwikset), one WiFi keypad lock from either platform's list works and skips the hub entirely. It's less robust for a busy commercial entrance, which is exactly the sort of trade-off an installer is there to weigh. Before you buy [#before-you-buy] **Ask us** — send the exact model to [support@getopencourt.com](mailto:support@getopencourt.com) and we'll confirm: * Whether it accepts **PIN door codes** from OpenCourt. * Whether it supports **in-app unlock**. * Whether **NFC/BLE proximity keys** are available on it. **Ask the vendor or your installer** — build grade, weather and traffic durability, whether it fits your door, whether it needs a bridge or hub, and how many codes it holds at once. If that capacity turns out to be low, you can switch the lock to [Just-in-Time scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling) yourself under **Settings → Access Controls**. Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Connect a Seam lock to your club](/help/facility-operators/access-controls/connect-a-seam-lock) * [Connect RemoteLock to your club](/help/facility-operators/access-controls/connect-remotelock) * [Connect UniFi Access to your club](/help/facility-operators/access-controls/connect-unifi) * [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling) * [Kisi access control (coming soon)](/help/facility-operators/access-controls/kisi-access-control) * [Thermostat control (coming soon)](/help/facility-operators/access-controls/thermostat-control) # Connect a Seam lock to your club Connect your club's smart locks through **Seam** so bookings get automatic door codes. *(About 5 minutes. You'll need admin access and a Seam-supported lock that's online.)* You need access-control permission. The page is under **Settings → Access Controls**. Before you begin [#before-you-begin] * Your lock is a model OpenCourt supports through Seam (for example August, Yale, Schlage, Kwikset, or Lockly) and it's **online**. Haven't bought one yet? Start with [Which locks work with OpenCourt](/help/facility-operators/access-controls/choose-a-lock-for-your-club), and [contact OpenCourt support](mailto:support@getopencourt.com) to confirm the model before installing. * You have the login for the lock's own account (for example your August or Yale account) — you'll sign in to it during the connection. Steps [#steps] 1. In the admin app, go to **Settings → Access Controls**. You land on the **Locks** tab. With nothing connected yet you'll see **No access control system connected**. The Access Controls page with no system connected, showing the Connect a provider button. 2. Click **Connect a provider**. The button expands in place into the systems OpenCourt supports. The expanded provider list showing Seam, RemoteLock, Rhombus, and UniFi Access. 3. Choose **Seam**. OpenCourt takes you to Seam's secure connect page. 4. There, choose your lock brand, **sign in to your lock account**, and approve access for OpenCourt. 5. When you're done, you're returned to the **Access Controls** page with your locks listed. The Locks tab after connecting Seam, listing the club's locks with their online status, battery level, and mapped spaces. Each lock card shows its name and model, whether it's **Online**, whether it's currently **Locked** or **Unlocked**, battery level, and badges for **Remote Unlock** and **Access Codes** — what that lock supports. 6. **Map each lock to a space.** Switch to the **Settings** tab and find **Court-to-Lock Mapping** — that heading follows your club's own wording, so it reads *Bay-to-Lock* or *Field-to-Lock* where that applies. Choose a lock for each space, then click **Save**. A booking on a mapped space automatically receives a time-limited door code. What happens next [#what-happens-next] Customers who book a mapped space get their own door code for the booking window — you don't issue or revoke anything. Open a lock to see its assigned codes and recent activity. A couple of settings worth knowing (both under **Settings → Access Controls**): * **Let customers unlock from the app** — turn this on so customers can open a mapped door from the OpenCourt app during their booking. See [Unlock a door from the app](/help/players/access-controls/unlock-a-door-from-the-app). * **Scheduling** — if your club runs a high volume of bookings, or your locks store a limited number of codes, read [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling) before going live. If something goes wrong [#if-something-goes-wrong] * **No locks appear after connecting** — the locks may be offline or not yet added in your lock account. Add or power them on in your lock's own app, then return to **Access Controls** and refresh. * **A customer says their code didn't work** — by default, codes activate **30 minutes before** the booking starts (not earlier) and expire **10 minutes after** it ends. Check the booking time and that the lock is online, then see [A customer can't get in](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in). * **Codes intermittently fail to appear on a lock** — the lock may be at its code-storage limit. See [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling) and consider Just-in-Time scheduling. Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Which locks work with OpenCourt](/help/facility-operators/access-controls/choose-a-lock-for-your-club) * [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling) * [Salto KS mobile access](/help/facility-operators/access-controls/salto-ks-mobile-access) * [Connect RemoteLock to your club](/help/facility-operators/access-controls/connect-remotelock) # Connect RemoteLock to your club Connect your club's **RemoteLock** account so bookings get automatic door codes, and so you can lock or unlock doors from the admin app. *(About 2 minutes. You'll need admin access and a RemoteLock account with at least one online lock.)* You need access-control permission. The page is under **Settings → Access Controls**. Before you begin [#before-you-begin] * You have a **RemoteLock account** with at least one lock that's online. * You can sign in to that RemoteLock account — you'll approve OpenCourt's access during the connection. Steps [#steps] 1. In the admin app, go to **Settings → Access Controls**. You land on the **Locks** tab. With nothing connected yet you'll see **No access control system connected**. The Access Controls page with no system connected, showing the Connect a provider button. 2. Click **Connect a provider**, then choose **RemoteLock**. You're sent to RemoteLock to sign in. The expanded provider list showing Seam, RemoteLock, Rhombus, and UniFi Access. 3. **Sign in to RemoteLock and approve access** for OpenCourt. 4. You return to the **Access Controls** page, which now lists the locks found on your account. You'll see a line like **"RemoteLock connected · 1 lock. Map each to a court in Settings."** — that line uses your club's own wording, so it reads "bay" or "field" if that's what you book. Each lock card carries badges for what it supports — **Online**, **PIN codes**, **Remote unlock**. The Locks tab after connecting RemoteLock, showing a lock with Online, PIN codes, and Remote unlock badges. 5. **Map each lock to a space.** Switch to the **Settings** tab and find **Court-to-Lock Mapping** — the heading follows your club's wording. Choose a lock for each space and click **Save**. A booking on a mapped space then automatically receives a time-limited door code. What happens next [#what-happens-next] Customers who book a mapped space get their own door code for the booking window — RemoteLock generates the code and programs it onto the lock for you. Open a lock to see its assigned codes and recent activity, and to lock or unlock the door remotely. On locks that support remote unlock, customers can also unlock a mapped door from the OpenCourt app during their booking (according to the access rules you set) — see [Set who can unlock doors from the app](/help/facility-operators/access-controls/set-who-can-unlock-doors). RemoteLock programs codes onto the lock in the background and confirms once the lock accepts them, retrying if the lock is asleep. A new code can show as **pending** for a while before it lands — and either way it only activates for the booking window, not the moment it's created. If something goes wrong [#if-something-goes-wrong] * **"We couldn't reach RemoteLock"** — a temporary connection issue. Try again shortly, or reconnect the integration from the **Access Controls** page. * **"RemoteLock is connected, but no locks were found"** — add your devices in RemoteLock, then return to **Access Controls** and refresh. * **A code is off by a few hours** — door codes follow the **lock's local time zone**. Make sure the lock's time zone in RemoteLock matches the club's; OpenCourt flags a mismatch on the lock's page. Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Which locks work with OpenCourt](/help/facility-operators/access-controls/choose-a-lock-for-your-club) * [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling) * [Connect a Seam lock to your club](/help/facility-operators/access-controls/connect-a-seam-lock) * [A customer can't get in — troubleshoot door access](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in) # Connect Rhombus to your club Connect your club's **Rhombus** access-control organization so you can unlock mapped doors from the admin app, and so your customers can unlock a door from the OpenCourt app during their booking. *(About 2 minutes. You'll need admin access and a Rhombus API key.)* You need access-control permission. The page is under **Settings → Access Controls**. Rhombus is an **unlock-only** integration. It does **not** create per-booking door codes (PINs) the way Seam and RemoteLock do — instead, people unlock a mapped door from the OpenCourt app. When a door is unlocked it opens for a few seconds and then relocks itself. Before you begin [#before-you-begin] * You have a **Rhombus organization** with at least one access-controlled door set up. * You have a **Rhombus API key** for that organization. Generate one in the Rhombus console (Settings → API keys). Treat it like a password — it grants access to your whole Rhombus organization. Steps [#steps] 1. In the admin app, go to **Settings → Access Controls**. You land on the **Locks** tab. With nothing connected yet you'll see **No access control system connected**. The Access Controls page with no system connected, showing the Connect a provider button. 2. Click **Connect a provider**, then choose **Rhombus**. The **Connect Rhombus** dialog opens — "Paste an organization API key from your Rhombus console. We'll validate it, then discover your access-controlled doors so you can map them to courts." The expanded provider list showing Seam, RemoteLock, Rhombus, and UniFi Access. 3. Paste your key into **Rhombus API key** and click **Connect**. OpenCourt validates the key with Rhombus and, if it's valid, finds your organization's doors. (Your key is stored securely and is never shown again.) The Connect Rhombus dialog with a masked field for the Rhombus API key. 4. You return to the **Access Controls** page, which shows a line like **"Rhombus connected · 1 door. Map each to a court in Settings."** — that line follows your club's wording. Note the door's badges: **Online** and **Remote unlock**, with no **PIN codes** badge — that's the unlock-only nature of Rhombus, visible at a glance. The Locks tab after connecting Rhombus, showing a door with Online and Remote unlock badges but no PIN codes badge. 5. **Map each door to a space.** Switch to the **Settings** tab and find **Court-to-Lock Mapping** — the heading follows your club's wording. Choose a door for each space and click **Save**. This is what tells OpenCourt which door belongs to which space, so the right people can unlock it. 6. **Choose who can unlock, and when.** Open a door and use its **Door access** section to control who can unlock it from the app and during which times (for example, only during a booking). See [Set who can unlock doors from the app](/help/facility-operators/access-controls/set-who-can-unlock-doors). What happens next [#what-happens-next] Customers who book a mapped space can unlock that space's door from the OpenCourt app, according to the access rules you set. You can also unlock any mapped door yourself from its page in the admin app — the door opens momentarily and then relocks on its own. If something goes wrong [#if-something-goes-wrong] * **"Couldn't connect to Rhombus — check the API key"** — the key was rejected or Rhombus couldn't be reached. Double-check you copied the whole key from the Rhombus console and try again. * **"Rhombus is connected, but no doors were found"** — add or enable access-controlled doors in Rhombus, then return to **Access Controls** and refresh. * **"The lock didn't respond"** when unlocking — a temporary issue reaching the door. Wait a moment and try again. Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Which locks work with OpenCourt](/help/facility-operators/access-controls/choose-a-lock-for-your-club) * [Set who can unlock doors from the app](/help/facility-operators/access-controls/set-who-can-unlock-doors) * [Unlock a door from the app](/help/players/access-controls/unlock-a-door-from-the-app) * [Connect a Seam lock to your club](/help/facility-operators/access-controls/connect-a-seam-lock) * [A customer can't get in — troubleshoot door access](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in) # Connect UniFi Access to your club {/* Maintainers: the UniFi-side paths here were walked on live hardware (UDR7, UniFi OS 5.1.19, Access 4.3.3, 2026-08-07). Two things move between Access versions and are worth re-checking when you touch this page: the API Token location (4.3.3 = Settings → General, bottom of page — no "Advanced" section) and Ubiquiti's console-compatibility list, which is why that list is dated inline and linked to the source rather than copied without provenance. Screenshot still to add: OpenCourt's Access Controls page with doors connected. */} Connect your club's **UniFi Access** console so every booking gets its own door code automatically, and so you and your customers can unlock a mapped door from the OpenCourt app. *(About 5 minutes. You'll need admin access, your console reachable from the internet, and a scoped UniFi Access API token.)* **Read this before you buy or configure anything: your console must NOT be enrolled in UniFi Identity Enterprise.** UniFi Identity Enterprise (also written **UID Enterprise**) is Ubiquiti's cloud-managed identity mode. **Any console enrolled in it has the local Access API switched off, and OpenCourt cannot connect to it at all.** This is not a setting we can work around, and no amount of tunnelling, port-forwarding, or token fiddling changes it. If your console is enrolled, OpenCourt's connect attempt fails with *"This console runs UniFi Identity Enterprise, which disables the local API."* **Use standard UniFi Access.** If a console is already on Identity Enterprise, it has to be moved back to standalone UniFi Access before it can be connected — so tell your installer this **before** they set the console up, not after. Undoing it later is far more work than avoiding it. You need access-control permission. The page is under **Settings → Access Controls**. What you'll need (and who sets it up) [#what-youll-need-and-who-sets-it-up] UniFi Access is a **complete access-control system, not a single device** — neither a hub nor a reader alone will open a door. For one door you'll typically need: * A **UniFi console that can run the UniFi Access application** — see the warning below, because not every UniFi console can. * An **Access Control Hub** — the door controller the reader and lock connect to. Ubiquiti sells several: **Door Hub**, **Door Hub Mini** (the compact single-door one), **Gate Hub**, **Elevator Hub**, **Enterprise Access Hub** (up to 8 doors), and **Retrofit Hub**. Pick by application, not by price — a gate needs the Gate Hub, not a Door Hub. * A **reader with PIN support** — see the table below. **This is the one choice that decides whether booking codes work at all**, so don't let it be made on price. * An **electric lock or strike**, plus the usual door hardware. * **Networking** — the hubs and readers are PoE-powered, and some hubs need the higher **PoE++** standard rather than ordinary PoE, so a suitable switch or injector may be required. Your installer will size this; you don't need to work it out yourself. * A way for OpenCourt to reach the console (the next section). **Not every UniFi console can run UniFi Access — check yours before you buy hardware.** This catches people out because the console is usually already installed, and the Access application simply isn't offered on it. **As of August 2026, Ubiquiti lists these as compatible:** Dream Machine Pro (UDM-Pro), Dream Machine Special Edition (UDM-SE), Dream Machine Pro Max (UDM-Pro-Max), Dream Wall (UDW), Dream Router 7 (UDR7), Dream Router (UDR), Cloud Gateway Max (UCG-Max), Cloud Gateway Fiber (UCG-Fiber), CloudKey+ (UCK-G2-PLUS), Network Video Recorder (UNVR), NVR Pro (UNVR-Pro), and Enterprise NVR (ENVR). Ubiquiti maintains this list and it changes as new hardware ships, so treat the version on their site as authoritative: **[UniFi Consoles with UniFi Access Support](https://help.ui.com/hc/en-us/articles/22230509487639-UniFi-Consoles-with-UniFi-Access-Support)**. **You may not need to replace your gateway.** Ubiquiti's own guidance is to add a supplementary console — a CloudKey+ or an NVR — alongside the one you have, and let it run Access. Site Manager still manages everything together. **Your installer should set up the UniFi Access application and the door itself.** That means installing the Access application on the console, adopting the hub and reader, creating the door, and wiring the lock. It's routine work for anyone who does UniFi installs, and we don't document it here — Ubiquiti's [Getting Started with UniFi Access](https://help.ui.com/hc/en-us/articles/17452334269975-Getting-Started-with-UniFi-Access) is the reference to hand them. **You'll know that part is finished when the door appears in UniFi Access showing a status of `Locked`.** That's the point to come back to this guide. Which UniFi readers accept booking codes [#which-unifi-readers-accept-booking-codes] **Booking codes require a PIN-capable reader. This is a hardware decision, and it cannot be fixed in software later.** Several UniFi readers have no keypad at all, and a reader without a keypad can never accept a booking code — there is nowhere to type it. **OpenCourt cannot detect which reader you installed.** Every UniFi door reports itself as able to take codes, so if the reader has no keypad, OpenCourt will still generate a code for each booking and your customers will simply have no way to enter it. Nothing will look broken until a customer is standing at the door. **Check the model against the table below before you order.** If a non-PIN reader is already installed, OpenCourt can still unlock the door from the app — but per-booking codes won't work until the reader is replaced. | Accepts PIN codes ✅ | No PIN ❌ | | ----------------------------- | --------------------- | | G6 Pro Entry | **G6 Entry** | | G3 Reader Pro · G2 Reader Pro | G3 Reader · G2 Reader | | G3 Reader Fingerprint | **Access Ultra** | | Reader Flex | Reader Lite | | Intercom · G3 Intercom | Retrofit Reader | | Retrofit Reader Fingerprint | — | **Two model names catch people out.** * **G6 Entry and G6 Pro Entry are not the same device.** Only the **Pro** accepts PIN codes. The names differ by one word and the price differs by a lot less than you'd expect — check the model before you buy. * **Access Ultra is an integrated hub and reader, and it still has no PIN.** It looks like the tidy all-in-one choice for a single door, and it will never accept a booking code. **A reader on its own does nothing — it has to be wired to an Access Control Hub.** Ubiquiti requires the reader to connect **directly to the hub** (same VLAN, Layer-2 Ethernet). Without a hub, a camera-style reader like the G6 Pro Entry is adopted into **UniFi Protect** only and never appears in **UniFi Access** — which is the application OpenCourt talks to, so we can't see the door at all. The hub is also the only part of the chain with a relay to actually throw the lock. Recent Access versions (3.2.42 and later) do let a non-camera reader run from a plain PoE switch, and it will show up in UniFi Access that way — but door unlocking isn't supported in that state, so it still gives OpenCourt nothing to work with. If you already own a reader and no hub, that's a small addition rather than a redo — a **Door Hub Mini** covers a single door. It does mean re-running the reader's Ethernet to the hub, so it's an installer visit, not a settings change. **OpenCourt doesn't spec, supply, or install the UniFi hardware** — that's the UniFi side. If you're new to UniFi Access, work with a **UniFi/Ubiquiti installer or IT person**; it's straightforward for anyone who does this regularly. We'll happily share these guides with them, but the install itself is on your side. How OpenCourt reaches your console [#how-opencourt-reaches-your-console] UniFi Access runs **entirely on your own console** — there is no UniFi cloud for door control. So unlike Seam or RemoteLock, where you just sign in to an account, something has to give OpenCourt a route to a box sitting in your building. **Sort this out before you connect**, because it produces the address you'll paste in later. There are two supported ways. **We recommend a port-forward**, and the reason is reliability rather than convenience: it adds nothing to your building that can quietly stop working. | | **Port-forward** ✅ recommended | **Cloudflare Tunnel** | | ----------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | **What it is** | Open port `12445` on your firewall, pointing at the console | A helper program makes an outbound-only connection to Cloudflare | | **Setup time** | \~10 minutes | \~30 minutes, one time | | **You need** | A **static IP** from your ISP, or **DDNS** (built into UniFi) | A Cloudflare account, a domain hosted on Cloudflare, **and a computer that is always on** | | **Extra equipment** | **None** | An always-on device — a NAS running Docker, a mini-PC, a Raspberry Pi | | **Open inbound ports** | Yes — port 12445 | None | | **Works behind CGNAT** | ❌ No | ✅ Yes | | **Your installer already knows it** | Almost certainly | Often not | Why we recommend the port-forward [#why-we-recommend-the-port-forward] **It adds no new hardware, no third-party account, and no software that has to keep running.** Once the rule is in place, the only things that need to stay up are your internet connection and the console itself — and if either of those is down, your club has bigger problems than door codes. A tunnel needs **a computer that is powered on and running the helper program at all times**. That machine becomes a part of your access-control system that nobody thinks of as part of your access-control system. When it reboots without restarting the helper, or its drive wears out, or an update stops the container, **door codes stop and nothing appears to have changed.** That is a much harder problem to spot than a firewall rule someone edited. **A tunnel cannot run on the UniFi console itself.** UniFi OS firmware updates erase anything installed outside the supported applications, so an update would silently break door access. It has to be a separate always-on device. Choose the Cloudflare Tunnel instead if any of these are true [#choose-the-cloudflare-tunnel-instead-if-any-of-these-are-true] * **Your ISP uses CGNAT.** Then a port-forward is impossible, not merely inadvisable. This is common on fixed wireless, mobile broadband and Starlink. **The test:** if your router's WAN address starts `100.64.` through `100.127.`, or doesn't match what a "what's my IP" search reports, you are behind CGNAT. * **You can't get a static IP and don't want to rely on DDNS.** * **Your organisation's IT policy is not to open inbound ports.** Some clubs inside a larger business or a landlord's network have this rule set for them. * **You already run an always-on NAS or server and are comfortable maintaining it.** Then the main drawback largely goes away. → **[Make your UniFi console reachable (Cloudflare Tunnel)](/help/facility-operators/access-controls/expose-unifi-console-cloudflare-tunnel)** Setting up the port-forward [#setting-up-the-port-forward] Four steps, all on your side. Your installer can do this in about ten minutes. 1. **Give the console a fixed local IP address**, or a DHCP reservation for it. Do this first. If the console's local address ever changes, the forwarding rule points at nothing. 2. **Make your public address stable — a static IP from your ISP, or DDNS.** ⚠️ **Check before you buy anything:** most business connections already have a *public* address, and what you might need to purchase is a **static** one so it stops changing. If you already have a static IP, or you're happy with DDNS, there is nothing to buy. **UniFi has DDNS built in** at **Settings → Internet → your WAN → Dynamic DNS**, which keeps this on your own equipment with nothing extra to run. 3. **Forward TCP port `12445`** to the console's local IP. 4. **Test it from outside your network before connecting** — use [the token self-test](#optional-have-your-installer-test-the-token-first) below, run from a phone on cellular rather than on the club's Wi-Fi. **A rule that works from inside the building proves nothing.** **Steps 1 and 2 are what keep this working for years.** Nearly every port-forward that fails later does so because the console's local IP moved or the club's public IP changed. Both are one-time settings. **Keep your API token private.** With a port-forward, the token is what authorises access to your console, so treat it the way you'd treat a key: don't share it, don't reuse it anywhere else, and create a fresh one if it ever ends up somewhere public. This is the same arrangement businesses use every day for remote access to equipment on site. Two easy habits make it stronger: **set your PIN length to 6 digits** (see [What happens next](#what-happens-next)), and **delete any old tokens** you're no longer using. Whichever you pick, OpenCourt secures the connection to your console. On a **tunnel** you get a normal verified certificate. On a **port-forward** your console presents its own self-signed certificate, so OpenCourt records that certificate's identity the first time it connects and refuses to talk to anything that doesn't match it afterwards. Nothing for you to configure. One consequence: **if the console is ever factory reset, it generates a new certificate** and OpenCourt will stop connecting on purpose. Reconnect on the Access Controls page and it re-records the new one. A normal firmware update does not do this. Before you begin [#before-you-begin] * You have a **UniFi Access console** (for example, a Dream Machine Pro Max) running the **UniFi Access** application, with at least one door connected through an **Access Control Hub** and a **PIN-capable reader** (see the table above). Remote unlock only works on a door bound to a hub. * **Your console is NOT enrolled in UniFi Identity Enterprise.** That mode turns off the local API OpenCourt connects to. Standard UniFi Access is what you want. (If it's already on Identity Enterprise, you'd need to move it back to standalone UniFi Access before connecting.) * **Your console is reachable from the internet**, by either a **port-forward on 12445** or a **Cloudflare Tunnel** — see [How OpenCourt reaches your console](#how-opencourt-reaches-your-console) above. Either way, you come out of it with the **Console address** you'll paste in below. * Your console is running **UniFi Access 1.9.2 or later** — the version that introduced the API OpenCourt uses. * You have a **scoped UniFi Access API token**. Creating one takes a minute — see the next section. Create the API token [#create-the-api-token] **This lives inside the UniFi Access application, not in UniFi Network or UniFi OS.** Several UniFi applications have their own "Settings → General" page, so make sure **Access** is the selected application at the top of the console before you start. If your sidebar shows Policies & Schedules, Card Inventory, Touch Pass and Visitors, you're in the right place. 1. In the console, open the **Access** application, then go to **Settings → General**. 2. Scroll to the **bottom of the page**. **API Token** is the last row, below Data Retention and Network. Click **Create New**. UniFi Access Settings → General, scrolled to the bottom, with the API Token row and its Create New link highlighted. > \[!TIP] > **There's no "Advanced" section to look for** — the token sits directly at the foot of the **General** page. > Older Access versions placed it under **Settings → Security → Advanced**, so check there if your console is > behind. 3. Fill in the dialog: The New API Token dialog, filled in correctly: Validity Period set to Never Expire, and Webhooks changed from None to Edit while every other permission stays at its default. The screenshot above shows the finished state — **Never Expire**, and **Webhooks on `Edit`**. Everything else is exactly as the dialog opened. | Field | What to set | | ------------------- | --------------------------------------------------------------------------------- | | **Name** | Anything you'll recognise later — **`OpenCourt Integration`** is a good choice. | | **Validity Period** | **Never Expire.** See the warning below — this one matters. | | **Permissions** | **Leave every row at its default, then change `Webhooks` from `None` to `Edit`.** | 4. Click **Create**, then **copy the token immediately** — see the warning below. About those permissions [#about-those-permissions] The dialog opens with sensible defaults, and **`Webhooks` is the only one you have to change.** It defaults to `None`, and OpenCourt uses it to receive door events from your console, so the connection won't work without it. For reference, this is what OpenCourt actually uses each one for: | Permission | Needed? | Why | | ------------------- | --------------------- | ----------------------------------------------------------------------------- | | **People & Groups** | Default (Edit) | Bookings are added as time-limited visitors alongside your own people. | | **Visitor** | Default (Edit) | Each booking becomes a visitor, valid only for its time window, then removed. | | **Access Policy** | Default (Edit) | Scopes each booking's access to the right door. | | **Credentials** | Default (Edit) | Issues and revokes the booking's PIN code. | | **Locations** | Default (Edit) | Reads your doors so they appear in OpenCourt, and unlocks them on request. | | **Device** | Default (View) | Reads hub and reader status. | | **System Log** | Default (View) | Not used by OpenCourt. Harmless to leave as-is. | | **Webhooks** | ⚠️ **Change to Edit** | Door events. **Defaults to `None` — this is the one to change.** | | **API Server** | Default (None) | Not used by OpenCourt. Leave it off. | **Set Validity Period to `Never Expire`.** If you pick a fixed period, the token silently stops working when it ends — and the first sign is a customer standing at a door their code no longer opens, weeks or months after everything was set up correctly. Nothing warns you beforehand. If your security policy won't allow a non-expiring token, that's fine — but **write the expiry date in your calendar with a reminder a week ahead**, and reconnect with a fresh token before it lapses. **UniFi shows the token only once.** Copy it before you close the dialog, and paste it somewhere safe. If you lose it you can't retrieve it — you'll have to delete it and create another. Optional: have your installer test the token first [#optional-have-your-installer-test-the-token-first] This is worth 30 seconds, because it tells you **which side a problem is on** before you involve anyone. Your installer runs it from any computer on the same network as the console, replacing the address and the token: ```bash curl -i -k 'https://CONSOLE-IP:12445/api/v1/developer/users' \ -H 'Authorization: Bearer YOUR_TOKEN' ``` * **`"code": "SUCCESS"` with a list of users** — the console, the API and the token are all good. Any later failure is about reachability from the internet, not about UniFi. * **`HTTP 401` with `CODE_UNAUTHORIZED`** — the token is wrong, was deleted, or has expired. Create a new one. * **Nothing connects at all** — UniFi Access isn't installed on that console, or the address or port is wrong. **`HTTP 200` on its own does not mean success — read the `code` field.** UniFi returns `HTTP 200` for most failures and puts the real result in the response body. A bad token is the exception and does return a genuine `401`, but almost everything else arrives as `HTTP 200` with a `code` other than `SUCCESS`, for example: ``` HTTP 200 \{"code": "CODE_USER_WORKER_NOT_EXISTS", "msg": "User not found."\} ``` **So the test to apply is `"code": "SUCCESS"`, never the HTTP status.** This trips up almost everyone troubleshooting the UniFi API for the first time. The `-k` is expected and correct. The console presents its own self-signed certificate on port 12445, which is normal for local UniFi Access, and OpenCourt handles that certificate properly when it connects. Run the same test from outside the club (port-forward only) [#run-the-same-test-from-outside-the-club-port-forward-only] Once the forward is in place, repeat the command **using your public address and from a connection that is not the club's Wi-Fi** — a phone hotspot works: ```bash curl -i -k 'https://your-host.example.com:12445/api/v1/developer/users' \ -H 'Authorization: Bearer YOUR_TOKEN' ``` **This is the check that matters.** A forward that works from inside the building proves nothing at all, because traffic never leaves your network. If this succeeds, OpenCourt can reach your console. Steps [#steps] 1. In the admin app, go to **Settings → Access Controls**. You land on the **Locks** tab. With nothing connected yet you'll see **No access control system connected**. The Access Controls page with no system connected, showing the Connect a provider button. 2. Click **Connect a provider**, then choose **UniFi Access**. A dialog opens. The expanded provider list showing Seam, RemoteLock, Rhombus, and UniFi Access. 3. Under **How is the console reached?**, pick the one that matches what you actually set up — **Direct / port-forward (recommended)** or **Cloudflare Tunnel**. > \[!NOTE] > **Pick the one you built, not the one marked recommended.** Choosing the wrong option here is the most common > reason a correct address and a valid token still fail to connect, because the two verify your console's > certificate in different ways. 4. In **Console address**, paste your console's address. For a port-forward that's the full address **including the port** (for example `https://your-host.example.com:12445`); for a tunnel it's just the hostname (for example `access.yourclub.com`). 5. In **API token**, paste the scoped token you created, then click **Connect**. The Connect UniFi Access dialog with the reachability options, console address, and API token fields. The dialog summarises the difference: **Direct / port-forward (recommended)** — "Port 12445 forwarded to the console. Nothing extra to run" — versus **Cloudflare Tunnel** — "no open ports. Needs an always-on device running the tunnel helper." 6. OpenCourt validates the token against your console, sets up push notifications for door events, and discovers your doors. You return to the **Access Controls** page, which now shows **UniFi Access connected** and the doors it found. 7. **Map each door to a space.** Switch to the **Settings** tab and find **Court-to-Lock Mapping** — the heading follows your club's wording. Choose a door for each space and click **Save**. This is what tells OpenCourt which door belongs to which space, so the right codes and unlock permissions apply. > \[!TIP] > **The door names in this list come straight from UniFi, and they're longer than what your installer typed.** > UniFi builds each label as *console name → floor or location → door name*, so a door someone named > `OpenCourt Door` shows up here as `Dream Router 7 - 1F - OpenCourt Door`. > > Two things follow from that. **Name doors after the space they serve** — `Bay 1`, `Court 3`, > `Front Entrance` — never leaving a default like `Door c84b`. And **name your floors and locations in UniFi > sensibly too**, because they appear in every label here and are what tells two similar doors apart. > > Renaming a door in UniFi later is safe and the mapping survives. **Deleting a door and recreating it is > not** — the new one has to be mapped again. 8. **Choose who can unlock, and when.** Open a door and use its **Door access** section to control who can unlock it from the app and during which times. See [Set who can unlock doors from the app](/help/facility-operators/access-controls/set-who-can-unlock-doors). What happens next [#what-happens-next] When a customer books a mapped space, OpenCourt creates a **door code** on your console that works only for that booking's time window, then removes it afterward — nothing to hand out or revoke. On doors bound to a hub, you can also unlock a mapped door yourself from its page in the admin app, and customers can unlock from the OpenCourt app during their booking (according to the access rules you set). The door opens momentarily and then relocks on its own. **Set your PIN length to 6 digits — UniFi defaults to 4.** **Your console decides how long the codes are, not OpenCourt.** The codes are generated by UniFi Access itself, using the **PIN** setting in **UniFi Access → Settings → General**. Out of the box that's **Fixed Length, 4 Digits** — only 10,000 possible codes, on a door that may be unattended 24/7. Selecting **6 Digits** takes it to a million and costs your customers two extra taps. The PIN setting in UniFi Access Settings → General, with 6 Digits selected instead of the 4 Digits default. The change applies to codes issued from then on. Codes already out with customers keep working, so it's safe to change at any time. Everything OpenCourt creates is **added alongside** your console's own setup — your existing cards, PINs, and policies keep working exactly as before, and OpenCourt only ever removes the codes it created. If something goes wrong [#if-something-goes-wrong] Narrow it down first — three questions [#narrow-it-down-first--three-questions] Answering these before you check anything saves most of the work: 1. **Is it one customer, or everyone?** One customer is almost always their booking or their code, not your setup. Everyone means the connection between OpenCourt and your console. 2. **Is it one door, or all doors?** One door points at that door's hardware or its mapping. All doors points at the console or the connection. 3. **Did anything change?** A new router, an internet outage, a UniFi firmware update, an IT visit, a console reset. Access control breaks far more often because something else changed than on its own. The five-minute self-check [#the-five-minute-self-check] Work down this list. It's ordered by how often each one turns out to be the cause. 1. ✅ **Is the API token still there?** This is the most common cause by far. Open **UniFi Access → Settings → General** and look at the **API Token** row. **If the token you created for OpenCourt is missing, it was deleted** — by another admin, by a console restore, or by someone tidying up. If it's listed but shows an expiry date that has passed, it's dead too. Either way: create a new one (**Never Expire**, and remember **Webhooks → Edit**), then reconnect in OpenCourt with the new token. 2. ✅ **Is the console online?** Check it in UniFi, or at `unifi.ui.com`. A console that's rebooting for a firmware update is briefly unreachable and needs nothing from you but a few minutes. 3. ✅ **Is the club's internet up?** Nothing reaches your console without it. 4. ✅ **Does the door still exist in UniFi Access, and is it still bound to its hub?** If the door was deleted and recreated, it's a *new* door as far as OpenCourt is concerned and needs re-mapping. 5. ✅ **Is the right reachability option still selected in OpenCourt?** If your setup changed from a tunnel to a port-forward, or the other way, the option in OpenCourt has to change with it. 6. ✅ **Port-forward only — has your public address changed?** Compare what a "what's my IP" search shows against the Console address saved in OpenCourt. If they differ, that's your answer, and a static IP or DDNS is the permanent fix. 7. ✅ **Tunnel only — is the always-on device still running the tunnel helper?** Check that the machine is powered on and the helper is running, and that the tunnel shows **Healthy** in Cloudflare. A machine that rebooted without restarting the helper is the usual culprit. **The single most useful test is the [self-test command](#optional-have-your-installer-test-the-token-first) above, run from outside the club.** It separates "the console and token are fine" from "OpenCourt can't reach it," which is the fork every other question hangs off. Remember to judge it by `"code": "SUCCESS"`, not by the HTTP status. Specific symptoms [#specific-symptoms] * **"This console runs UniFi Identity Enterprise, which disables the local API"** — the console is enrolled in UniFi Identity Enterprise, which turns off the local API OpenCourt uses. Move the console back to standalone UniFi Access, then connect again. * **"Couldn't connect — check the API token and its scopes"** — the token was rejected. Confirm you copied the whole token and that it hasn't passed its validity period. **The most common cause is `Webhooks` left at `None`** — it's the one permission the dialog doesn't grant by default. The [token self-test](#optional-have-your-installer-test-the-token-first) above tells you in one command whether the token itself is the problem. * **It worked for months and then stopped** — check the token's **Validity Period**. A token with a fixed period stops working the moment it expires, with no warning. Create a new one set to **Never Expire** and reconnect. * **The Access application isn't offered on your console** — not every UniFi console can run UniFi Access. Check the compatibility warning above, and note that adding a CloudKey+ or NVR alongside your existing gateway is usually cheaper than replacing it. * **Codes are issued but customers can't enter them** — the reader has no keypad. Check its model against the PIN-capable table above. * **Can't reach the console** — double-check the address, and confirm the console is online. For a tunnel, make sure the tunnel is running and the hostname resolves. For a port-forward, check that port **12445** is forwarded to the console's local IP, and **test from outside your network** — a phone on cellular, not the club's Wi-Fi. A rule that works from inside the building tells you nothing. * **It worked, then stopped after an internet outage or a router change (port-forward)** — your public IP probably changed. That's what a static IP or DDNS prevents. Update the Console address in OpenCourt, then fix the underlying cause so it doesn't recur. * **Port-forwarding won't work at all, from anywhere** — you may be behind **CGNAT**, where your ISP shares one address between customers. Check whether your router's WAN address starts `100.64.`–`100.127.`, or differs from what a "what's my IP" search shows. If so, ask your ISP for a public IP, or use the [Cloudflare Tunnel](/help/facility-operators/access-controls/expose-unifi-console-cloudflare-tunnel) instead — it works behind CGNAT. * **Everything stopped right after the console was factory reset (port-forward)** — expected. The console generated a new certificate, and OpenCourt deliberately refuses to connect to one it doesn't recognise. Reconnect on the Access Controls page. * **A door shows no remote-unlock option** — remote unlock only works on doors bound to an **Access Control Hub**. Door codes still work on any PIN-capable reader on that hub. * **A reader you installed doesn't appear in UniFi Access at all** — it isn't wired to an Access Control Hub. Camera-style readers (G6 Entry / G6 Pro Entry) adopt into UniFi *Protect* without one, which looks like a working install but leaves the door invisible to UniFi Access, and therefore to OpenCourt. * **"The lock didn't respond"** when unlocking — a temporary issue reaching the door (offline or busy). Wait a moment and try again. * **Codes work, but door activity never appears in OpenCourt** — the token is missing the **Webhooks** permission, which is the one the dialog leaves at `None`. Codes and unlocking work without it, so everything looks fine until you notice the history is empty. Create a token with **Webhooks → Edit** and reconnect. * **A customer's code doesn't work, but everyone else's does** — check the booking is for the space mapped to that door, and that the customer is trying during their booked window. Codes are created for the booking's time only. Also confirm they're entering it on a keypad reader, not tapping a card reader. Still stuck? Send us this [#still-stuck-send-us-this] If you contact us, these five things let us skip straight to the cause: 1. **Whether it's one customer or everyone**, and **one door or all doors** 2. **What changed recently**, if anything 3. **The output of the self-test command** run from outside the club — with the token itself removed 4. **Your console's UniFi OS and Access version numbers** (Access shows its version at the bottom of its sidebar) 5. **How OpenCourt reaches the console** — port-forward or tunnel Email [support@getopencourt.com](mailto:support@getopencourt.com). ⚠️ **Never send us your API token** — we don't need it, and you should replace any token that's been shared. Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Which locks work with OpenCourt](/help/facility-operators/access-controls/choose-a-lock-for-your-club) * [Set who can unlock doors from the app](/help/facility-operators/access-controls/set-who-can-unlock-doors) * [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling) * [Unlock a door from the app](/help/players/access-controls/unlock-a-door-from-the-app) * [Make your UniFi console reachable (Cloudflare Tunnel)](/help/facility-operators/access-controls/expose-unifi-console-cloudflare-tunnel) * [Connect Rhombus to your club](/help/facility-operators/access-controls/connect-rhombus) * [A customer can't get in — troubleshoot door access](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in) # Disconnect or switch your lock provider Disconnect your club's lock provider, or replace it with a different one. *(About 5 minutes. You'll need admin access. Disconnecting revokes every active door code, so pick a quiet time.)* This lives under **Settings → Access Controls** and needs access-control permission. Your club runs **one** provider at a time — Seam, RemoteLock, Rhombus, or UniFi Access. There's no way to run two at once, so switching means disconnect first, then connect the new one. Pause codes without disconnecting [#pause-codes-without-disconnecting] If you only need to stop new codes for a while — a closure, a hardware swap, testing — don't disconnect. Change the **access-control mode** instead. The mode has three options: * *Off* — "No access codes are used." * *Manual Access Codes* — you set codes yourself. * *Smart Lock (\{provider})* — codes are generated automatically. Switching from *Smart Lock* to *Off* or *Manual Access Codes* shows this confirmation: > New events and reservations will no longer generate \{provider} access codes. Existing codes on previously created events will remain active. Your \{provider} connection and lock mappings will be preserved if you switch back. So a mode switch **pauses** code generation but keeps your connection, your lock-to-space mappings, and your per-lock rules. Disconnecting removes all of that. For anything temporary, switch the mode. Disconnect the integration [#disconnect-the-integration] 1. Go to **Settings → Access Controls** and stay on the **Locks** tab. **Disconnect Integration** sits at the top right, above your lock cards — not on the Settings tab. The Locks tab with the Disconnect Integration button at the top right, above the lock cards. 2. Click **Disconnect Integration**. A confirmation opens titled **Disconnect locks integration?** with this text: "This will immediately revoke all active door codes and remove all lock devices. Customers and staff will lose smart lock access until you reconnect a provider. You can reconnect at any time." The Disconnect locks integration confirmation dialog, warning that active door codes will be revoked, with Cancel and Disconnect buttons. 3. Click **Disconnect**. You'll see **Locks integration disconnected.** and the page returns to the provider picker: "Choose the access control system your club uses." — listing **Seam**, **RemoteLock**, **Rhombus**, and **UniFi Access**. What disconnecting removes [#what-disconnecting-removes] Disconnecting is immediate and clears everything: * **All active door codes are revoked** and stored codes are deleted — customers and staff lose smart lock access right away. * **Every lock device and its space mapping is removed.** * **The provider connection is removed.** * The action is recorded in your club's activity log. Reconnecting later means re-mapping locks to spaces and reconfiguring per-lock rules from scratch. Switch to a different provider [#switch-to-a-different-provider] 1. **Pick a quiet time.** Customers with upcoming bookings lose their codes the moment you disconnect. 2. **Disconnect** the current provider (steps above). 3. **Connect the new provider** from the picker: [Seam](/help/facility-operators/access-controls/connect-a-seam-lock), [RemoteLock](/help/facility-operators/access-controls/connect-remotelock), [Rhombus](/help/facility-operators/access-controls/connect-rhombus), or [UniFi Access](/help/facility-operators/access-controls/connect-unifi). 4. **Re-map each lock to its space** — mappings don't carry over. 5. **Re-set your per-lock rules** (who can unlock, and when). 6. **Test one booking end-to-end** to confirm codes reach the hardware. **Existing future bookings do not get new codes on their own.** Codes are issued when a booking is created or changed — connecting a provider and mapping spaces doesn't sweep back over bookings that already exist. So every booking made *before* the switch is left without a code, silently. Re-save each affected upcoming booking (opening and saving it re-issues the code), or contact [OpenCourt support](mailto:support@getopencourt.com) to re-issue them in bulk. If you have more than a handful, ask us — don't click through them one by one. If something goes wrong [#if-something-goes-wrong] * **Disconnect fails or errors** — a temporary connection issue. Try again shortly; if it keeps failing, contact OpenCourt support. * **Customers report lost door codes after disconnecting** — expected. Disconnecting revokes every active code immediately (see the timing note above). Re-save their bookings or contact support to re-issue codes. * **You disconnected but only wanted a pause** — reconnect the provider, then re-map locks and rules. Next time, switch the **mode** to *Off* instead — it keeps your connection and mappings. Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Connect a Seam lock to your club](/help/facility-operators/access-controls/connect-a-seam-lock) * [Connect RemoteLock to your club](/help/facility-operators/access-controls/connect-remotelock) * [Connect Rhombus to your club](/help/facility-operators/access-controls/connect-rhombus) * [Connect UniFi Access to your club](/help/facility-operators/access-controls/connect-unifi) # Door codes day-to-day: view, share, and manage access codes Your locks are connected and mapped — this is the day-to-day: where to find any booking's door code, what the customer already sees, and what happens to a code when a booking is cancelled, rescheduled, or moved. *(For admins and front desk. No setup needed if your locks are already connected.)* Access codes live under **Settings → Access Controls** in the admin app, on the **Access Codes** tab. If you haven't connected a lock yet, start with [How access controls work in OpenCourt](/help/facility-operators/access-controls). Where do I see a booking's door code? [#where-do-i-see-a-bookings-door-code] **Front-desk answer first.** Go to the **Access Codes** tab. It lists every code across all your locks, with a link from each row's **Event** to the booking it belongs to. The Access Codes table listing door codes with their lock, booking, space, validity window, and status. There's a search box above the table, so you can type a customer's event name or a code to jump straight to it. * **Per lock.** Open a lock from the **Locks** tab and scroll to **Lock Activity** for just that lock's history. A lock with no codes shows *No access codes have been generated for this lock.* Each code is created the moment a booking is made on a lock-mapped **space** — your courts, bays, or fields. By default it's active from **30 minutes before** the start to **10 minutes after** the end — if you've customized a lock's access rule, the window follows that rule instead. The **Status** column tells you where each code stands: | Status | Meaning | | ------------ | ------------------------------------------------------------------------------------- | | **Active** | The code works right now. | | **Upcoming** | Issued, but its window hasn't started yet. | | **Expired** | The booking window has passed; the code no longer works. | | **Revoked** | The code was removed (for example, the booking was cancelled). | | **Unknown** | OpenCourt can't confirm the code's state on the lock — check that the lock is online. | On **Native** scheduling, a far-future booking may show a code that isn't on the lock yet — the real PIN appears once it's pushed (\~72 hours ahead; **Just-in-Time** pushes \~60 minutes ahead). This is expected. See [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling). What does the customer see? [#what-does-the-customer-see] Sharing is automatic — you don't send codes out. The customer already has theirs: * **In the app**, on the booking or event details as **Access code**. Before it's usable they see *Access code will be available at \{time}*. It also appears in their reservations list. * **In email** — the booking confirmation, event-joined, and event-reminder emails each include a line **Access Code: \{code}**. So the front desk only needs the **Access Codes** table for walk-up questions ("what's my code?") — everyone who booked already received theirs. What happens when a booking changes? [#what-happens-when-a-booking-changes] | Change | What happens to the code | What the customer gets | | ----------------------------------- | -------------------------------------- | ---------------------------------------------- | | **Cancelled** | Code is revoked. | Their code stops working. | | **Rescheduled** (same space/door) | **Same PIN**, validity window updates. | No new code to learn. | | **Moved to a different space/door** | Old code revoked, **new code issued**. | An automatic *Your access code changed* email. | | **Extended** | **Same PIN**, window extends. | No new code to learn. | Using manual codes without a smart lock [#using-manual-codes-without-a-smart-lock] You don't need a smart lock to hand out door codes. Under **Settings → Access Controls**, the access-code mode has three options: * **Off** — *No access codes are used.* * **Manual Access Codes** — *Set access codes manually on events and reservations. A default code can auto-apply to court bookings.* Pick this and fill the required **Default access code for court bookings** field (for example *1234*). Every booking then shows that code. (Those labels follow your club's wording — "bay" or "field" instead of "court" where that applies.) * **Smart Lock (\{provider})** — the connected-lock behavior described above, with per-booking generated codes. In **Manual Access Codes** mode, any single event or reservation can override the default in its own **Access Code** field. Manual codes have **no time window** — they're always shown to participants, not just around the booking. Switching away from Smart Lock mode warns: *New events and reservations will no longer generate \{provider} access codes. Existing codes on previously created events will remain active. Your \{provider} connection and lock mappings will be preserved if you switch back.* Nothing is lost — you can switch back later. The activity trail [#the-activity-trail] * **Per lock.** Each lock's page has a **Lock Activity** section showing the last 2 days by default. * **Club-wide.** The **Unlock History** tab logs every event with **Date & Time**, **Lock**, **Action**, **Method**, and **Details**. The Unlock History table listing door events with their lock, action, method, and linked details. * **Club activity log.** **In-app** unlock attempts — successes and failures — also land in the club activity log with the customer's name, because the customer was signed in when they tapped. Keypad entries can't be attributed to a person: a door code is shared by everyone on the booking, so it identifies the booking. Reading the Method column [#reading-the-method-column] **Method** is the most useful column, because it tells you *how* the door opened — and that determines whether there's a booking to trace it back to. | Method | What happened | Can you tie it to a booking? | | ------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | **Keycode** | Someone typed a code on the keypad. | Usually yes — **Details** shows the code and links to the event, e.g. *1027 · Coach-Led Drill Sessions*. | | **Manual** | The door was operated by hand — a thumb-turn from the inside, a key, or the lock's own button. | No. There's no code involved, so **Details** just reads *Reported by lock*. | | **Auto-lock** | The lock re-locked itself after its timer. | No — it's the lock's own housekeeping. | | **Unknown** | The lock reported a change without saying how. | No. | So a run of **Manual** unlocks isn't a mystery — that's people leaving through the door from inside. When you need to know *who*, look for **Keycode** rows: those are the ones that carry a code and a linked booking. Not every Keycode row resolves to a code. If someone typed a code the lock knows but OpenCourt didn't issue — a staff code you set up directly on the hardware, for example — you'll see **Keycode** with *Reported by lock* and no link. That's expected, and a useful signal that a non-OpenCourt code is in circulation. If something goes wrong [#if-something-goes-wrong] * **A customer says their code doesn't work** — check the row's **Status** and the lock's Lock Activity, then see [When a customer can't get in](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in). * **The code shows but isn't on the lock yet** — this is the Native "not-yet-materialized" case; the deep triage is in [When a customer can't get in](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in). * **The unlock history shows "Access Denied" or "Access Code Failed"** — start the diagnosis in [When a customer can't get in](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in). Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling) * [When a customer can't get in](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in) * [Set who can unlock doors from the app](/help/facility-operators/access-controls/set-who-can-unlock-doors) # Make your UniFi console reachable (Cloudflare Tunnel) {/* Publishing decision (2026-08-01, Alex): ship without screenshots, ahead of the first real UniFi customer. Steps are written from Cloudflare's CURRENT docs — Networking > Tunnels, then the tunnel's Routes tab > Add route > Published application. Cloudflare retired the old Zero Trust > Networks nav and the separate Public Hostnames tab, so any older draft of this page (or guide found online) is stale. NOT walked end-to-end against a live console. Add dashboard screenshots at the first customer install. */} UniFi Access runs entirely on your own console — there is no UniFi cloud for door control — so OpenCourt needs a way to reach that console over the internet. **There are two supported ways to provide it, and this guide covers one of them.** A **Cloudflare Tunnel** works like this: a small helper makes an **outbound-only** connection to Cloudflare, and Cloudflare gives OpenCourt a normal `https://…` address that points back to your console — **no ports opened on your firewall, no static IP needed, and it works behind CGNAT**. *(About 30 minutes, one time.)* **A port-forward is the way we recommend, and it's simpler — read [How OpenCourt reaches your console](/help/facility-operators/access-controls/connect-unifi#how-opencourt-reaches-your-console) before following this guide.** A port-forward takes about 10 minutes, adds no extra equipment, and has nothing that can silently stop running. This guide is the right one only in specific cases. **Use the tunnel if:** your ISP uses **CGNAT** (a port-forward is then impossible), you can't get a static IP and don't want to rely on DDNS, your security policy forbids an open inbound port, or you already run an always-on NAS or server and are happy maintaining it. ⚠️ **The main trade-off:** a tunnel needs **a computer that is always on** running the helper program. If that machine reboots without restarting it, or its drive fails, **door codes stop working and nothing looks like it changed.** A port-forward has no such component. Only choose this path if someone will notice when that box goes down. This is a networking task, not an everyday admin task, and **OpenCourt doesn't do the install** — if your club has a **UniFi installer or IT person**, hand it to them; it's quick for anyone who works with UniFi gear. You're welcome to share this guide with them. Before you start, make sure the console is **not** enrolled in **UniFi Identity Enterprise** — that mode turns off the local API OpenCourt connects to, and no amount of tunnelling will get around it. Use standard UniFi Access. (If it's already on Identity Enterprise, move it back to standalone UniFi Access first.) Before you begin [#before-you-begin] * A **Cloudflare account** (the free plan is fine) with a **domain managed in Cloudflare**. If you don't have a domain on Cloudflare yet, add one — a cheap domain works; you point its nameservers at Cloudflare. Without a domain in Cloudflare there's nothing to attach the tunnel to. * One **always-on device on the same network as the console** to run the tunnel helper (`cloudflared`) — a NAS that runs Docker, a small always-on mini-PC, or a Raspberry Pi. **Don't install it on the UniFi console itself** — that setup gets wiped by firmware updates. Use a separate little box. * Your **console's local IP address** (for example `192.168.1.10`), from your UniFi network settings. Steps [#steps] 1. In the Cloudflare dashboard, go to **Networking → Tunnels** and select **Create Tunnel**. Choose **Cloudflared**, and give it a name that says what it's for — `opencourt-access` works. 2. Cloudflare shows a **one-line install command with a token**. Run it on your always-on device — pick your operating system and it generates the exact command; the **Docker** one is usually easiest. Within a few seconds the tunnel appears on the Tunnels page with a **Healthy** status. 3. Open the tunnel, go to its **Routes** tab, and select **Add route → Published application**. Fill it in like this: | Field | Value | | ----------------------- | ----------------------------------------------------------------------------------------------------------------- | | **Hostname** | a subdomain plus your Cloudflare domain — for example `access` + `yourclub.com`, giving you `access.yourclub.com` | | **Service URL** | your console's local address **with port 12445** — for example `https://192.168.1.10:12445` (use your real IP) | | **TLS → No TLS Verify** | **On**, in the route's additional/origin settings | **No TLS Verify** matters and it's the step people skip. The UniFi console presents a self-signed certificate, so without it Cloudflare refuses that last hop and the connection fails. It only affects the hop *inside your own network* — the tunnel → Cloudflare → OpenCourt path stays fully encrypted, and OpenCourt verifies the certificate on its end. 4. **Save.** Your console is now reachable at `https://access.yourclub.com`. That hostname is what you paste into OpenCourt's **Console address** field — choose **Cloudflare Tunnel** as the connection type. Continue with [Connect UniFi Access to your club](/help/facility-operators/access-controls/connect-unifi). Cloudflare reorganized this part of its dashboard, and older guides you'll find online say **Zero Trust → Networks → Tunnels** with a separate **Public Hostnames** tab. If your account still shows that layout, the settings are identical — it's the same tunnel, just reached a different way. Keep it running [#keep-it-running] The little box running the tunnel must **stay powered on**. If it sleeps or loses power, the tunnel drops and OpenCourt can't sync door codes until it's back — so use an always-on NAS or mini-PC, not a laptop that sleeps. (A UPS on that device and the console keeps everything online through short power blips.) The port-forward alternative [#the-port-forward-alternative] If you'd rather not run a tunnel, you can instead **forward the console's port 12445** to the internet and give OpenCourt that address (for example `https://your-public-host:12445`), choosing **Direct / port-forward** when you connect. This needs a **static (or otherwise stable) public IP** so the address OpenCourt connects to doesn't change. OpenCourt pins the console's certificate on first connect. This works, but it opens a port on your firewall, so a tunnel is the recommended, safer option. If something goes wrong [#if-something-goes-wrong] * **The tunnel shows "Down" or "Degraded" in Cloudflare** — the `cloudflared` helper isn't running. Make sure the device it's installed on is powered on and the container/service is up, then re-check. * **OpenCourt says it can't reach the console** — check the route's three settings: the **Service URL starts with `https://`**, it **ends with `:12445`**, and **No TLS Verify is on**. Those are the usual culprits, and a missing No TLS Verify is the most common of the three. Also confirm the console itself is online on the local network. * **Everything's connected but connect still fails** — double-check the console isn't on **UniFi Identity Enterprise** (it disables the local API), and that your API token has all the required scopes. See [Connect UniFi Access to your club](/help/facility-operators/access-controls/connect-unifi#if-something-goes-wrong). Related [#related] * [Connect UniFi Access to your club](/help/facility-operators/access-controls/connect-unifi) — the next step, once the console is reachable. * [How access controls work in OpenCourt](/help/facility-operators/access-controls) # Home Assistant {/* Updated 2026-09-28: outbound webhooks now ship (OC-3238, docs/webhooks.md "Outbound"). A club subscribes its own URL under Settings → API → Webhooks and receives signed `booking.created` / `booking.updated` / `booking.canceled` POSTs whose `data.object` is the /v1 booking DTO (start, end, spaces[] in club time). The API section is gated on the club's `api_surface` setting (default hidden), so the copy routes clubs through support to switch it on. What is still NOT built, and why the page keeps a "what's next" section: there is no timed signal AT a booking's start or end (no `booking.started` / `booking.ended` in services/SignalService/registry.ts), and no guided Home Assistant setup. Today HA has to schedule its own automations from the start/end times in the payload. The "no port forwarding" claim rests on Home Assistant Cloud (Nabu Casa) CLOUDHOOKS — a public https URL that relays to a local HA webhook trigger. Self-hosted HA without Nabu Casa needs a tunnel, same as UniFi Access. Deliveries do not carry a signature HA's webhook trigger can check, so the webhook URL itself is the secret. Direction of control is a deliberate product call (Alex, 2026-08-02): we EMIT booking facts, the club's automation decides what happens. Do not rewrite this page into "OpenCourt controls your lights". Vercel is serverless — a persistent WebSocket per club's HA instance is architecturally off the table. Webhooks and polling are the only viable shapes. Don't let a future draft promise live bidirectional sync. */} **Home Assistant** is open-source home and building automation that runs on a small box at your venue. It speaks Zigbee, Z-Wave, Matter, Wi-Fi and most things in between, which makes it the usual choice for clubs that want lights, heating, ventilation and everything else on one system they control. With OpenCourt **webhooks**, your **bookings** can drive it. **You can connect Home Assistant today with webhooks.** There's no dedicated Home Assistant setting yet — you point an OpenCourt webhook at a Home Assistant webhook trigger and build your automations on it. Why it suits a club that runs unstaffed [#why-it-suits-a-club-that-runs-unstaffed] A venue open 24/7 with nobody on site has the same problem in every room: the building doesn't know when anyone is coming. Lights burn all night or a customer walks into a dark bay. Heating runs for an empty building or the first booking of the day starts cold. OpenCourt already knows exactly when every space is occupied, because that's the booking calendar — the same information that issues a door code. Home Assistant already knows how to switch anything you've connected to it. The integration is the wire between the two. Your automations stay yours [#your-automations-stay-yours] The design is deliberate: **OpenCourt sends the facts, your Home Assistant decides what happens.** OpenCourt tells Home Assistant when a booking is made, changed or canceled, with its spaces and its start and end times. What that triggers is entirely yours to write. That matters for three reasons: * **Home Assistant is better at automation than a booking platform will ever be.** You get its full engine, not the handful of toggles we'd have built. * **Nothing is locked to us.** Your automations are yours, in your config, running on your hardware. * **You set the safety margins.** How long lights stay on after a booking ends, what happens when someone's still in the building, which circuits are off-limits — those are decisions for the club, not for us. If you automate anything that cuts power — lighting especially — build in a grace period and a manual override at the wall. A space can still be occupied after a booking's end time, and no schedule knows that as well as a person standing in the room. What clubs ask us for [#what-clubs-ask-us-for] The kinds of automation this opens up, based on what operators already tell us they want: * **Lighting** per bay, court or studio, on before arrival and off after the last booking. * **Heating and cooling**, either through Home Assistant or through our own [thermostat control](/help/facility-operators/access-controls/thermostat-control). * **Ventilation and extraction** in enclosed simulator bays. * **Screens, projectors and audio** powered down overnight. * **Alarm arming** once the last booking of the day has ended. We're not promising any specific one of these — what OpenCourt provides is the booking signal. Which devices respond to it is a question for your Home Assistant setup. Connect it with webhooks [#connect-it-with-webhooks] A **webhook** is a message OpenCourt sends to an address you choose each time something changes at your club. Home Assistant can receive these through a **webhook trigger** in an automation. 1. **Ask us to turn on the API section.** Webhooks live in the OpenCourt API settings, which are off for a club by default. Email [support@getopencourt.com](mailto:support@getopencourt.com) to switch them on. 2. **Create a webhook trigger in Home Assistant** and copy its address. The address must use `https` and be reachable from the internet. 3. **In OpenCourt, go to Settings → API → Webhooks** and click **Add endpoint**. 4. **Paste the address into Endpoint URL.** Under **Signals to send**, select `booking.created`, `booking.updated` and `booking.canceled`. OpenCourt selects the **Bookings** access they need for you. 5. **Click Add endpoint** to save it. The **Webhook deliveries** panel on the same page shows every message sent, and lets you send a test. Each message carries the booking's spaces and its start and end times in your club's timezone. Your automation uses those times to schedule what happens — for example, lights on 10 minutes before the start time and off 15 minutes after the end time. When a booking moves or is canceled, a new message arrives so your automation can change or remove what it scheduled. Anyone who knows a Home Assistant webhook address can call it. Keep the address private, and don't share it in screenshots or support tickets. Without opening your firewall [#without-opening-your-firewall] **Home Assistant Cloud** (Nabu Casa) can give a webhook trigger a public address that forwards to your local instance — no ports opened, no static IP, no tunnel to maintain. A self-hosted instance with no such address needs to be reachable another way, in the same shape as [making a UniFi console reachable](/help/facility-operators/access-controls/expose-unifi-console-cloudflare-tunnel). Home Assistant's own documentation covers the webhook trigger settings that allow an outside service to call it. Or poll the API [#or-poll-the-api] A club comfortable with Home Assistant configuration can also read bookings from OpenCourt's **read-only REST API**. Its schedule endpoint returns every booking with its spaces and start and end times in your club's timezone. The API is in preview, so response shapes can still change — worth knowing before you build something you depend on. What's next [#whats-next] Today OpenCourt sends a message when a booking is made, changed or canceled — not at the moment a booking starts or ends, so Home Assistant does the timing. We're looking at messages sent at the start and end of a booking, and a simpler setup made for Home Assistant. Register your interest [#register-your-interest] Tell us you run Home Assistant and we'll factor your setup into what we build next. * Email [support@getopencourt.com](mailto:support@getopencourt.com), or * Talk to your OpenCourt account representative. Useful things to mention: whether you run Home Assistant Cloud or self-host, what you'd want a booking to trigger, and whether your venue operates unstaffed. Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Thermostat control (coming soon)](/help/facility-operators/access-controls/thermostat-control) * [Which locks work with OpenCourt](/help/facility-operators/access-controls/choose-a-lock-for-your-club) # How access controls work in OpenCourt (overview) Access controls connect your club's smart locks to OpenCourt so every booking gets its own **door code** automatically — and, on supported locks, so customers can unlock the door from the OpenCourt app. This page explains how it fits together; the linked guides cover each task. Access controls live under **Settings → Access Controls** and are available to admins with access-control permission. What you get [#what-you-get] * **Automatic door codes.** Map a lock to a **space** (your courts, bays, or fields) and every booking on it gets a code that works only for that booking's time window, then disappears. Nothing to hand out or revoke. * **In-app unlock.** When you enable it, customers can unlock a mapped door from the OpenCourt app during their booking (see [Unlock a door from the app](/help/players/access-controls/unlock-a-door-from-the-app)). * **NFC/BLE proximity keys.** Clubs on a Salto KS access-control system can grant phone-based access — the customer holds their phone to the reader instead of typing anything. * **A full activity trail.** Every code, every unlock attempt, and every change is visible in your admin panel. Everything OpenCourt creates is **added alongside** your lock's existing setup — your staff codes, fobs, and cards keep working, and OpenCourt only ever removes the codes it created. Which provider do I connect? [#which-provider-do-i-connect] You connect **one** provider, and they come in two kinds. **Multi-brand platforms — Seam and RemoteLock.** Both sit in front of dozens of lock brands, so you connect by signing in to the account you already have with them. Between them they cover the consumer and light-commercial locks most clubs buy (August, Yale, Schlage, Kwikset, Lockly and more), and Seam additionally reaches professional access-control systems: **Salto KS**, **Brivo**, and **Avigilon Alta** (formerly Openpath). If you already have an account with either platform, use that one. **Single-system integrations — Rhombus and UniFi Access.** Pick these when you already run that specific system at your venue: Ubiquiti gear on the wall (Dream Machine, UniFi Access hubs and readers) means **UniFi Access**; doors managed at console.rhombus.com means **Rhombus**. | | **Seam** | **RemoteLock** | **Rhombus** | **UniFi Access** | | ---------------------- | ------------------------------ | ------------------------------ | ----------- | ----------------------- | | Door codes | Yes, on supported locks (most) | Yes, on supported locks (most) | — | Yes | | In-app unlock | Yes, on supported locks (most) | Yes, on supported locks (most) | Yes | Yes | | NFC/BLE proximity keys | Via Salto KS | — | — | — | | Connect with | Account sign-in | Account sign-in | API key | Console address + token | A couple of details behind that table: **Rhombus is unlock-only** — it never issues door codes. **RemoteLock and UniFi Access generate the code themselves** rather than OpenCourt generating it. And on **UniFi Access**, in-app unlock only works on doors bound to a hub. UniFi Access runs on **your own console** rather than a vendor cloud, so it needs one extra setup step — making the console reachable from the internet. See [Connect UniFi Access to your club](/help/facility-operators/access-controls/connect-unifi). Before you buy a lock, check it works with OpenCourt. See [Which locks work with OpenCourt](/help/facility-operators/access-controls/choose-a-lock-for-your-club), and send us the exact model — we'll confirm whether it does PIN door codes, in-app unlock, and NFC/BLE proximity keys. Build quality and installation are questions for the vendor or your installer, not us. How a door code reaches the lock [#how-a-door-code-reaches-the-lock] 1. You connect a provider and **map each lock to a space**. 2. A customer books that space. 3. OpenCourt creates a code for the booking and schedules it onto the lock. 4. The code activates for the booking window, then is removed. Codes are **time-limited**: by default they become active **30 minutes before** the reservation starts and expire **10 minutes after** it ends (each lock's access rule can adjust this). Exactly *when* the code is loaded onto the hardware depends on your scheduling mode — see [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling). Start here [#start-here] **Set up** * **[Which locks work with OpenCourt](/help/facility-operators/access-controls/choose-a-lock-for-your-club)** — check a model before you buy: brands and systems, and what each one supports. * **[Connect a Seam lock](/help/facility-operators/access-controls/connect-a-seam-lock)** — the most common setup. * **[Connect RemoteLock](/help/facility-operators/access-controls/connect-remotelock)** · **[Connect Rhombus](/help/facility-operators/access-controls/connect-rhombus)** · **[Connect UniFi Access](/help/facility-operators/access-controls/connect-unifi)** * **[Set who can unlock doors from the app](/help/facility-operators/access-controls/set-who-can-unlock-doors)** — turn on in-app unlocking and set per-lock rules for who can unlock and when. * **[Salto KS mobile access](/help/facility-operators/access-controls/salto-ks-mobile-access)** — phone-based access on a Salto KS system. **Run it day-to-day** * **[Door codes day-to-day](/help/facility-operators/access-controls/door-codes-day-to-day)** — look up any booking's code, see what customers see, and what happens when bookings change. * **[Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling)** — code timing, and Just-in-Time vs Native scheduling for high-volume clubs. * **[Home Assistant](/help/facility-operators/access-controls/home-assistant)** — send bookings to Home Assistant with webhooks, so they drive lights, HVAC and whatever else you automate. **When something's wrong** * **[A customer can't get in — troubleshoot door access](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in)** — the triage guide for the front desk. * **[Disconnect or switch your lock provider](/help/facility-operators/access-controls/disconnect-or-switch-lock-providers)** — what disconnecting does, and how to change systems safely. **Coming soon** * **[Kisi access control](/help/facility-operators/access-controls/kisi-access-control)** — an integration we're building for clubs already running Kisi. * **[Thermostat control](/help/facility-operators/access-controls/thermostat-control)** — heating and cooling that follows your bookings. Common questions [#common-questions] Is every door code unique? [#is-every-door-code-unique] Yes — every booking gets its own code, and it only works during that booking's window. The thing to know is that a code belongs to the **booking**, not to one person: * **A space reservation** gets one code. Whoever booked it and everyone playing with them all see the same code. * **An event or program** gets one code for that session, which every registered participant sees. That's deliberate: nobody gets stuck outside because they weren't the one who made the booking. By default a code works from 30 minutes before until 10 minutes after, following the window your club set on that lock — outside it, the code does nothing. What happens if the internet goes down? [#what-happens-if-the-internet-goes-down] Codes that already reached the lock keep working — they're stored on the lock itself, so customers can still type them in. New codes can't reach the lock until it's back online, and in-app unlock needs the lock online. If outages are common at your facility, prefer Native scheduling (codes load \~72 hours ahead) — see [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling). Do our existing keys, fobs, and staff codes keep working? [#do-our-existing-keys-fobs-and-staff-codes-keep-working] Yes. OpenCourt adds booking codes alongside whatever your lock system already has, and only ever removes the codes it created. Can I see which codes were used, and when? [#can-i-see-which-codes-were-used-and-when] Yes. Every lock has its own activity feed, and the club-wide **Unlock History** logs each door event with the time, the lock, what happened, and the method. What it ties back to depends on how they got in: * **Someone typed a door code** — you see the code and the booking or event it belongs to, like *1027 · Coach-Led Drill Sessions*. Because that code is shared by everyone on the booking, it identifies the **booking, not the individual**. * **Someone unlocked from the app** — they were signed in, so this one is attributable. Your club's activity log records the attempt by name, successful or not. * **The door was opened by hand** from the inside, or re-locked on its timer — there's no code involved, so there's nothing to attribute. See [Door codes day-to-day](/help/facility-operators/access-controls/door-codes-day-to-day). Do customers need a separate app for the door? [#do-customers-need-a-separate-app-for-the-door] No. Codes and in-app unlock live in the same OpenCourt app they book with. The one exception is NFC/BLE proximity keys (Salto KS), which must use your club's branded app. # Kisi access control (coming soon) {/* This page is a COMING-SOON placeholder published for SEO/AI-search reach (2026-08-02, Alex's call). Nothing is built: Kisi appears only as a future case in webhookRouter.ts. Deliberately does NOT promise per-booking keypad PINs: Kisi has no Seam-style typed PIN credential (two_factor_pin is deprecated and card-bound). The mechanism described here is Kisi's ACCESS LINKS API, re-verified against docs.kisi.io on 2026-08-02: create a link scoped to a group with a validity window, pass quick_response_code_type, and the response carries a QR image you can render in your own app. Two hard constraints kept in the copy: (1) QR scanning needs a Kisi Terminal Pro / QR scanner, NOT a bare Reader Pro; (2) Kisi's own docs say digital credentials BYPASS geofence and reader-proximity restrictions and recommend them for short-term / low-security doors — fine for booking-length access, but a deliberate call to make when this gets scoped. Nothing is built. */} **Kisi** is a cloud-based access-control platform used by gyms, coworking spaces, and clubs to manage doors, readers, and who can open what. If your facility runs Kisi, you're in the right place — OpenCourt is building an integration. **This integration isn't live yet.** This page exists so you can find it and register interest. Nothing below is available in your admin panel today. How it would work [#how-it-would-work] Kisi doesn't use typed keypad PINs the way most smart locks do. Instead it issues **access links** — and that turns out to suit bookings well. For each booking, OpenCourt would ask Kisi for an access link scoped to your doors, valid only for that booking's window. Kisi returns a **QR code** and a web link, and the access **expires on its own** when the window closes. Same idea as a door code: issued automatically, scoped to the right door, nothing to revoke. Your customer would see the **QR code inside the OpenCourt app**, on the booking — right where a door code appears today — and scan it at the reader on arrival. No Kisi app to download, no separate login. Scanning needs a **Kisi Terminal Pro** (or another Kisi QR code scanner). A Kisi Reader Pro on its own reads cards and mobile credentials but not QR codes, so check what's on your doors before planning around this. We're also looking at unlocking straight from the app, the way our other providers work, so a customer could tap **Unlock** instead of scanning. Which of these ships first depends on what we hear from clubs actually running Kisi. Who this is for [#who-this-is-for] Clubs that already run Kisi and don't want to replace it. If you're choosing an access-control system now and door access through OpenCourt is important to you, one of our [current integrations](/help/facility-operators/access-controls/choose-a-lock-for-your-club) will serve you sooner. In the meantime [#in-the-meantime] Kisi keeps working exactly as it does now — this integration would connect it to OpenCourt, not replace it. Until then, you can still use OpenCourt's [manual access codes](/help/facility-operators/access-controls/door-codes-day-to-day) to show a door code on bookings without any lock integration at all. Register your interest [#register-your-interest] Tell us you're on Kisi and we'll factor your setup into the build, then let you know when it's ready. * Email [support@getopencourt.com](mailto:support@getopencourt.com), or * Talk to your OpenCourt account representative. Useful things to mention: how many doors, which Kisi readers you use, and whether you need access tied to individual bookings or just general member access. Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Which locks work with OpenCourt](/help/facility-operators/access-controls/choose-a-lock-for-your-club) * [Door codes day-to-day](/help/facility-operators/access-controls/door-codes-day-to-day) # Salto KS mobile access If your club runs a **Salto KS** access-control system, OpenCourt can connect to it so customers get **phone-based (mobile key) access** instead of typing a keypad code. Their phone becomes the key: they hold it near the lock and the door opens. The initial connection is set up by the OpenCourt team. Once it's in place, **you assign and revoke keys yourself**, customer by customer — that's the part you'll do day to day, and it's covered below. You need access-control permission to assign keys. Customer profiles live under **Customers** in the admin panel. Getting Salto KS connected [#getting-salto-ks-connected] Linking your Salto KS system to OpenCourt is a one-time setup we handle for you. It involves matching your Salto **site**, **credential manager**, and **access group** to your club, and we do it this way because getting those three wrong sends keys to the wrong doors. To get started, [contact OpenCourt support](mailto:support@getopencourt.com) with: * **Confirmation that your Salto KS account is linked to Seam.** OpenCourt reaches Salto through Seam, so this has to exist first — your Salto administrator or installer can set it up. * **Which Salto site** this club corresponds to, if your Salto account covers more than one location. * **Which access group** customers should be placed in. This is the group that governs which doors they can open and when, so pick the one you'd put a regular member in. If you're not sure about the last two, loop in whoever administers your Salto KS account — they'll know. We'll confirm once it's live, and from that point everything below is yours to run. Grant mobile keys per customer [#grant-mobile-keys-per-customer] Mobile keys are assigned **per customer**, not automatically to everyone. Open the customer's admin profile and go to the **Access Controls** tab. The **Digital Keys Access** card — "Manage whether this user can receive Seam mobile key credentials for this club." — shows their status as **Assigned** or not, and gives you **Assign** or **Revoke**. The Access Controls tab on a customer's admin profile, showing the Digital Keys Access card with an Assigned status and a Revoke button. The card states the prerequisite plainly: "To assign digital keys, the user must have a complete profile: first name, last name, and a valid email address." If **Assign** doesn't work, that's almost always why — fix the profile on the **Details** tab first. Mobile keys are presented in your club's **branded mobile app**, so customers need a recent version installed. Do you still want door codes? [#do-you-still-want-door-codes] Mobile keys and per-booking door codes are **independent**. You can run either or both: * **Mobile keys only.** The access-code mode stays **Off**. Nobody gets a PIN; access is entirely phone-based through the keys you assign. This is the common shape for clubs on Salto KS. * **Both.** The mode is set to **Smart Lock**, so bookings also generate keypad codes, and mobile keys go to the people you assign them to — members and staff, typically, with drop-ins falling back to a code. Tell us which you want when we set the connection up, and mention it any time you want to change. It matters more than it sounds: with codes **Off**, a customer without an assigned mobile key has **no way in**. What the customer sees [#what-the-customer-sees] On a mobile-key setup, the customer taps **Door access** on your club's home screen and a **Club access** screen opens **inside your club's own branded app** — a native screen, not a separate Salto app and nothing extra to download. Their phone *is* the key, so they hold it near the lock instead of typing a code. The Club access screen in a club's branded app, telling the customer to hold their phone close to the lock while it searches for it. **Tell your front desk this one thing:** the screen says *"Hold your phone close to the lock. It may take 5–10 seconds to open,"* and while it works it shows *Looking for the lock…* and sometimes *Retrying unlock…* That searching phase is normal Bluetooth behavior, not a failure. Customers who walk away after two seconds will report the door as broken when nothing is wrong. This is the one part of OpenCourt door access that requires your **branded mobile app**. Customers on the web app won't see it. See [Getting through the door at your club](/help/players/access-controls/unlock-a-door-from-the-app). Access stays centralized in your Salto KS platform: OpenCourt grants and revokes membership in the access group you nominated, rather than running a parallel system. Which doors that group opens is defined in Salto, so it's Salto — not OpenCourt — that decides where a key works. If something goes wrong [#if-something-goes-wrong] * **A customer doesn't get a mobile key** — check their **Access Controls** tab shows **Assigned**, and that their profile has a first name, last name, and valid email. Then confirm they're on a current version of your club's app. * **The key screen sits on "Looking for the lock…"** — that's Bluetooth searching, and it can take 5–10 seconds. Have them hold the phone within a few inches of the reader with Bluetooth on, and wait. If it never connects, check the lock has power and is online in Salto. * **A customer can't find Door access in the app** — mobile keys only appear in your club's **branded mobile app**. On the web app there's nothing to show. Confirm they've installed the app and are signed in. * **You need to change which doors customers can open** — that's governed by the Salto **access group** we mapped, so it's changed in Salto KS, not in OpenCourt. Adjust the group's permissions in your Salto console, or [contact us](mailto:support@getopencourt.com) if you want customers moved to a different group. Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Connect a Seam lock to your club](/help/facility-operators/access-controls/connect-a-seam-lock) * [Unlock a door from the app](/help/players/access-controls/unlock-a-door-from-the-app) # Set who can unlock doors from the app Decide whether customers can open a door straight from the OpenCourt app, and set per-lock rules for **who** can unlock and **when**. *(About 5 minutes. You'll need admin access and a lock that supports remote unlock.)* You need access-control permission. Everything here lives under **Settings → Access Controls**. Per-lock **Door access** rules are available on **Seam**, **Rhombus**, and **UniFi Access** locks that support remote unlock. RemoteLock manages its own codes, so a RemoteLock lock shows only the **door code window** settings — not the who-can-unlock rules. **Customers can still unlock a RemoteLock door from the app** once you turn on in-app unlocking below — it just uses the standard reservation window (30 minutes before until 10 minutes after) for everyone, with no per-role overrides. In-app unlocking is separate from door codes: a customer might get in with a **door code**, with **in-app unlock**, or both. This page covers in-app unlock — but a lock's **default** rule also sets the **door code window** (when a booking's code activates and expires), so that one timing applies to both. Overrides don't; see the callout under "Per-lock access rules" below. There are two layers of control. 1. The club-wide switch [#1-the-club-wide-switch] Go to **Settings → Access Controls** and find the **Remote Unlock** section — "Allow users to unlock doors from the club home page. Access rules on each lock control who can unlock and when." Switch it to **Enabled**. While it's **Disabled**, customers never see an unlock option — they use their door code instead. The Remote Unlock section on the Access Controls settings page, switched to Enabled. The Remote Unlock section only appears once you're in Smart Lock mode with at least one connected lock that supports remote unlock. 2. Per-lock access rules (who, and when) [#2-per-lock-access-rules-who-and-when] Open a lock from the **Locks** tab and find its **Door access** section — "Control who can unlock this lock from the app and when — and, for locks with door codes, when a booking's code is active." Start with the **Default access rule**. It offers three modes: | Mode | What the customer can do | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- | | **Can always unlock** | Unlock whenever, regardless of bookings. | | **During reservations only** | Unlock only around their booking — set **min before** and **min after** (default: **30 min** before to **10 min** after). | | **No app unlock** | No in-app unlock for this rule set. | Below that, **Overrides (optional)** lists your rule sets — **Non-members** plus each rule set you've created (for example *Coach* or *Legacy Member*). Each row shows **Using default** until you switch it on, at which point it reads **Custom rule** and opens the same three modes. So you might leave the default at *During reservations only* and give coaches *Can always unlock*. The Door access section of a lock's page, showing the default access rule and a Non-members override set to a custom rule. **The default rule and an override do different things to door codes.** * On the **Default access rule**, the min before / min after window sets *both* the app-unlock window and when a booking's **door code** goes active. The page says so: "These minutes also set when a booking's door code is active." * On an **override**, the minutes move *only* the app unlock button: "Applies to the app unlock button only — door codes always use the default window above." So if you want a group's **door code** to activate earlier, change the **default** window. Widening an override won't do it. When you switch an override to **Custom rule** it starts out matching the default — same mode, same minutes. That's a starting point, not a link: change it and it stays changed, and later edits to the default won't flow through. If the effective rule for non-members works out to **Can always unlock**, the page raises an amber alert: **"Non-members can unlock this door anytime."** — *"With this rule, anyone signed in to OpenCourt who isn't a member of your club can unlock this door at any time — no booking required. If you only meant members, set the Non-members override below to 'During reservations only' or 'No app unlock'."* Only leave it that way on purpose. The separate "Door code window" field [#the-separate-door-code-window-field] If you set the default rule to **Can always unlock** or **No app unlock**, the min before / min after fields disappear — but a code-capable lock still needs to know when a booking's code is live. So a separate **Door code window** box appears, captioned "When a booking's keypad code is active, relative to the reservation." Set it there instead. **Code-only locks show just this box.** A keypad with no remote unlock, or a RemoteLock lock that pushes its own codes, has no app-unlock policy to configure — the whole **Door access** section collapses to the **Door code window** field. Steps [#steps] 1. In **Settings → Access Controls**, set **Remote Unlock** to **Enabled**. 2. Open the lock you want to configure and go to its **Door access** section. 3. Set the **Default** rule's mode. For *During reservations only*, set the **min before** and **min after**. 4. (Optional) Switch **Non-members** or a specific rule set to **Custom rule** and set its mode. 5. Save. What customers see [#what-customers-see] Two things, and they're independent: * **An unlock control on their reservation card**, which counts down (*"Unlock available in 12 min"*), turns into a green **Unlock door** button when your window opens, and reads *"Door offline — try again shortly"* if the lock is unreachable. * **A Door access button** on the club home screen. Tapping it lists the doors they can unlock — or, for mobile-key setups, opens the door-access screen in your club's branded app. Because they're independent, a customer can have a perfectly good door code while the unlock button shows the door offline. The code is stored on the lock, so it still works. See [Getting through the door at your club](/help/players/access-controls/unlock-a-door-from-the-app). If something goes wrong [#if-something-goes-wrong] * **Customers don't see an unlock option** — check that **Remote Unlock** is **Enabled**, the customer's rule set isn't on **No app unlock**, and (for *During reservations only*) they're inside the allowed window with a booking on the space mapped to that door. * **There's no Door access rules section on a lock** — who-can-unlock rules appear on **Seam**, **Rhombus**, and **UniFi Access** locks that support remote unlock. A RemoteLock lock shows only the door code window; its customers can still unlock from the app, on the standard reservation window. * **A non-member can unlock when they shouldn't** — set the **Non-members** override to **No app unlock**, or to *During reservations only*. * **A customer still can't get in** — work through [A customer can't get in — troubleshoot door access](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in). Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Getting through the door at your club](/help/players/access-controls/unlock-a-door-from-the-app) * [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling) * [A customer can't get in — troubleshoot door access](/help/facility-operators/access-controls/troubleshoot-customer-cant-get-in) * [Set up Salto KS mobile access](/help/facility-operators/access-controls/salto-ks-mobile-access) # Thermostat control (coming soon) {/* COMING-SOON placeholder published for SEO/AI-search reach (2026-08-02, Alex's call). Nothing is built — no thermostat code anywhere in the repo (verified 2026-08-02). Reachable via Seam alongside the locks we use today. Brand list re-verified against seam.co/supported-devices-and-systems on 2026-08-02 (ecobee, Nest, Honeywell, Sensi, Tado, Venstar, Trane, American Standard, Aprilaire, Sinope, LUX, + Sensibo for ductless mini-splits / IR AC). Seam's thermostat API does heat/cool/auto/eco/off, fan modes, named climate presets, daily+weekly programs, and /thermostats/set_temperature_threshold which emits thermostat.temperature_threshold_exceeded -- that's the basis for the HVAC-failure alerting section. Positioning per Alex 2026-08-02: 24/7 UNSTAFFED venues are the primary case, and cooling is first-class, not an afterthought. Keep claims capability-neutral until scoped; nothing is built. */} Conditioning an empty building is one of the larger costs a club carries, and one of the easiest to get wrong — a space that's freezing when the first booking starts, a bay that's stifling by mid-afternoon, or HVAC running all night for nobody. OpenCourt is building **smart thermostat control** so temperature follows your bookings instead of a fixed schedule. **This isn't live yet.** This page exists so you can find it and register interest. There's no thermostat setting in your admin panel today. Built for unstaffed hours [#built-for-unstaffed-hours] This matters most at venues that run **24/7 with nobody on site** — indoor golf, self-serve courts, late-night and early-morning slots. When there's no one to flip a switch, the building either runs all night or the first customer of the day walks into an uncomfortable space. Neither is good, and one of them is expensive. Because bookings already drive door access, they can drive temperature the same way, with no one present and nothing for staff to remember. Heating and cooling, equally [#heating-and-cooling-equally] Cooling is the half people forget. An indoor golf bay with a projector, a screen and four people in it heats up fast, and hot-climate clubs spend more on air conditioning than northern clubs spend on heat. This works the same in both directions: * **Pre-condition before arrival** — warm or cool, whichever the space needs, so the first booking of the day starts comfortable. * **Fall back when nothing's booked**, including gaps between bookings and the long unbooked stretch overnight. * **Follow the space, not the building** — a club with several bays, courts, or studios rarely needs all of them conditioned at once. Catching HVAC problems overnight [#catching-hvac-problems-overnight] The same connection reads the actual temperature back, so a space that drifts far outside its expected range can raise an alert. At an unstaffed venue that's the difference between finding out at 6am and finding out when a customer arrives at 6am. The hardware [#the-hardware] The same platform we already use for smart locks, **Seam**, also connects thermostats from **ecobee**, **Google Nest**, **Honeywell**, **Sensi**, **Tado**, **Venstar**, **Trane**, **American Standard**, **Aprilaire** and **Sinopé**. So for many clubs this would run on hardware you may already own, through a connection you may already have. If your space is cooled by **ductless mini-splits or a wall AC unit** rather than central HVAC — common in converted units and smaller studios — **Sensibo** is on the same list and controls those, so you're not shut out by not having a conventional thermostat. Brand support changes, so check a specific model on [Seam's supported devices list](https://www.seam.co/supported-devices-and-systems). Who this is for [#who-this-is-for] Any club paying to condition space that isn't always occupied, and **especially venues running unstaffed around the clock**. The savings scale with how much of your day is unbooked — which, for a 24/7 space, is most of it. In the meantime [#in-the-meantime] Nothing to do. If you're replacing a thermostat or adding AC control soon, choosing something from the list above keeps this option open without committing you to anything. Register your interest [#register-your-interest] We're prioritising this partly by who tells us they want it, so it's worth a message. * Email [support@getopencourt.com](mailto:support@getopencourt.com), or * Talk to your OpenCourt account representative. Useful things to mention: which thermostats or AC controllers you run (brand and model), how many zones, whether you operate unstaffed overnight, and whether you want temperature to follow individual bookings or just your opening hours. Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Which locks work with OpenCourt](/help/facility-operators/access-controls/choose-a-lock-for-your-club) # A customer can't get in — troubleshoot door access "A customer can't get in" has two very different causes, and they're fixed in opposite ways. Either the **lock is down** — a connection hiccup that fails for *everyone* and is fixed on site — or **this one customer's access** isn't valid right now, which is fixed in your settings or by explaining the timing. The customer only ever sees "it didn't open," so your first job is to tell the two apart. Everything here lives under **Settings → Access Controls** and the **Access control** pages in your admin panel. You'll need access-control permission. First: is it everyone, or just this customer? [#first-is-it-everyone-or-just-this-customer] Don't try to ask the customer "does it work for everyone else?" — they don't know. Figure out the scope yourself: 1. **Have staff (or a known-good account with a current booking on that door) try the same door.** If it also fails, the lock is down for everyone. If it works, the problem is specific to this customer. 2. **Check the unlock history.** Open the **Unlock history** tab and look at the last hour. A run of **Unlocked** rows from *other* people means the lock is fine and this is a per-customer issue. **Access Denied**, **Unlock Failed**, or **Device Disconnected** rows across *multiple different* people means the lock is down. Unlock history table listing Date and Time, Lock, Action, Method and Details, with both Unlocked and Access Denied rows. The **Method** column is what makes this readable. **Keycode** rows are people typing a booking code, and their **Details** links to the event — those are the ones you can trace. **Manual** rows are the door being opened by hand from the inside, with no code involved. Don't mistake a healthy run of Manual unlocks for a problem. Full breakdown in [Door codes day-to-day](/help/facility-operators/access-controls/door-codes-day-to-day#reading-the-method-column). * **Fails for everyone →** jump to [The lock is down for everyone](#the-lock-is-down-for-everyone). * **Works for others →** jump to [One customer can't get in (others can)](#one-customer-cant-get-in-others-can). The lock is down for everyone [#the-lock-is-down-for-everyone] When unlock fails for everyone, it's almost always a temporary connection hiccup between the lock (or its controller) and its provider — not anyone's account. It usually clears within a few minutes. * **Symptoms:** unlock worked recently, then started failing for multiple different people; the app shows "unlock failed" or just spins; the unlock history shows **Device Disconnected**, **Unlock Failed**, or **Low Battery** rows. * **Wait 1–2 minutes and retry.** These hiccups often self-clear. * **Power-cycle the controller on site.** Find the access-control unit wired to the door, unplug it, wait about 15 seconds, plug it back in, give it a minute to come back online, then try again. This resolves the large majority of cases. * **Test from the admin side.** Open the lock's page and use the **Unlock** button in the header. The status bar there shows whether the lock is online and its battery level — a **Low Battery** warning is worth acting on before it fails completely. Per-lock admin page header with online status, lock state, battery level, and an Unlock button. Scroll down the same page for **Lock Activity**, which narrows to that one lock and adds a **From** / **To** date range. Alongside the Locked/Unlocked events you'll see OpenCourt's own code operations — *Access Code Scheduled*, *Access Code Removed*, *Access Code Deleted*, *Access Code Time Frame Changed* — each with the code and its booking. That's the record that answers "did their code ever actually reach this lock?" The Lock Activity section on a lock's page, with a date range filter and rows including Access Code Scheduled and Access Code Removed. If the controller is powered and online but unlock still fails for everyone, or a power-cycle didn't help (or it keeps recurring), contact OpenCourt support with the door, when it started, and what the unlock history shows. One customer can't get in (others can) [#one-customer-cant-get-in-others-can] The lock is fine — others are getting in. Now it's about *how* this customer gets in and *when* they're trying. Work through the symptom that matches. Their door code doesn't work [#their-door-code-doesnt-work] Codes are time-limited. The same rule that governs in-app unlock drives the code window: by default a code activates **30 minutes before** the reservation and expires **10 minutes after** it ends (a per-lock rule can change this). Outside that window the code does nothing. 1. Open the **Access codes** tab and find their code. 2. Check the **Valid From**, **Valid Until**, and **Status** columns. **Upcoming** means it hasn't turned on yet (they're early). **Expired** or **Revoked** means the window has passed or the booking was cancelled. Only **Active** codes open the door. 3. Confirm the **space** column on the code matches the space they actually booked — a code only opens the door mapped to that space. (That column is headed **Court**, **Bay**, or **Field**, matching your club.) Access codes table with Code, Lock, Event, space, Valid From, Valid Until and Status columns, showing Upcoming rows. A big gap between **Created At** and **Valid From** is normal. A code for a booking weeks out is created the moment the booking is made, sits **Upcoming**, and only becomes **Active** in its window. A code that isn't loaded onto the lock yet is normal, not broken. Native scheduling pushes codes about **72 hours** ahead; Just-in-Time pushes them about **60 minutes** before (the lock must be online then). On RemoteLock, a code stays **pending** until the lock confirms it, so a pending code ahead of the booking is expected. They don't see an unlock option in the app [#they-dont-see-an-unlock-option-in-the-app] In-app unlock needs a chain of things to all be true. Check them in order: 1. **Club-wide unlock is on.** In **Settings → Access Controls**, the **Remote Unlock** setting must be enabled ("Allow users to unlock doors from the club home page…"). If it's off, no one sees the option. 2. **The lock supports remote unlock.** Some locks only issue codes and can't be unlocked remotely. 3. **Their rule set allows it.** The per-lock **Access Rule** for their rule set must not be *No app unlock*. 4. **They're in the window, on the right space.** If the rule is *During reservations only*, they must be inside the window (default 30 min before to 10 min after, per-lock adjustable) and their booking must be on the court mapped to that door. The app says access denied / not authorized [#the-app-says-access-denied--not-authorized] This means they reached the lock but the rule turned them away. Two usual causes: * **Their rule set's Access Rule is "No app unlock"** — that role is intentionally not allowed to unlock this door. Check the door's rules on the lock page and confirm the rule assigned to their rule set (including any **Non-members** override). * **They're outside the "During reservations only" window**, or their booking is on a space that isn't mapped to this door. Confirm the timing and the court. Their code changed [#their-code-changed] If you (or the customer) moved the booking to a **different space or door**, OpenCourt issues a **new** code for the new door and sends a "Your access code changed" email. The old code stops working. Ask them to use the latest code from the app (booking details → **Access code**) or their most recent confirmation email. A plain reschedule that keeps the same court keeps the same PIN with an updated window. Membership changed and access stopped [#membership-changed-and-access-stopped] Access rules can differ by rule set. If a customer's membership lapsed or changed, they may fall under a different rule set — often the **Non-members** override — whose Access Rule is *No app unlock* or a narrower window. Check which rule set they're on now and what that rule set's Access Rule allows. Every **in-app** unlock attempt — success or failure — is recorded in the club activity log with the customer's name, since they were signed in when they tapped. If the customer's memory of "it just stopped" doesn't match, the activity log shows exactly what happened and when. (Keypad entries land in Unlock History instead, tied to the code and its booking rather than a person.) Still stuck? [#still-stuck] If the customer clearly should get in — valid **Active** code (or all four unlock conditions met), right space, right time — and still can't, contact OpenCourt support. Include: * The **customer's name** * The **door** (lock name) * The **time it failed** * What the **unlock history** shows for that attempt Related [#related] * [How access controls work in OpenCourt](/help/facility-operators/access-controls) * [Set who can unlock doors from the app](/help/facility-operators/access-controls/set-who-can-unlock-doors) * [Access codes & scheduling](/help/facility-operators/access-controls/access-codes-and-scheduling) * [Door codes day to day](/help/facility-operators/access-controls/door-codes-day-to-day) * [Unlock a door from the app](/help/players/access-controls/unlock-a-door-from-the-app) # How BayControl works for simulator bays BayControl locks each golf simulator bay whenever nobody has booked it, and opens it for the customer who did. Customers type the code from their booking on the bay's screen and play; when their time is up, the bay locks itself again. The result is **unstaffed bays** — you can sell late-night and early-morning slots without anyone at the desk. BayControl is part of OpenCourt and follows your OpenCourt booking schedule. It is being switched on club by club — [contact us](/contact) to add it to your venue. What does BayControl do for my venue? [#what-does-baycontrol-do-for-my-venue] * **Locks a bay when it is free.** Every screen in the bay shows your club's lock screen — your name, a clock, the next booking, and a QR code that lets a walk-up customer book the bay on their phone. * **Opens for the paying customer.** Every booking on a bay comes with its own code. The bay greets the customer and asks for it. * **Stays out of the way during play.** Once the bay opens, the game has the screens. The only thing on top is a small end-of-session notice in the corner you choose. * **Ends sessions on time.** Customers get warnings before their time runs out — 10, 5 and 1 minutes by default — and the bay locks itself, ready for the next booking. * **Keeps working when the internet does not.** A network drop does not lock a paying customer out of a bay they paid for, and your staff can still open a bay. * **Puts every bay on one screen.** The admin panel shows which bays are locked, in session, or need attention, and lets staff open or lock a bay from the front desk or a phone. Does BayControl work with my simulator? [#does-baycontrol-work-with-my-simulator] Yes. BayControl works with all the top golf simulators, and it needs no new hardware. Your bays keep running the simulator software they run today. Running something less common? Tell us which simulator software your bays use and we'll confirm it before you start. What do my customers see? [#what-do-my-customers-see] 1. They book a bay in OpenCourt, the same way they do now. 2. Their code arrives with the booking — on the booking in the OpenCourt app, labelled **Bay unlock code**, and in their confirmation and reminder emails. Nobody at the desk has to hand anything out. 3. At the bay, the screen greets them by name and asks for the code. A few minutes before the start time is fine. 4. They play. Near the end, a small notice counts down their remaining time. 5. When their time is up, the bay thanks them and locks for the next booking. The customer's side is in [Unlock your simulator bay](/help/players/simulator-bays/unlock-your-simulator-bay). How do staff handle walk-ins and problems? [#how-do-staff-handle-walk-ins-and-problems] * **A walk-in or a lesson with no booking:** staff open the bay for a set number of minutes from the admin panel, and it locks itself again when the time runs out. Staff can also open a bay at the bay itself. * **A customer who cannot find their code:** staff read it back to them from the booking in the admin panel. * **A session that has to end now:** staff lock the bay from the admin panel. What do I need to use BayControl? [#what-do-i-need-to-use-baycontrol] * **Your simulator bays booked through OpenCourt.** BayControl follows your OpenCourt schedule. * **The PC that already runs each bay's screens and simulator.** There is nothing new to buy. * **An internet connection** at the venue. * **BayControl switched on for your club.** [Contact us](/contact) and we'll set it up with you. Common questions [#common-questions] Do I need a smart lock or new hardware? [#do-i-need-a-smart-lock-or-new-hardware] No. BayControl locks the bay's screens and simulator, not a door, and it uses the PC each bay already has. If your club also uses OpenCourt's [door locks and access controls](/help/facility-operators/access-controls), the two work side by side: a customer gets an **Access code** for the door and a **Bay unlock code** for the bay. What happens if the venue's internet goes down? [#what-happens-if-the-venues-internet-goes-down] Customers with a booking still get in, and your staff can still open a bay. New bookings made during the outage reach the bay once the internet is back. Can customers start early or get extra time at the end? [#can-customers-start-early-or-get-extra-time-at-the-end] Yes. By default a code works from 5 minutes before the booking until 2 minutes after it, and a customer can take one free extra minute at the end to save their round. You set all of these for your club in the admin panel. Can I match the lock screen to my brand? [#can-i-match-the-lock-screen-to-my-brand] Yes. The lock screen shows your club's name, and you choose its colors in the admin panel. Can I try it before rolling it out to every bay? [#can-i-try-it-before-rolling-it-out-to-every-bay] Yes. Start with one bay, check it with your team, then add the rest. [Contact us](/contact) to plan it. Guides for BayControl clubs [#guides-for-baycontrol-clubs] Clubs using BayControl have step-by-step guides for setup and day-to-day running. [Open the club guides](/help/facility-operators/bay-control/set-up-bay-control) with the help password OpenCourt gave your club. # Auto-Allocating with Memberships How It Works [#how-it-works] There are three pieces to the system: 1. Booking Pass - Define the benefit [#1-booking-pass---define-the-benefit] [A booking pass](/help/facility-operators/booking-and-guest-passes/what-are-booking-passes) defines what the benefit is. You create it once, and it gets allocated to members repeatedly. Types of the most popular booking passes: 1. [**Guest pass (e.g. members get 3 guest passes per month)**](/help/facility-operators/booking-and-guest-passes/creating-a-guest-pass) 2. [**Free reservation pass (e.g. members get 4 free reservations a month)**](/help/facility-operators/booking-and-guest-passes/creating-a-free-reservation-pass) 3. [**Free court hours pass (e.g. members get 5 free hours for court bookings per month)**](/help/facility-operators/booking-and-guest-passes/creating-a-free-court-hours-pass) 4. [**Free event pass (e.g. members get 1 free Open Play a week or 1 Clinic a week)**](/help/facility-operators/booking-and-guest-passes/create-a-free-event-pass) 5. [**Free event with specific event tag pass (e.g. members get 1 free specific group class per week)**](/help/facility-operators/booking-and-guest-passes/creating-a-free-pass-for-events-with-a-specific-tag) 6. [**Free lesson pass (e.g. members get 1 free 1hr private lesson per month)**](/help/facility-operators/booking-and-guest-passes/creating-a-free-lesson-pass) Each one of those can be a family shared pass. *** 2. Allocation Rules - Define who gets it and when [#2-allocation-rules---define-who-gets-it-and-when] An allocation rule connects pass templates to memberships. It says: "Members with \[this membership] get \[these passes] every \[week/month/etc.]." [You can find more information on how to do it here.](/help/facility-operators/booking-and-guest-passes/creating-an-allocation-rule) *** 3. Pass Allocations - The actual passes members receive [#3-pass-allocations---the-actual-passes-members-receive] When the rule fires, individual pass instances are created for each eligible member. These are the actual passes that get consumed during booking. *** What Happens Automatically [#what-happens-automatically] Once a rule is set up, the system handles everything: When a member's membership activates [#when-a-members-membership-activates] Passes are created immediately for the current period (and any pre-created future periods). The member can start using them right away. At the start of each new period [#at-the-start-of-each-new-period] A scheduled job runs daily at 5 AM (club's local time). For each rule, it checks which periods are due, finds all eligible members, and creates fresh passes. Members wake up to new passes in their account. When a member changes their membership [#when-a-member-changes-their-membership] The system automatically adjusts: * **Upgrade** (e.g., Basic → Premium) — Old passes stay active until they expire. New passes from the Premium rule are created. * **Downgrade** (e.g., Premium → Basic) — Premium passes that haven't started yet are archived. Basic passes are created. * **Same product, different price** — If the rule has price-level filters, eligibility is recalculated. Before confirming a membership change, admins see an **impact preview** showing exactly which passes will be archived (red) and which will be created (green). When a member cancels their membership [#when-a-member-cancels-their-membership] * Passes that are currently active stay usable until the membership end date * Pre-created future passes (for periods after membership end) are archived immediately * Passes that overlap the cancellation boundary are marked "Pending Archival" — usable now but will be archived when the membership expires When a member's membership is terminated immediately [#when-a-members-membership-is-terminated-immediately] All passes are archived immediately. Past bookings made with those passes remain valid — nothing is retroactively revoked. When a membership is frozen [#when-a-membership-is-frozen] Passes become unusable during the freeze but aren't deleted. When the membership resumes, all passes (including ones created during the freeze) become usable again. *** How Members Use Their Passes [#how-members-use-their-passes] Viewing Passes [#viewing-passes] Members can see their passes at **My Profile → Booking Passes**, organized by status: * **Active** — Currently usable, with remaining uses/minutes and expiration date * **Upcoming** — Start date is in the future (pre-created passes) * **Expired** — Validity period ended * **Exhausted** — All uses or minutes consumed Using Passes During Booking [#using-passes-during-booking] When a member books an event or reserves a court: 1. The system checks all their active passes against the booking: * Does the pass support this event type? * Does the pass match the event's tags? (if tag-restricted) * Is the event within the pass's time restrictions? * Does the pass have remaining uses or minutes? 2. Matching passes are shown during checkout 3. The member selects which pass to apply 4. The pass is consumed — remaining uses or minutes are decremented 5. If the booking is refunded later, the pass is restored *** Managing Rules (Admin) [#managing-rules-admin] Editing a Rule [#editing-a-rule] When you edit an existing rule (change the schedule, product filters, or pass configurations), the system shows a confirmation dialog: * **Passes being removed** (red) — Templates or configurations that are being removed. Pre-created future passes from these will be archived. * **Passes being added** (green) — New templates or configurations. Passes will be created for eligible members. * **Passes unchanged** (gray) — No change to these. Archiving a Rule [#archiving-a-rule] Archiving a rule stops future allocations. Existing passes that were already created remain active and usable — they're not retroactively removed. Activity Log [#activity-log] Each rule has an **Activity** tab showing every allocation run: * When it ran (scheduled time and actual execution time) * How many users were processed * How many passes were created * Execution duration * Status (Completed, Failed, In Progress) * For failed runs: error details and a retry button Manually Granting Passes [#manually-granting-passes] Besides auto-assignment, admins can manually grant passes to specific users from **Booking Passes → Grant**. This is useful for one-off situations like comp passes, promotional offers, or correcting missed allocations. *** Example Setup [#example-setup] Here's how a club might set up passes for three membership tiers: **Gold Membership ($150/month)** * Rule: Monthly, 1st of each month, pre-create 7 days ahead * Passes: 12 Open Play Sessions + Unlimited Court Bookings + 50% Off Clinics **Silver Membership ($100/month)** * Rule: Monthly, 1st of each month, pre-create 7 days ahead * Passes: 8 Open Play Sessions + 4 Hours of Court Time **Basic Membership ($50/month)** * Rule: Monthly, 1st of each month, pre-create 7 days ahead * Passes: 4 Open Play Sessions Each month: 1. On the 25th (7 days before the 1st), next month's passes are pre-created and visible as "Upcoming" 2. On the 1st, passes become active 3. Members book events throughout the month, consuming passes 4. At the end of the month, unused passes expire (if configured with a 1-month validity) 5. Cycle repeats *** Tips [#tips] * **Start with "Whole Event" passes** for simple session-based benefits — they're the easiest to understand and manage * **Use "By Minutes" passes** when booking durations vary (e.g., members can book 30-min, 60-min, or 90-min slots from the same pool) * **Pre-create passes 7 days ahead** so members can see their upcoming passes before the new period starts * **Use tag restrictions** to differentiate benefits (e.g., "Beginner Clinic Pass" only works for events tagged "Beginner") * **Check the Activity tab** after setting up a new rule to confirm passes were allocated correctly * **Use the impact preview** before changing memberships — it shows exactly what will happen to the member's passes * **Don't delete pass templates** if they're referenced by active rules — archive them instead # Create a Family-Shared Pass **Example:** A family membership includes 10 hours of court time per month, shared between all family members. *** How Family Sharing Works [#how-family-sharing-works] * The primary member (pass holder) gets the pass allocated via a membership * All linked family members automatically become pass holders too * Everyone draws from the **same pool** — if the pass has 10 hours, the entire family shares those 10 hours * Family sharing applies to all existing and future allocations of that pass template Family members are managed through the [**Family** section of a member's profile](/help/facility-operators/customers-and-families/creating-and-managing-a-family-account). Each family member must be linked to the primary member. *** Step 1: Create the Pass Template [#step-1-create-the-pass-template] 1. Go to **Booking Passes → Booking Passes** (Manage tab) 2. Click **Create Booking Pass** Fill in the form: | Field | Value | | ------------------- | ---------------------------------------------------- | | **Name** | Family Court Hours | | **Display Name** | Family Court Time *(optional)* | | **Pass Applies To** | Event | | **Redemption Mode** | By Hours *(or Whole Event, depending on your needs)* | | **User Type** | Pass Holder Only | Toggle on **"Can be redeemed by family members"**. Under **Event Restrictions**, check the event types the pass should cover — e.g., **Reservations**. Click **Create Booking Pass**. *** How It Works for Members [#how-it-works-for-members] 1. A parent (primary member) has a Family Membership with 3 linked family members 2. On the 1st of the month, 600 minutes (10 hours) are allocated to the parent 3. All family members can see and use the pass in their own accounts 4. The parent books a 90-minute court → 510 minutes remaining 5. A family member books a 60-minute court → 450 minutes remaining 6. Everyone draws from the same pool until it's used up 7. On the 1st of next month, a fresh 600-minute pool is allocated Every family member sees the same remaining balance under **Booking passes** in their profile, so the pool is always in sync. *** Family Sharing with Different Pass Types [#family-sharing-with-different-pass-types] Family sharing works with any pass type and redemption mode: | | Pass TypeHow sharing works | | ------------------------------------------------- | ------------------------------------------------------------------------ | | **Whole Event** (e.g., 4 free reservations) | Each family member's booking consumes one pass from the shared pool of 4 | | **By Hours** (e.g., 10 hours of court time) | Each family member's booking deducts from the shared hour pool | | **Percentage Discount** (e.g., 20% off) | Each family member gets the discount applied when they book | | **Fixed Discount** (e.g., $10 off) | Each family member gets the discount applied when they book | | **Coach / Whole Lesson** (e.g., 2 lesson credits) | Each family member can book lessons using the shared credits | *** Variations [#variations] **Per-member passes (no sharing):** If you want each family member to get their own separate pass, leave family sharing OFF and create individual allocation rules per membership. Each person gets their own pool. **Guest pass + family sharing:** You can enable both family sharing and Guest Only user type — but this is unusual. More commonly, you'd create a family-shared "Pass Holder Only" pass for the family's own bookings and a separate "Guest Only" pass for bringing non-family guests. *** Key Points [#key-points] * **Family members share one pool** — they don't each get their own allocation. If you allocate 4 passes, that's 4 total for the entire family, not 4 per person. * Family sharing is set on the **pass template**, not the allocation rule. Once enabled, it applies to every allocation of that template. * **Adding a new family member** to the primary account gives them immediate access to any family-shared passes the primary member has. * **Removing a family member** revokes their access to the shared passes. * Family members are linked through the member's profile — the admin doesn't need to allocate passes to each family member separately. # Create a Free Event Pass **Example:** Members get 1 free Open Play session or 1 Free Clinic per week. *** Step 1: Create the Pass Template [#step-1-create-the-pass-template] 1. Go to **Booking Passes page** 2. Click **Create Booking Pass** 3. Enter the name and the description of the pass (optional) 4. Pick Event that it applies to. 5. Choose Whole Event/Reservation 6. Choose Pass Holder Only as a user type that it can be applied to. 7. If you want your members to share it with their family members, toggle on this setting: 8. Under **Event Restrictions**, check only **Open Play (or Clinic, or other Event type)**. Leave Reservations and other event types unchecked. Click **Create Booking Pass**. *** Step 2: Create the Allocation Rule [#step-2-create-the-allocation-rule] Now set up automatic allocation so members get passes when their membership activates. 1. Go to **Booking Passes → Allocation Rules** 2. Click **Create Rule** Use this form to choose who will receive these passes, when they’ll be granted, and how often they’ll be issued. 1\. Fill out the name and the description (optional)\ 2.Select the memberships and plans these passes should apply to. 3\. Define how often you will grant those passes - e.g. every month, or every week etc.\ 4\. Choose how many days in advance the passes should be issued (Lead Time). For example, if members can book 3 days in advance, the passes should be issued 3 days before the start of the new week or month. If passes are issued weekly, also select the day of the week when they should be allocated.\ 5\. If you check “Allocate for the current ongoing interval”, it’ll create the passes for he current period. Otherwise, it will start generating passes from the next month/week. 6\. Under **Booking Passes**, click **Add a booking pass** and configure how many passes you want to give per certain period. Click **Save**. *** How It Works for Members [#how-it-works-for-members] 1. A Gold member joins an Open Play event 2. The system finds an available free Open Play pass and applies it 3. The event fee is waived — the pass is consumed 4. If the member joins another Open Play that same week, they pay full price 5. Next Monday (or the chosen day), a new pass is allocated automatically *** Variations [#variations] **Multiple event types:** If you want the pass to cover both Open Play and Tournaments, check both under Event Restrictions. The pass will apply to either type. **Monthly instead of weekly:** Change the allocation rule frequency to "Month(s)" and set the day of month. For example, 2 free Open Play sessions per month. *** Key Points [#key-points] * Each event entry consumes one pass, regardless of the event's duration * The pass only covers the event types you selected — other event types are unaffected * Unused passes expire at the end of the week (based on the 1-week validity) unless you check **"Never expires"** * Members can see their remaining passes in **My Profile → Booking Passes** # Creating a Free Court Hours Pass **Example:** Premium members get 5 free hours for court bookings per month. *** Step 1: Create the Pass Template [#step-1-create-the-pass-template] 1. Go to **Booking Passes page** 2. Click **Create Booking Pass** 3. Enter the name and the description of the pass (optional) 4. Pick Event that it applies to. 5. Choose Whole Event/Reservation 6. Choose Pass Holder Only as a user type that it can be applied to. 7. If you want your members to share it with their family members, toggle on this setting: 8. Under **Event Restrictions**, check only **Reservations**. Leave all event types (Open Play, Clinic, etc.) unchecked — this pass should only apply to court reservations. Click **Create Booking Pass**. **Note:** "By Hours" is only available for Event passes — not Coach passes. *** Step 2: Create the Allocation Rule [#step-2-create-the-allocation-rule] Now set up automatic allocation so members get passes when their membership activates. 1. Go to **Booking Passes → Allocation Rules** 2. Click **Create Rule** Use this form to choose who will receive these passes, when they’ll be granted, and how often they’ll be issued. 1\. Fill out the name and the description (optional)\ 2.Select the memberships and plans these passes should apply to. 3\. Define how often you will grant those passes - e.g. every month, or every week etc.\ 4\. How many days in advance you’ll issue them (Lead Time) - e.g. your members can book 7 days in advance, then they’ll need to have passes 7 days in advance before the new month/week starts.\ 5\. If you check “Allocate for the current ongoing interval”, it’ll create the passes for he current period. Otherwise, it will start generating passes from the next month/week. 6\. Under **Booking Passes**, click **Add a booking pass** and configure how many hours (minutes) you want to give per certain period. Click **Save**. *** How It Works for Members [#how-it-works-for-members] 1. A Premium member books a 90-minute court reservation 2. The system deducts 90 minutes from their pool (300 → 210 minutes remaining) 3. They book another 60-minute reservation — pool goes to 150 minutes 4. They can keep booking until the pool runs out 5. If they try to book a 2-hour slot with only 90 minutes left, the pass won't cover it — they pay full price 6. On the 1st of next month, a fresh 300-minute pool is allocated Members can see their remaining balance in **My Profile → Booking Passes** (displayed as hours and minutes, e.g., "3h 30m remaining"). *** When to Use Hours vs Whole Event [#when-to-use-hours-vs-whole-event] | | ScenarioUse | | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | Members get a fixed number of sessions (e.g., 4 bookings/month) | **Whole Event** — see [Free Reservation Pass](/help/facility-operators/booking-and-guest-passes/creating-a-free-reservation-pass) | | Members get a time budget they can split freely (e.g., 5 hours/month) | **By Hours** — this guide | The "By Hours" mode is more flexible — members can book three 90-minute sessions or five 60-minute sessions, whatever they prefer. *** Key Points [#key-points] * The hour pool is set in the **allocation rule** (Minutes Available field), not on the pass template * A booking must fit entirely within the remaining pool — partial coverage is not supported * Unused hours expire at the end of the month unless you check **"Never expires"** in the allocation rule * The pass applies to all event types you selected in Event Restrictions — if you only checked Reservations, Open Play bookings won't deduct from the pool # Creating a Free Lesson Pass **Example:** Members get 1 free 1-hour private lesson per month. *** Step 1: Create the Pass Template [#step-1-create-the-pass-template] 1. Go to **Booking Passes page** 2. Click **Create Booking Pass** 3. Enter the name and the description of the pass (optional) 4. Pick Coach that it applies to. 5. Choose Whole Event/Reservation 6. Choose Pass Holder Only as a user type that it can be applied to. 7. If you want your members to share it with their family members, toggle on this setting: 8. Under **Coach Restrictions**, choose how specific you want to be. Option A: Any coach, any service [#option-a-any-coach-any-service] Select **"All coach lessons"**. The pass works for any coach at the club, for any of their services. Option B: Specific coach, all their services [#option-b-specific-coach-all-their-services] Select **"Specific coaches"** → check the coach. Leave their services unchecked — this means the pass covers all services that coach offers. **Option C: Specific coach, specific service (recommended for this example)** Select **"Specific coaches"** → check the coach → click the expand arrow next to their name → check only **"Private Lesson — 1 Hour"**. This ensures the pass only covers 1-hour private lessons and only with coaches you chose. If the member books a 90-minute lesson or a different coach, the pass won't apply — they pay full price. Click **Create Booking Pass**. *** Step 2: Create the Allocation Rule [#step-2-create-the-allocation-rule] Now set up automatic allocation so members get passes when their membership activates. 1. Go to **Booking Passes → Allocation Rules** 2. Click **Create Rule** Use this form to choose who will receive these passes, when they’ll be granted, and how often they’ll be issued. 1\. Fill out the name and the description (optional)\ 2.Select the memberships and plans these passes should apply to. 3\. Define how often you will grant those passes - e.g. every month, or every week etc.\ 4\. How many days in advance you’ll issue them (Lead Time) - e.g. your members can book 7 days in advance, then they’ll need to have passes 7 days in advance before the new month/week starts.\ 5\. If you check “Allocate for the current ongoing interval”, it’ll create the passes for he current period. Otherwise, it will start generating passes from the next month/week. 6\. Under **Booking Passes**, click **Add a booking pass** and configure how many passes you want to give per certain period. Click **Save**. *** How It Works for Members [#how-it-works-for-members] 1. A VIP member books a 1-hour private lesson with Coach Sarah 2. The system finds an available lesson pass and applies it 3. The lesson fee is waived — the pass is consumed 4. If the member books another lesson that month, they pay full price 5. If the member tries to book a 90-minute lesson with Sarah, the pass doesn't apply (wrong service) 6. If the member books with a different coach, the pass doesn't apply (wrong coach) 7. On the 1st of next month, a new pass is allocated automatically *** Variations [#variations] **Any coach:** Use "All coach lessons" in Coach Restrictions to let the pass work with any coach at the club. Good for general lesson credits. **Multiple coaches:** Check several coaches under "Specific coaches." The pass applies to any of the selected coaches. **All services for a coach:** Select the coach but leave all services unchecked — the pass covers any service that coach offers, not just one specific type. **Discount instead of free:** Change the Redemption Mode to "Percentage Discount" (e.g., 50% off) or "Fixed Discount" (e.g., $20 off) for a partial discount on lessons instead of a fully free lesson. *** Key Points [#key-points] * **"By Hours" is not available for Coach passes** — use "Whole Lesson" instead. Each lesson consumes one pass regardless of duration. * Service-level restrictions prevent misuse — if the membership includes 1-hour lessons, restricting to the "Private Lesson — 1 Hour" service means the pass can't be used for more expensive 90-minute sessions * Coach passes don't support time-of-day restrictions — coach scheduling is managed through the coach's own availability settings * Unused passes expire at the end of the month unless you check **"Never expires"** in the allocation rule # Creating a Free Pass for Events with a Specific Tag **Example:** Members get 1 free "Youth Program" class per week. *** Prerequisites [#prerequisites] Before creating this pass, make sure you've created the event tag you want to use: 1. Go to **Events → Tags** 2. Create a tag (e.g., "Team Practice", "Beginner", "Youth Program") 3. Assign the tag to the relevant events See [Event Tags](/help/facility-operators/events-and-programs/event-tags) for details on creating and assigning tags. *** Step 1: Create the Pass Template [#step-1-create-the-pass-template] 1. Go to **Booking Passes page** 2. Click **Create Booking Pass** 3. Enter the name and the description of the pass (optional) 4. Pick Event that it applies to. 5. Choose Whole Event/Reservation 6. Choose Pass Holder Only as a user type that it can be applied to. 7. If you want your members to share it with their family members, toggle on this setting: 8. Under **Event Restrictions**, enter the tag you want the pass to be applied to. In this case the pass will be applied to all events with the tag “Youth Program” Click **Create Booking Pass**. *** Step 2: Create the Allocation Rule [#step-2-create-the-allocation-rule] Now set up automatic allocation so members get passes when their membership activates. 1. Go to **Booking Passes → Allocation Rules** 2. Click **Create Rule** Use this form to choose who will receive these passes, when they’ll be granted, and how often they’ll be issued. 1\. Fill out the name and the description (optional)\ 2.Select the memberships and plans these passes should apply to. 3\. Define how often you will grant those passes - e.g. every month, or every week etc.\ 4\. Choose how many days in advance the passes should be issued (Lead Time). For example, if members can book 3 days in advance, the passes should be issued 3 days before the start of the new week or month. If passes are issued weekly, also select the day of the week when they should be allocated.\ 5\. If you check “Allocate for the current ongoing interval”, it’ll create the passes for he current period. Otherwise, it will start generating passes from the next month/week. 6\. Under **Booking Passes**, click **Add a booking pass** and configure how many passes you want to give per certain period. Click **Save**. *** How It Works for Members [#how-it-works-for-members] 1. A member joins an event tagged "Group Fitness" 2. The system finds an available pass with that tag restriction and applies it 3. The event fee is waived — the pass is consumed 4. If the member joins another "Group Fitness" event that same week, they pay full price 5. If the member joins an event without the tag (e.g., a regular Open Play), the pass is not used 6. Next Monday, a new pass is allocated automatically *** How Tag Matching Works [#how-tag-matching-works] * The pass applies to any event that has **any** of the selected tags * If you select multiple tags (e.g., "Youth Program" and "Yoga"), the pass applies to events with either tag * If you also check event types (e.g., Clinic), the pass applies to events matching the event type **OR** the tag — it's OR logic, not AND *** Variations [#variations] **Multiple tags:** Select several tags to make the pass cover a broader set of events — e.g., "Beginner" and "Intermediate" to cover all non-advanced classes. **Tag + event type:** Check both a tag and an event type for maximum flexibility. For example, check "Clinic" event type and "Beginner" tag — the pass covers all clinics OR any event tagged "Beginner." **Monthly instead of weekly:** Change the frequency to "Month(s)" for a monthly allowance instead. *** Key Points [#key-points] * Only events with the matching tag are covered — untagged events are not affected * Remember to tag your events consistently — if an event isn't tagged, the pass won't apply even if it's the right type of event * Tags use OR logic: selecting multiple tags means any one of them qualifies the event * Unused passes expire based on the validity period unless you check **"Never expires"** # Creating a Free Reservation Pass *** Step 1: Create the Pass Template [#step-1-create-the-pass-template] 1. Go to **Booking Passes page** 2. Click **Create Booking Pass** 3. Enter the name and the description of the pass (optional) 4. Pick Event that it applies to. 5. Choose Whole Event/Reservation 6. Choose Pass Holder Only as a user type that it can be applied to. 7. If you want your members to share it with their family members, toggle on this setting: 8. Under **Event Restrictions**, check only **Reservations**. Leave all event types (Open Play, Clinic, etc.) unchecked — this pass should only apply to court reservations. Click **Create Booking Pass**. *** Step 2: Create the Allocation Rule [#step-2-create-the-allocation-rule] Now set up automatic allocation so members get passes when their membership activates. 1. Go to **Booking Passes → Allocation Rules** 2. Click **Create Rule** Use this form to choose who will receive these passes, when they’ll be granted, and how often they’ll be issued. 1\. Fill out the name and the description (optional)\ 2.Select the memberships and plans these passes should apply to. 3\. Define how often you will grant those passes - e.g. every month, or every week etc.\ 4\. How many days in advance you’ll issue them (Lead Time) - e.g. your members can book 7 days in advance, then they’ll need to have passes 7 days in advance before the new month/week starts.\ 5\. If you check “Allocate for the current ongoing interval”, it’ll create the passes for he current period. Otherwise, it will start generating passes from the next month/week. 6\. Under **Booking Passes**, click **Add a booking pass** and configure how many passes you want to give per certain period. Click **Save**. *** How It Works for Members [#how-it-works-for-members] 1. A Premium member books a court reservation 2. The system finds an available free reservation pass and applies it 3. The booking fee is waived — one pass is consumed (e.g., 4 → 3) 4. After all 4 passes are used, the member pays the regular court fee 5. On the 1st of next month, 4 new passes are allocated automatically Members can see their remaining passes in **My Profile → Booking Passes**. *** Key Points [#key-points] * The pass covers the full reservation regardless of duration — a 60-minute and 90-minute booking each consume one pass * If you want duration-based passes instead (e.g., 5 hours of court time), see [Create a Free Court Hours Pass](/help/facility-operators/booking-and-guest-passes/creating-a-free-court-hours-pass) * Only court reservations are covered — Open Play, clinics, and other events are not affected * Unused passes expire at the end of the month unless you check **"Never expires"** in the allocation rule # Creating a Guest Pass **Example:** Gold members get 3 free guest passes per month. *** Step 1: Create the Pass Template [#step-1-create-the-pass-template] 1. Go to **Booking Passes page** 2. Click **Create Booking Pass** 3. Enter the name and the description of the pass (optional) 4. Pick Event that it applies to. 5. Choose Whole Event/Reservation 6. Choose Guest Only as a user type that it can be applied to. 7. Leave **Family Sharing** off — guest passes apply to guests, not family members. 8. Under **Event Restrictions**, select which event types guests can attend — for example, check **Reservations** and **Open Play**. Click **Create Booking Pass**. *** Step 2: Create the Allocation Rule [#step-2-create-the-allocation-rule] Now set up automatic allocation so members get passes when their membership activates. 1. Go to **Booking Passes → Allocation Rules** 2. Click **Create Rule** Use this form to choose who will receive these passes, when they’ll be granted, and how often they’ll be issued. 1\. Fill out the name and the description (optional)\ 2.Select the memberships and plans these passes should apply to. 3\. Define how often you will grant those passes - e.g. every month, or every week etc.\ 4\. How many days in advance you’ll issue them - e.g. your members can book 7 days in advance, then they’ll need to have passes 7 days in advance before the new month/week starts.\ 5\. If you check “Allocate for the current ongoing interval”, it’ll create the passes for he current period. Otherwise, it will start generating passes from the next month/week. 6\. Under **Booking Passes**, click **Add a booking pass** and configure how many passes you want to give per certain period. Click **Save**. *** How It Works for Members [#how-it-works-for-members] 1. A Gold member creates a reservation and adds a guest 2. The system checks for available guest passes 3. If the member has passes remaining, the guest's fee is waived 4. One pass is consumed — remaining uses go down (e.g., 3 → 2) 5. If all 3 passes are used up, the guest pays full price 6. On the 1st of next month, 3 new passes are allocated automatically *** Key Points [#key-points] * **Guest Only** user type means the pass only covers guests — the member's own participation is unaffected * Unused passes expire at the end of the month (based on the 1-month validity period) * To let unused passes carry over, check **"Never expires; unused passes accumulate"** in the allocation rule * Guest passes work for both reservations and events — control which types via the Event Restrictions checkboxes # Creating a Pass Package **Use cases:** * A "10-Pack of Court Bookings" that anyone can buy * A "Beginner Bundle" with 5 clinic passes and 3 court hours * A "Private Lesson Package" with 5 lessons with a specific coach *** Step 1: Create the Booking Pass Templates First [#step-1-create-the-booking-pass-templates-first] Before creating a package, you need the booking pass template(s) that will be included. If you haven't created them yet, go to **Booking Passes → Booking Passes** and create the templates you need. See the most popular individual pass guides for details: 1. [Guest pass](/help/facility-operators/booking-and-guest-passes/creating-a-guest-pass) 2. [Free reservation pass](/help/facility-operators/booking-and-guest-passes/creating-a-free-reservation-pass) 3. [Free court hours pass](/help/facility-operators/booking-and-guest-passes/creating-a-free-court-hours-pass) 4. [Free event pass](/help/facility-operators/booking-and-guest-passes/create-a-free-event-pass) 5. [Free event with specific event tag pass](/help/facility-operators/booking-and-guest-passes/creating-a-free-pass-for-events-with-a-specific-tag) 6. [Free lesson pass](/help/facility-operators/booking-and-guest-passes/creating-a-free-lesson-pass) > You only need the **pass templates** — you do NOT need allocation rules for packages. The package itself handles allocation on purchase. *** Step 2: Create the Pass Package [#step-2-create-the-pass-package] 1. Go to **Products → Add Product** 2. At the top, you'll see two options: **General Product** and **Pass Package**. Select **Pass Package**. *** Product Details [#product-details] | | FieldDescription | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------- | | **Name** | The product name — visible to admins and customers (e.g., "10 Private Lessons Package") | | **Internal description** | Notes for your team only — not shown to customers | | **Category** | Optional — organize products by category (e.g., "Packages", "Passes"). You can create new categories from the dropdown. | *** Product Image [#product-image] Upload an optional image to showcase the package on your store page. Images are cropped to square format, max 5MB. *** Sales Channels [#sales-channels] Choose where the package can be purchased: | | Channel Description | | ---------------- | ---------------------------------------------------- | | **Online Store** | Visible to customers on your club's store page | | **POS** | Available for sale at the point of sale (front desk) | Both channels can be enabled at the same time. If you enabled the “Online Store” channel, your customers will see it on the Store page, and will be able to purchase from the app or website: *** Customer Description [#customer-description] This description is visible to customers on the store page and payment link. Use it to explain what's included in the package — e.g., "Includes 10 court booking passes, valid for 3 months." *** Pricing [#pricing] | | FieldDescription | | -------------------------- | --------------------------------------------------------------------------------------------------------------- | | **Price** | The package price (e.g., $99.00) | | **Sales Tax** | Uses your club's default tax rate. Toggle "Use custom sales tax" to override. | | **Rule set-based pricing** | Optional — set different prices for different membership rule sets (e.g., members pay $79, non-members pay $99) | *** Booking Passes [#booking-passes] This is where you configure what's included in the package. Validity Period [#validity-period] Set how long the passes remain valid after purchase: * Enter an amount and unit (e.g., **3 months**, **30 days**, **1 year**) * Or check **"Never expires"** if passes should last indefinitely This validity period applies to **all passes** in the package. Adding Passes [#adding-passes] Click **"Add Pass"** to add a booking pass to the package. For each pass, configure: | | FieldDescription | | --------------------- | ----------------------------------------------------------------------------------- | | **Booking Pass** | Select the pass template from the dropdown | | **Number of Uses** | How many times the pass can be used (e.g., 10). Leave empty for unlimited. | | **Minutes Available** | For "By Hours" passes only — the total minutes in the pool (e.g., 600 for 10 hours) | You can add **multiple passes** to a single package. For example, a "Premium Bundle" could include: * 10 court reservation passes * 2 guest passes * 1 private lesson pass Click **"Add Pass"** again for each additional pass template. *** Deferred Start (Optional) [#deferred-start-optional] By default, passes become active immediately on purchase. Enable **Deferred Start** to let buyers choose a future start date — useful for seasonal packages or programs that start on specific dates. Toggle **"Enable deferred start"** to reveal the configuration: | | FieldDescription | | -------------------- | --------------------------------------------------- | | **Period Type** | How start dates are determined | | **Max Advance Days** | How far in the future buyers can choose (1–90 days) | **Period type options:** | | | Period TypeWhat it doesExample | | ------------------ | --------------------------------------------------------------------- | ------------------------------------------ | | **Calendar Week** | Buyer picks a start-of-week date. You choose which day of the week. | "Passes start every Monday" | | **Calendar Month** | Buyer picks a start-of-month date. You choose which day of the month. | "Passes start on the 1st of each month" | | **Calendar Year** | Buyer picks a start-of-year date. You choose the month and day. | "Passes start on January 1st" | | **Free Date** | Buyer picks any date within the advance window. | "Start whenever you want (within 60 days)" | When the buyer purchases the package, they'll see a date picker at checkout with the available start dates based on your configuration. *** Step 3: Save [#step-3-save] Click **Save** at the bottom of the form. The package is now created and available for purchase through the channels you selected. *** Example: 10-Pack of Court Bookings [#example-10-pack-of-court-bookings] **Scenario:** You want to sell a package of 10 court reservations for $150, valid for 3 months. **Prerequisites:** Create a "Court Booking" pass template (Event type, Whole Event, Pass Holder Only, Reservations only) — see [Free Reservation Pass](/help/facility-operators/booking-and-guest-passes/creating-a-free-reservation-pass). **Package setup:** | | SectionConfiguration | | ------------------------ | ------------------------------------------------------------------------------ | | **Name** | 10-Pack Court Bookings | | **Customer Description** | Book 10 court sessions at a discounted rate. Valid for 3 months from purchase. | | **Sales Channels** | Online Store: ON, POS: ON | | **Price** | $200.00 | | **Validity Period** | 3 months | | **Pass 1** | Court Booking — 10 uses | | **Deferred Start** | Off (passes start immediately) | **How it works for the customer:** 1. Customer finds the package on the club's online store 2. They purchase it for $200 3. 10 court booking passes are immediately allocated to their account 4. Each time they book a court, one pass is consumed 5. After 3 months, any unused passes expire *** Example: Beginner Bundle [#example-beginner-bundle] **Scenario:** A "Beginner Bundle" for $99 that includes 5 clinic passes and 3 hours of court time. **Prerequisites:** * A "Clinic Pass" template (Event type, Whole Event, Clinic only) * A "Court Hours" template (Event type, By Hours, Reservations only) **Package setup:** | | SectionConfiguration | | ------------------------ | ---------------------------------------------------------------------------- | | **Name** | Beginner Bundle | | **Customer Description** | Perfect for new players! Includes 5 group clinics and 3 hours of court time. | | **Price** | $99.00 | | **Validity Period** | 2 months | | **Pass 1** | Clinic Pass — 5 uses | | **Pass 2** | Court Hours — 180 minutes | | **Deferred Start** | Calendar Month, Day 1, Max 60 days | With deferred start on "Calendar Month, Day 1", the buyer can choose to start their bundle on the 1st of this month or next month. *** Packages vs Allocation Rules [#packages-vs-allocation-rules] | | | Pass PackagesAllocation Rules | | -------------------------- | --------------------------------------- | ---------------------------------- | | **How passes are granted** | One-time purchase | Automatically with membership | | **Recurring** | No — buy once | Yes — renews on schedule | | **Tied to membership** | No — anyone can buy | Yes — requires specific membership | | **Sold via** | Store, POS, payment link | N/A — automatic | | **Best for** | Drop-in packs, seasonal bundles, trials | Ongoing member benefits | Use **allocation rules** for recurring member benefits (e.g., "Gold members get 4 free reservations every month"). Use **packages** for one-time purchases that anyone can buy. *** Key Points [#key-points] * Pass templates must be created **before** you can add them to a package * The validity period applies to all passes in the package — you can't set different expiration dates for individual passes within a package * When a package is purchased, passes are allocated immediately (unless deferred start is enabled) * Packages appear as products in your store — customers don't need a membership to purchase them * You can edit a package after creation, but changes won't affect already-purchased packages * Rule set-based pricing lets you offer member discounts on packages (e.g., members pay $79, non-members pay $99) # Creating an Allocation Rule This is where you connect passes to memberships and define the schedule. 1. Go to **Booking Passes → Rules** 2. Click **Create Rule** 3. Configure: Which memberships receive this pass? [#which-memberships-receive-this-pass] Select one or more membership products. You can optionally narrow it down to specific price tiers within a product (e.g., only the "Annual" price, not the "Monthly" price). How often are passes allocated? [#how-often-are-passes-allocated] Set the recurrence schedule: | | | ScheduleWhen new passes are createdExample | | ----------- | -------------------------------- | ------------------------------------------ | | **Weekly** | Every N weeks on a specific day | Every Monday | | **Monthly** | Every N months on a specific day | 1st of every month | | **Yearly** | Every N years on a specific date | January 1st each year | | **Daily** | Every N days | Every day | | **Never** | One-time allocation only | On membership activation, no recurring | You can also set: * **Start date** / **End date** — Optional bounds for when the rule is active * **Pre-create advance days (Lead Time)** — How many days before a period starts to create the passes (e.g., 7 days ahead so members can see upcoming passes) Which passes to allocate? [#which-passes-to-allocate] Add one or more pass templates with their quantities: * Select a pass template * Set **number of uses** (e.g., 8 sessions) or leave unlimited * Set **amount of minutes** (for minute-based passes, e.g., 600 minutes = 10 hours) * Set **validity period** (e.g., 1 month — the pass expires at the end of the period) * Toggle **Never Expires** if the pass should stay valid indefinitely.\ **Note:** If this pass is issued on a recurring basis, any unused passes will roll over into the next period. You can add multiple pass templates to a single rule. For example, a "Premium" membership rule might allocate both "8 Open Play Sessions" and "50% Off Clinics" every month. Preview and Confirm [#preview-and-confirm] Before creating the rule, a preview shows: * How many currently eligible members will receive passes * Which passes will be allocated * When the first allocation will occur Click **Confirm** to create the rule. Passes are immediately allocated to all currently eligible members. # Creating and Managing Subscriptions *** Subscriptions are different from memberships. A membership usually defines a user’s core access and rules, such as how far in advance they can book courts or bays, how far in advance they can view the schedule, court or bay pricing, and other membership-level benefits. A subscription, on the other hand, gives the user additional recurring benefits on top of those membership benefits. Unlike memberships, users can have multiple subscriptions at the same time, and each subscription can provide its own set of benefits. These benefits are added alongside the user’s membership benefits, and the subscription billing cycle can be completely separate from the membership billing cycle. Before you start [#before-you-start] Before creating a subscription, you need to [create the **booking pass**](/help/facility-operators/booking-and-guest-passes/creating-booking-passes) that the subscription will deliver. The booking pass is the actual benefit the subscriber will receive on a recurring basis. Once that booking pass is ready, you can attach it to a subscription product. How to create a subscription product [#how-to-create-a-subscription-product] 1. Go to the **Subscriptions** page. 2. Click **New Subscription Product**. 3. Enter the product details: * **Name** * **Short description** * **Full description** (optional) * **Picture** (optional) 4. Choose how you want to sell the subscription: * in the **online store** on the homepage * through a **payment link** that you can send directly to users Set up the subscription tier [#set-up-the-subscription-tier] In the subscription product, add the tier details: * **Tier name** — for example, Monthly or Quarterly * **Price** * **Billing frequency** — for example: * every 1 month for a monthly subscription * every 3 months for a quarterly subscription You can also customize pricing: * set a default price for non-members * override the price for members * set different prices for different membership tiers Add subscription benefits [#add-subscription-benefits] After setting up the pricing and billing cycle: 1. Click **Add Benefit** 2. Select the **booking pass** you want to include in the subscription You can add more than one benefit if needed. This means a single subscription can include multiple booking passes, even different types of passes bundled together. Once everything is set up, click **Create Product**. How to add a subscriber [#how-to-add-a-subscriber] After the subscription product is created: 1. Go to the **Subscribers** section 2. Click **Add Subscriber** 3. Search for and select the user 4. Choose which subscription to assign to them Once added, the subscriber will appear on the Subscribers page. Managing subscribers [#managing-subscribers] On the **Subscribers** page, you can view and filter subscriptions by status, including: * Active * Past due * Frozen * Canceled Clicking into a subscription lets you see its details, including: * the next charge date * past invoices * payment history * the benefits included in the subscription * the booking passes the user received * how many uses are left * when the current booking pass expires for the active billing period For example, if the subscription renews monthly, you’ll see the details for the current month. If it renews quarterly, you’ll see the details for the current quarter. Example use cases [#example-use-cases] Subscriptions can be used for recurring packages such as: * 1 private lesson per month * 4 private lessons per month * 2 clinics per month * 4 clinics per month Summary [#summary] A subscription is a recurring product that automatically charges the user on a set schedule and gives them the booking pass or passes included in that subscription. To create one, first set up the booking pass, then create the subscription product, add its pricing and billing cycle, attach the benefits, and assign subscribers. # Creating Booking Passes For an overview of how passes work with memberships, see Booking Passes & Auto-Assignment. For common pass configurations (guest pass, hours-per-month, coach lesson pass, etc.), see Types of Booking Passes. *** Getting Started [#getting-started] 1. In the admin sidebar, click **Booking Passes** 2. Click the **Booking Passes** tab 3. Click **Create Booking Pass** *** Step 1: Basic Information [#step-1-basic-information] Every pass starts with a name and optional details. Name (required) [#name-required] The internal name your team uses to identify this pass. Members won't see this unless you leave Display Name empty. Choose something descriptive — e.g., "Monthly Guest Pass", "Off-Peak Court Hours", "Private Lesson Credit — Coach Sarah". Minimum 3 characters. Display Name (optional) [#display-name-optional] The name members see during checkout when the pass is applied. If left blank, the internal Name is shown instead. **When to use it:** When your internal naming is detailed (e.g., "2025 Q1 Promo — 50% Off Open Play") but you want members to see something cleaner (e.g., "50% Off Open Play"). Description (optional) [#description-optional] Internal notes for your team. Not visible to members. Use it to document the purpose of the pass, which membership it's tied to, or any special conditions. *** Step 2: Pass Type — Event or Coach [#step-2-pass-type--event-or-coach] Choose what the pass applies to: Court Reservations and Events (Clinics, Open Plays etc) or Coaches Lessons. Select the appropriate type. **This cannot be changed after the pass is created**, so choose carefully. *** Step 3: Redemption Mode [#step-3-redemption-mode] The redemption mode determines how the pass is consumed when used. Whole Event / Whole Lesson (default) [#whole-event--whole-lesson-default] One pass = one booking. Each time the member joins an event or books a lesson, one pass is consumed regardless of the event's duration. **Best for:** Session-based passes like "4 open play sessions per month" or "2 private lessons per month". By Hours (Event passes only) [#by-hours-event-passes-only] The pass holds a pool of hours. Each booking deducts the event's duration from the pool. For example, a 10-hour pass used for a 90-minute booking has 8.5 hours remaining. **Best for:** Flexible court time passes where members can split their hours across multiple bookings of varying lengths. **Note:** The hour pool is set when you create the allocation rule, not on the template itself. The template just defines the redemption mode. Percentage Discount [#percentage-discount] The pass provides a percentage off the booking price each time it's used. Enter the discount percentage (0.01%–100%, up to 2 decimal places). **Best for:** Membership perks like "20% off all clinics" or "50% off lessons". Fixed Discount [#fixed-discount] The pass provides a fixed dollar amount off the booking price. Enter the discount amount (minimum $0.01). **Best for:** Flat-rate discounts like "$10 off any court reservation" or "$25 off private lessons". > **Important:** The redemption mode cannot be changed once passes have been allocated to members or have redemption history. *** Step 4: User Type — Who Can Use the Pass [#step-4-user-type--who-can-use-the-pass] This controls whether the pass applies to the pass holder, their guests, or both. Both Pass Holder and Guests (default) [#both-pass-holder-and-guests-default] The pass can be used by the member themselves and by any guests they add to events or reservations. Pass Holder Only [#pass-holder-only] Only the member who owns the pass can use it. Guests they invite are not covered. Guest Only [#guest-only] The pass only applies to guests — not the member themselves. This is the key setting for creating **guest passes**. When a member adds a guest to an event or reservation, the guest's fee is covered by this pass. **Tip:** To give members a clean setup, create two separate passes: a "Pass Holder Only" pass for their own sessions and a "Guest Only" pass for bringing friends — each with its own allocation limit. *** Step 5: Family Sharing [#step-5-family-sharing] Toggle **"Can be redeemed by family members"** to allow all linked family members to use the pass. When enabled: * Every family member linked to the pass holder automatically becomes a pass holder too * They can use the pass independently — no need to book through the primary member * This applies to all existing and future allocations of this pass template When disabled (default), only the person the pass is allocated to can use it. **Example:** A parent has a "Monthly Court Hours" pass with family sharing enabled. Their kids, who are linked as family members, can each book courts independently using the same pass pool. *** Step 6: Restrictions [#step-6-restrictions] Restrictions define where and when the pass can be used. The available restrictions depend on the pass type. Event Restrictions (Event passes only) [#event-restrictions-event-passes-only] You must select at least one event type or event tag. The form shows two columns: **By Event Type** (left column) — check which event types qualify: * Reservations * Open Play * Tournament * League * Clinic * Other **By Event Tag** (right column) — select specific tags: * Choose from your club's event tags (e.g., "Beginner", "Youth", "Ladies Night") Event types and tags use **OR logic** — an event qualifies if it matches any selected event type **OR** has any of the selected tags. The **summary box** at the bottom shows a description of what qualifies so you can verify before saving. Coach Restrictions (Coach passes only) [#coach-restrictions-coach-passes-only] Choose how specific the pass should be: **All coach lessons** — the pass works for any coach at the club, for any service they offer. Simple and broad. **Specific coaches** — select exactly which coaches the pass works for. The form shows a list of all active coaches with checkboxes. For each selected coach, you can optionally restrict the pass further to specific services: 1. Click the expand arrow next to the coach's name 2. By default, the pass works for **all services** that coach offers 3. Check specific services to limit the pass to only those (e.g., "Private Lesson — 1 Hour" but not "90 Minute Lesson") If a coach has no services checked, the pass covers all their services. If specific services are checked, only those are covered. **Example:** A pass restricted to two Coaches "Private Lesson — 1 Hour" service. If the member tries to book their 30-minute lesson or a different coach entirely, the pass won't apply. Time of Day Restrictions (Event passes only) [#time-of-day-restrictions-event-passes-only] Optionally limit when the pass can be used based on the time of the event. 1. Toggle on **"Enable time restriction"** 2. Configure allowed time windows in the schedule editor: * Toggle which days of the week the pass is valid * Set time ranges for each day (e.g., Monday–Friday 6:00 AM – 3:00 PM) * Add multiple non-overlapping time slots per day * Add date-specific overrides (e.g., block holidays) Time restrictions are applied **in addition to** event type and tag restrictions — the event must match both to qualify. **Example:** An "Off-Peak Court Pass" restricted to weekdays before 3 PM. A 5 PM booking won't use this pass — the member pays full price. **Note:** Time restrictions are not available for Coach passes. Coach scheduling is managed through the coach's own availability settings. *** Step 7: Save [#step-7-save] Click **Create Booking Pass** at the bottom of the page. The form validates all fields — if anything is missing or invalid, it scrolls to the first error. On success, you're redirected to the Manage page where your new pass template appears in the list. *** What's Next After Creating a Pass [#whats-next-after-creating-a-pass] A pass template by itself doesn't do anything — you need to **allocate** it to members. There are two ways: 1. **Manual allocation** — Go to a member's profile and allocate the pass directly 2. **Allocation rules** — Set up automatic allocation tied to a membership plan. When a member activates that membership, the pass is automatically allocated. See Booking Passes & Auto-Assignment for details. *** Editing an Existing Pass [#editing-an-existing-pass] Go to **Booking Passes → Manage** and click on a pass template to edit it. Most fields are editable at any time, with two exceptions: * **Pass Type** (Event vs Coach) — locked after creation * **Redemption Mode** — locked once the pass has been allocated to any member or has redemption history A yellow warning box appears on locked fields explaining why they can't be changed. *** Tips [#tips] * **Name passes clearly** — admins will see these names in allocation rules, reports, and member profiles. Use descriptive names like "Gold — 10hr Court Pass" rather than "Pass 1". * **Use Display Name for clean member-facing labels** — keep internal names detailed for your team, and set a short Display Name for what members see at checkout. * **Family sharing shares the pool, not individual allocations** — if a pass has 10 hours and family sharing is on, the entire family draws from the same 10-hour pool. * **Test restrictions with the summary box** — the green/amber summary at the bottom of the Event Restrictions section shows exactly which events qualify. Verify it before saving. * **You can always add restrictions later** — start simple. If a "Whole Event" pass for all event types is too broad, you can edit it and add tag or time restrictions without affecting existing allocations. * **Combine with allocation rules for automation** — the real power of passes comes from automatic allocation with memberships. Create the template here, then set up an allocation rule to deliver it when members activate their plan. # Booking & Guest Passes Booking passes are member benefits: free court time, event entries, lessons or guest visits that renew on a schedule. Start with what passes are, then create templates, allocation rules and packages. Articles [#articles] * [What Are Booking Passes?](/help/facility-operators/booking-and-guest-passes/what-are-booking-passes) — Booking passes are benefits that members receive as part of their membership — things like "4 open play sessions per month", "10 hours of court time", "20% off clinics", or "unlimited drop-in access". * [Redeeming Booking Passes / Guest Passes](/help/facility-operators/booking-and-guest-passes/redeeming-booking-passes-guest-passes) * [Creating Booking Passes](/help/facility-operators/booking-and-guest-passes/creating-booking-passes) — This guide walks you through creating a booking pass template step by step. * [Creating an Allocation Rule](/help/facility-operators/booking-and-guest-passes/creating-an-allocation-rule) * [Auto-Allocating with Memberships](/help/facility-operators/booking-and-guest-passes/auto-allocating-with-memberships) * [Creating a Guest Pass](/help/facility-operators/booking-and-guest-passes/creating-a-guest-pass) — Give your members a set number of free guest visits per month. * [Creating a Free Reservation Pass](/help/facility-operators/booking-and-guest-passes/creating-a-free-reservation-pass) — Give your members a set number of free court reservations per month. * [Creating a Free Court Hours Pass](/help/facility-operators/booking-and-guest-passes/creating-a-free-court-hours-pass) — Give your members a pool of free court hours per month. * [Create a Free Event Pass](/help/facility-operators/booking-and-guest-passes/create-a-free-event-pass) — Give your members a set number of free event entries per week/month. * [Creating a Free Pass for Events with a Specific Tag](/help/facility-operators/booking-and-guest-passes/creating-a-free-pass-for-events-with-a-specific-tag) — Give your members free access to events tagged with a specific category — like a skill level, age group, or program name. * [Creating a Free Lesson Pass](/help/facility-operators/booking-and-guest-passes/creating-a-free-lesson-pass) — Give your members free private lessons with a specific coach. * [Create a Family-Shared Pass](/help/facility-operators/booking-and-guest-passes/create-a-family-shared-pass) — Let an entire family share a single pass pool. * [Creating a Pass Package](/help/facility-operators/booking-and-guest-passes/creating-a-pass-package) — A Pass Package is a product that bundles one or more booking passes together. * [Creating and Managing Subscriptions](/help/facility-operators/booking-and-guest-passes/creating-and-managing-subscriptions) — A subscription lets you give users recurring benefits on a set schedule, such as every month or every quarter. # Redeeming Booking Passes / Guest Passes There are 3 types of booking passes: 1. Passes for you only — they allow you a certain number of free court reservations and/or participations in Open Plays, Clinics etc. or a certain number of hours. 2. Passes that you can share with your family members 3. Guest Passes — they allow to bring certain number of guests for free. Let’s see how to redeem them with one click. Using a booking pass for yourself [#using-a-booking-pass-for-yourself] 1. First, you choose the time and the court as usual or sign up for an event. 2. If you have any passes applicable to the reservation or this event you’ll see **"Apply Booking Pass"** checkbox. You can uncheck it if you prefer to not use it this time. 3. Once selected, the total will update to $0 (or reduced if partial coverage). 4. The booking pass usage count will update automatically. *** Using a booking pass for you family member [#using-a-booking-pass-for-you-family-member] 1. First, you choose the time and the court as usual or sign up for an event. 2. If you have family members linked to your account, you’ll see a dropdown menu that lets you select either yourself or a family member when signing up for an event or making a reservation. 3. Pick any family member and the booking pass will be applied automatically if it can be shared with the family