# Revenue analytics
Source: https://docs.1club.ai/analytics/billing
Revenue, payments, outstanding balances, and average charge value, broken down by source, revenue account, and plan over time.
The **Revenue** category in the **Analytics** hub is the financial snapshot of your organization. It shows what you billed, what you collected, and what is still outstanding, broken down by where the revenue came from (bookings, memberships, or other), by revenue account, and by plan. Owners and managers use it to track how the business is doing.
## When to use it
Use it for the higher-level "how are we doing this month?" view, without exporting raw charges. Typical questions it answers:
* **Which membership plans bring in the most revenue?** - a tennis and padel gym sees membership revenue growing while court bookings stay flat, then checks the **By plan** tab to find the tier behind it.
* **Are we collecting what we bill?** - a muay-thai gym watches the collection rate and the overdue invoice count, and runs a bill run when overdue invoices climb.
* **Did collection keep up with a billing spike?** - a large gym reads paid versus unpaid by day to catch a stretch of annual renewals where collection lagged.
* **Is revenue landing in the right accounts?** - a reformer studio maps class and retail revenue to its revenue accounts and exports the month for its bookkeeper.
For day-to-day invoice and payment work, use [Sales > Finance > Invoices](/sales/invoices) or [Sales > Finance > Payments](/sales/payments) instead. See [Worked examples](#worked-examples) for more.
## What "revenue" means here
* A line item appears in **revenue** when its invoice is issued, regardless of payment status.
* A line item appears in **paid** when a payment has been applied to it.
* The difference is your **outstanding** balance for that period.
If a booking was made in January but paid in March, it counts toward January's revenue (it is invoice-date based) and March's payments (it is payment-date based). For most reporting this is what you want; if you need strict cash accounting, lean on the **Payments report** below, which is payment-date based.
## Reports in this category
Open **Analytics** and pick the **Revenue** category to reach four reports:
* **Revenue dashboard** - KPI cards plus a stacked revenue-versus-paid trend by source.
* **Revenue report** - A month-by-month matrix with **By source**, **By account**, and **By plan** tabs.
* **Payments report** - Billed, paid, and unpaid amounts with a payments-by-day chart.
* **Invoices dashboard** - Invoice counts and amounts by status, with collection rate and payment methods.
Every report shares the same filter bar and exports to CSV where an **Export** button is shown.
## Filters
* **Date range** - Sets the reporting period. Defaults to the last 30 days. Pick a preset or a custom range on the calendar, and step through periods with the arrows.
* **Billing entity** - On the **Revenue**, **Revenue Report**, and **Invoices Dashboard** reports, narrows the figures to one company's money. It only appears when your organization has two or more [billing entities](/settings/billing-entities).
* **Club** - There is no club control on the report. Reports follow the club selected in the breadcrumb at the top of the admin; **All clubs** shows the whole organization.
## Revenue dashboard
The **Revenue dashboard** (**Analytics** > **Revenue** > **Revenue**) is the headline view.
KPI cards, three across the top:
* **Total revenue** - Sum of all invoices issued in the date range (gross, before payment).
* **Total payments** - Sum of all payments received in the date range. Compare with total revenue to gauge your collection rate.
* **Average charge value** - Mean amount per charge across the period.
### Revenue trends chart
A stacked bar chart of revenue versus paid amounts, split into three sources:
* **Bookings** (blue) - Class bookings, area bookings, drop-ins.
* **Memberships** (green) - Plan subscriptions and one-off membership purchases.
* **Other** (yellow) - Everything that is not a booking or membership (POS, retail, manual invoices, and so on).
Within each source, the **paid** portion is rendered solid and the **unpaid** portion is rendered semi-transparent on top, so a tall translucent bar means you billed but did not collect. The time bucket adapts to your date range: daily for short ranges, weekly for medium ranges, monthly for long ones.
## Revenue report
The **Revenue report** (**Analytics** > **Revenue** > **Revenue Report**) is a month-by-month matrix with three tabs. Click any month cell to drill down into the charges behind that figure.
* **By source** - Revenue grouped by **Revenue Source** (bookings, memberships, other).
* **By account** - Revenue grouped by **Revenue Account**, showing each account's name and code. Use this for bookkeeping and for mapping revenue to your chart of accounts.
* **By plan** - Revenue grouped by **Plan**, with each plan's billing frequency shown. Use this to see which membership plans drive the most revenue.
The **Export** button downloads the tab you are viewing: the By source tab exports the revenue report, By account exports the charges (revenue-account) report, and By plan exports the revenue-by-plan report.
## Payments report
The **Payments report** (**Analytics** > **Revenue** > **Payments Report**) is payment-date based and focuses on collection.
KPI cards:
* **Total revenue** - Amount billed in the period.
* **Paid** - Payments collected.
* **Unpaid** - The gap between billed and paid.
The **Payments by day** chart stacks paid (green) and unpaid (red) amounts per day, so you can see collection cadence and spot days where billing outran collection.
## Invoices dashboard
The **Invoices dashboard** (**Analytics** > **Revenue** > **Invoices Dashboard**) is the invoice-level view.
Count cards: **Total invoices**, **Paid invoices**, **Pending invoices**, **Overdue invoices**.
Amount cards: **Total invoice amount**, **Paid amount**, **Pending amount**, and **Collection rate** (paid as a percentage of billed).
Charts:
* **Invoice trends** - Invoice count and amount over time.
* **Payment methods** - A pie of amount collected per payment method.
* **Invoice status breakdown** - Paid, pending, overdue, and cancelled invoice counts.
Use collection rate and the overdue count together to decide when to run reminders or a [bill run](/sales/bill-runs).
## Exporting reports
Reports with an **Export** button download a CSV of what is on screen, honoring your filters. The **Revenue report** exports per tab (source, revenue account, or plan). For outstanding balances per member, the **Balance report** (under the **Members** analytics category) exports each member's due and credit amounts. For bundled accounting exports such as the QuickBooks bundle, use [Settings > Data Tools > Export](/settings/export).
## Worked examples
**Racket sports - membership versus court revenue.** A tennis and padel club opens the **Revenue dashboard** and sees the Memberships (green) bars growing while Bookings (blue) stay flat. They switch to the **Revenue report** By plan tab to confirm which membership tier is driving the growth, then export it for the owner.
**Martial arts - collecting on overdue invoices.** A muay-thai gym checks the **Invoices dashboard**, sees a rising **Overdue invoices** count and a collection rate under 90%, and kicks off a [bill run](/sales/bill-runs) to chase the overdue balances.
**Pilates / Yoga - reconciling the month.** A reformer studio uses the **Revenue report** By account tab to map class and retail revenue to the right revenue accounts, drills into a month cell to verify a large figure, and exports the CSV for their bookkeeper.
**Gym / Fitness - watching cash collection.** A large fitness club uses the **Payments report** to track paid versus unpaid by day across the month, catching a stretch where billed amounts spiked (annual renewals) but collection lagged, then follows up on the unpaid invoices.
**Climbing - is the intro offer paying off?** A climbing gym compares the **Revenue dashboard** Other (yellow) source against the trend, confirming that day-pass and gear revenue rose alongside the promotion rather than cannibalizing memberships.
## Tips & best practices
* Set the **Date range** before reading any number. Reports default to 30 days, so a surprising "zero" usually means the period or the selected club is excluding data.
* Use the **Revenue dashboard** for the trend and the **Revenue report** for exact monthly figures; drill into a cell whenever a number looks off.
* Reconcile the chart of accounts with the **By account** tab monthly, so exports to your accounting system are already mapped correctly.
* When you need strict cash accounting, read the **Payments report** (payment-date based) rather than the revenue figures (invoice-date based).
* Pair the **Invoices dashboard** collection rate with the **Balance report** to see both the aggregate and the per-member outstanding picture.
## Troubleshooting
* **Total payments is lower than total revenue.** You have outstanding invoices in the period, or some invoices were issued in this period but paid earlier. Look at the translucent (unpaid) portions of the revenue trend bars to find the gap.
* **A number is zero when you know you transacted.** Check the date range first (it defaults to 30 days), then the club selected in the breadcrumb and the billing entity filter.
* **Refunds seem to change the totals.** Refunds net into payment totals but not into the revenue total, so a $100 invoice followed by a $50 refund shows as $100 revenue and $50 net payment.
* **Revenue by account has an "unassigned" or default row.** Line items without a mapped revenue account fall into the default account. Map them on the plan, product, or pricing rule so future revenue lands in the right account.
* **The Export button is disabled.** There are no rows for the current filters. Widen the date range or clear a filter, then export.
## Who can see this
Revenue analytics is part of the analytics module, gated on the `analytics.read.club` permission (broader analytics permissions satisfy it through the permission hierarchy). Roles that lack club-scoped analytics see only their own-scope data and will not see these reports. See [Roles and permissions](/reference/roles-permissions).
## Related
* [Schedule and instructor analytics](/analytics/schedule) - Check-ins, bookings, classes, and instructor reports.
* [Sales > Finance > Invoices](/sales/invoices) - Day-to-day invoice management.
* [Sales > Finance > Payments](/sales/payments) - Individual payments and their statuses.
* [Sales > Plans](/sales/plans) - Membership plans and their revenue accounts.
* [Sales > Finance > Bill runs](/sales/bill-runs) - Batch billing and collection.
* [Settings > Data Tools > Export](/settings/export) - Pull invoices, payments, or the QuickBooks bundle as CSV.
* [Analytics overview](/analytics/overview) - Other reports.
# Clubs analytics
Source: https://docs.1club.ai/analytics/clubs
Today and historical dashboards for club-level activity, revenue, and utilization, per location.
The **Clubs** category in **Analytics** rolls up activity, revenue, and resource utilization for each of your locations. It answers two questions: what is happening in the gym right now, and how have the numbers trended over a period you choose. Both dashboards read live from your operational data, so bookings, check-ins, and payments show up as they land. Owners and managers use it to run each location and compare them.
## When to use it
Use it to watch the day as it happens, compare your locations, and decide where you have spare or missing capacity. Typical questions it answers:
* **Which location's courts are underused?** - an operator with two padel venues compares average area utilization per venue over 90 days, then checks the day-of-week pattern to see when the quieter site fills.
* **Why is revenue low today when bookings look normal?** - a CrossFit box sees check-ins tracking as usual on the hourly chart while revenue stays flat, a sign of unpaid or comped bookings.
* **Is there demand for another class slot?** - a yoga studio reads average class utilization alongside the weekday pattern before adding a second morning reformer class.
See [Worked examples](#worked-examples) for how to read each of these.
## The two dashboards
Open **Analytics** and pick the **Clubs** category to reach two dashboards:
* **Today's performance** (`/analytics/clubs/today`) - A same-day snapshot of bookings, revenue, active members, and check-ins, broken down by hour. It refreshes on its own every five minutes.
* **Performance trends** (`/analytics/clubs/historical`) - The same metrics plus resource utilization over a date range you pick, with daily trends and day-of-week patterns.
Both dashboards follow the club selected in the breadcrumb at the top of the admin. Leave it on **All clubs** to see the whole organization, or pick one location to scope every card and chart to it. Numbers are counted in your organization's time zone (set under **Settings** > **Organization** > **Details**), so "today" means today where the gym is, not where you are.
## Today's performance
This is the live operations view. It shows the current date and the timestamp of the last completed hour at the top, and re-queries every five minutes so you can leave it open on a screen at the front desk. It has no date filter, because it is always scoped to today.
### KPI cards
The top rows summarize the day so far:
* **Bookings created** - Bookings made for today, regardless of status.
* **Revenue today** - Net revenue recognized today. This counts money actually taken in: it excludes voided charges, reversals, and the portion of any sale paid from wallet credit rather than fresh payment.
* **Active members** - Distinct members who have a booking or a check-in today.
* **Check-ins** - Total check-ins recorded today.
* **Bookings confirmed** - Bookings for today currently in confirmed status.
* **Bookings cancelled** - Bookings for today that were cancelled.
* **Memberships created** - New memberships sold today.
* **Revenue from bookings** - The share of today's net revenue that came from bookings (as opposed to membership sales).
### Hourly trends chart
A combined chart plots **bookings created**, **revenue**, and **check-ins** hour by hour across the day. Use it to see when your day peaks. Hover any hour for exact figures, and click a series name in the legend to hide or show it.
The Today view focuses on activity and revenue by hour. For utilization rates (class, area, and instructor occupancy), use **Performance trends** below. Utilization is computed over completed days, not the current in-progress hours.
## Performance trends
This is the historical view. Set a date range in the filter bar (it defaults to the last 30 days) and, optionally, pick a single club in the breadcrumb. As the range widens, the trend charts automatically group points by day, week, or month so the chart stays readable.
### KPI cards
* **Total bookings** - Bookings created in the range.
* **Total revenue** - Net revenue for the range, on the same real-money basis as the Today view.
* **Total memberships** - Memberships sold in the range.
* **Total check-ins** - Check-ins recorded in the range.
* **Average booking value** - Total revenue divided by total bookings.
* **Average class utilization** - Average share of class seats filled.
* **Average area utilization** - Average share of area operating hours booked.
* **Average instructor utilization** - Average share of instructor operating hours booked.
### How utilization is calculated
Utilization compares what was booked against what was available, expressed as a percentage:
* **Class utilization** - Booked seats divided by class capacity (the sum of each class's maximum capacity for the day).
* **Area utilization** - Booked hours divided by the area's available hours, where availability comes from the area's (or club's) operating hours.
* **Instructor utilization** - Booked instructor hours divided by the instructor's available hours from their operating hours.
Because availability is driven by operating hours, keeping **Settings** > **Clubs** > **Areas** and instructor schedules accurate directly affects whether these percentages are meaningful.
### Charts
* **Daily trends** - Bookings, check-ins, and revenue over the range on one chart.
* **Utilization trends** - Class, area, and instructor utilization percentages over time, with revenue overlaid as a dashed line so you can see whether busier resources actually convert to income.
* **Day of week patterns** - Average bookings, revenue, and check-ins for each weekday, so you can tell a quiet Tuesday from a packed Saturday.
## Worked examples
### Comparing two padel locations
You run two padel venues and want to know which courts are underused. Open **Performance trends**, set the range to the last 90 days, and note the **average area utilization** with **All clubs** selected in the breadcrumb. Then switch to each venue in turn. If the downtown site sits at 78% area utilization and the suburban site at 41%, the suburban courts have slack. Cross-check the **day of week patterns** chart per site: if the suburban venue only fills on weekends, the fix is midweek programming or off-peak pricing rather than more courts.
### Spotting a revenue dip early
A CrossFit box keeps **Today's performance** open at reception. Mid-afternoon the **revenue today** card is well below a normal day while **bookings created** looks normal. The **hourly trends** chart shows check-ins tracking as usual but revenue flat. That pattern points to unpaid or comped bookings rather than low attendance. Confirm it in the [bookings dashboard](/analytics/schedule) or the bookings **Audit**, which highlights unpaid bookings.
### Checking capacity before adding a class
A yoga studio is considering a second morning reformer slot. On **Performance trends**, filter to the studio and read **average class utilization** alongside the **day of week patterns** bars. If mornings already run near capacity and weekday demand is even, the second slot is justified. If utilization is 55% and concentrated on two days, promote the existing class first.
## Tips and best practices
* **Start with All clubs, then drill in.** Read the organization-wide numbers first, then switch the breadcrumb to a single location to explain an outlier.
* **Set the date range once.** Both charts on Performance trends share the range from the filter bar, so you only set it once per visit.
* **Leave Today open at the desk.** It refreshes every five minutes on its own, which makes it a good live board for front-of-house staff.
* **Fix operating hours before trusting utilization.** Utilization percentages depend on operating hours for areas and instructors. If they look wrong, verify the schedule under **Settings** > **Clubs** > **Areas** first.
* **Use the legend to declutter.** On busy charts, click a series in the legend to hide it and focus on one metric.
## Troubleshooting
| Symptom | Likely cause | Fix |
| - | - | - |
| Performance trends shows nothing | No activity in the selected range or club | Widen the date range, or switch the breadcrumb to **All clubs**. |
| Utilization percentages look far too low or too high | Operating hours for areas or instructors are missing or wrong, so available capacity is miscalculated | Check and correct operating hours under **Settings** > **Clubs** > **Areas** and on instructor schedules. |
| Revenue is lower than the cash you expected | Net revenue excludes voided charges, reversals, and any part of a sale paid from wallet credit | This is expected. Wallet-funded amounts are not new money in. Reconcile against [Analytics > Billing](/analytics/billing). |
| Today's revenue looks stalled while check-ins keep rising | Bookings are being taken without payment or are comped | Review unpaid bookings in **Analytics** > **Bookings** > [**Audit**](/analytics/schedule#booking-audit). |
| A club is missing from the breadcrumb | Your account only has access to certain locations, or the club is inactive | Confirm your access and the club's status under [Reference > Organization and clubs](/reference/organization-clubs). |
| Today's numbers seem an hour or a day off | "Today" uses the organization's time zone, not your device's | Verify the time zone under **Settings** > **Organization** > **Details**. |
## Related
* [Analytics overview](/analytics/overview) - How every dashboard is laid out and who can see what.
* [Members analytics](/analytics/members) - Member growth, activity, and balances.
* [Schedule and instructor analytics](/analytics/schedule) - Check-ins, bookings, and classes dashboards, including the booking audit.
* [Revenue analytics](/analytics/billing) - Revenue, payments, and invoices in detail.
* [Settings > Clubs > Areas](/settings/areas) - Manage areas, capacity, and operating hours that drive utilization.
* [Reference > Organization and clubs](/reference/organization-clubs) - How clubs and locations are set up.
# Engagement analytics
Source: https://docs.1club.ai/analytics/engagement
Member engagement scores (0-100) - how they are calculated, the four scoring categories, the dashboard, and using scores for churn-risk and win-back.
The **Member Engagement** dashboard (**Analytics** > **Members** > **Member Engagement**) shows how engaged your members are right now and how their scores have moved over time. An engagement score is a 0 to 100 number per contact, written by a background job that combines four categories: financial activity, active services, physical activity, and overall member engagement (consistency, tenure, and value). Owners and managers use it to see who is thriving and who is going quiet.
Engagement *analytics* (this page) is the 0 to 100 scoring dashboard. The smart-tag category that groups members by recent check-ins is called **Attendance** and lives under [Smart tagging](/settings/smart-tagging). They are related but different systems - the score is a single number; Attendance is a tag.
## When to use it
The score turns each member's payments, memberships, visits, and tenure into one number, so you can spot at-risk members early and size a win-back or loyalty offer. Typical questions it answers:
* **Are more members slipping?** - watch whether the **Low** count grows week over week.
* **How big is your win-back audience?** - the **Low** count, which picks up quiet members such as a reformer client who paused for the summer and stopped visiting, sizes a win-back promotion or automation.
* **How many members could you ask for a referral?** - the **Rockstar** count sizes a loyalty offer or referral ask.
* **What happened with this member?** - pick one member to see how their score moved when you investigate a churn case.
See [Using this for churn-risk and win-back](#using-this-for-churn-risk-and-win-back) and the [worked examples](#worked-examples) for more.
## Score levels
The score maps to one of four levels. The thresholds are fixed in code and are the same everywhere a level appears (the dashboard distribution, the score chip on member pages, and any tag-based automations driven by engagement):
| Level | Score range | Meaning |
| - | - | - |
| **Rockstar** | 75 to 100 | Highly engaged, frequent users |
| **High** | 50 to 74 | Well-engaged regulars |
| **Medium** | 25 to 49 | Moderate activity |
| **Low** | 0 to 24 | At-risk, infrequent |
## How scoring is calculated
The final score is a weighted average of four category scores, each itself a 0 to 100 number. By default all four weigh **25%**:
* **Financial activity** (25%) - recent charges and payment activity.
* **Active services** (25%) - active memberships and service usage.
* **Physical activity** (25%) - check-ins, bookings, and visit frequency.
* **Member engagement** (25%) - activity consistency, tenure, and lifetime value.
Weights and time windows are configurable per organization under [Smart tagging](/settings/smart-tagging). If you change the weights so they no longer sum to 100, the service normalizes them automatically before applying. The final score is rounded and clamped to the 0 to 100 range. **If scoring is disabled under Smart tagging, this dashboard shows no data.**
### Time windows
Three windows drive the math:
* **Recent activity** - derived from your engagement tag period (30 days if not set). This is the main window for "did they do anything lately".
* **Very recent activity** - fixed at **7 days**. Activity inside this window earns a bonus.
* **Historical data** - the fetch window for gathering a member's raw activity (90 days by default).
### What each category rewards
Each category starts at 0 and builds up. Understanding the steps helps you read a score.
* **Financial activity** - 0 if there are no charges in the recent window. Otherwise it starts at 50, adds 30 if there was a charge in the last 7 days, and adds 20 for two or more recent charges (capped at 100).
* **Active services** - 0 if the member has no active membership. Otherwise it starts at 70, adds 15 for more than one active membership, and adds 15 for tenure of three months or more. A **paused** membership still counts as active here, because the contractual relationship is intact - a temporary hold should not crater the score.
* **Physical activity** - 0 if there were no check-ins in the recent window. Otherwise it starts at 40, adds 30 for a check-in in the last 7 days, adds 20 for four or more recent check-ins (or 10 for two or more), and adds 10 if there is any recent booking.
* **Member engagement** - combines three signals: recent check-in consistency (up to 40), tenure (up to 30 for a year or more), and lifetime spend (up to 20 for spend over 1,000).
Scores are recalculated only for **active members** (contacts of type member with at least one active membership). A member without an active membership will not get a fresh score on the nightly run.
### When scores are written
Scores are not computed live. They are written by the daily member smart-tagging job that runs **nightly at 03:00 UTC**, right after tags are updated, and on demand when you use **Sync now** on the [Smart tagging](/settings/smart-tagging) page. The dashboard reads the stored score, so newly created activity is reflected only after the next run. Each time a score changes, the change is recorded as a contact fact, which is what powers the progression chart and the per-member history.
## Worked examples
These use the default equal weights (25% each) and default windows (30-day recent, 7-day very-recent).
### A regular who just paid (martial arts)
A jiu-jitsu member trained 5 times in the last 30 days (2 of them this week), has an active membership going back 8 months, and paid their monthly dues 3 days ago.
* Financial: recent payment, within 7 days = 50 + 30 = **80**.
* Services: active membership + 8 months tenure = 70 + 15 = **85**.
* Physical: check-ins in window, one this week, 4+ visits = 40 + 30 + 20 = **90**.
* Engagement: 4+ check-ins (40) + 6-to-12-month tenure (20) = **60**.
* Score = (80 + 85 + 90 + 60) / 4 = **79 -> Rockstar**.
### A quiet member on a hold (Pilates / yoga)
A reformer client paused their membership for the summer, last visited 6 weeks ago, and has made no payments in the last 30 days.
* Financial: no recent charges = **0**.
* Services: paused membership still counts as active, 5 months tenure = 70 + 15 = **85**.
* Physical: no check-ins in the recent window = **0**.
* Engagement: no recent check-ins, 5-month tenure (10) = **10**.
* Score = (0 + 85 + 0 + 10) / 4 = **24 -> Low**.
The high services score keeps them off zero, but the Low level correctly flags that they have gone quiet - a good win-back candidate.
### Weighting for a check-in-driven gym
A strength-and-conditioning gym cares more about attendance than spend. Under Smart tagging you raise **Physical activity** to 40% and **Member engagement** to 30%, dropping Financial and Services to 15% each. Now a member who shows up constantly but pays annually (few charges) scores higher, matching how you actually think about engagement.
## What is on the page
### Filters
* **Time range** - the standard analytics range picker. Like other day-based dashboards it defaults to the last 30 days; widen it to see a longer progression.
* **Contact type** - restrict the chart to specific contact types (member, drop-in, and so on).
* **Member** - select a single contact to switch the chart from organization-wide averages to that member's own progression.
### KPI cards
Four cards across the top:
* **Total members** - count of contacts of type member or drop-in in your organization (archived contacts are excluded).
* **Average score** - mean across members who have a score, with the count of scored members shown underneath.
* **Recent tag changes** - tag additions and removals recorded in the last 7 days.
* **Recent score changes** - score updates recorded in the last 7 days.
### Score distribution
A row of four counts showing how many members sit in each level: **Rockstar** (green), **High** (blue), **Medium** (amber), **Low** (red). This is your at-a-glance health check.
### Progression chart
A line chart of the average engagement score over time. The Y-axis is fixed at 0 to 100 and the time bucket adapts to the range. The line is built from recorded score-change events grouped by day and averaged, so it tracks how scores move as the nightly job re-runs, rather than a snapshot of every member each day.
When you select a single member, the chart switches to that member's individual score history (rendered green instead of blue) and the legend changes from **Average score** to **Engagement score**. A panel above the chart shows the member's **current score** and any tags. If a member has no score yet, you will see **No score** and the chart will be empty.
## Using this for churn-risk and win-back
* **Spot at-risk members early.** If the **Low** count grows week over week, members are slipping. Cross-check against recent tag changes for context.
* **Build a win-back audience.** Use the **Low** count as the size of a re-engagement campaign, then target those members with a promotion or an automation.
* **Reward Rockstars.** Use the Rockstar count as the audience size for a loyalty offer or referral ask.
* **Track a specific member's journey.** Pick a member in the dropdown to see how their score moved - useful when investigating a churn case or qualifying a referral candidate.
* **Tune before you trust.** Calibrate weights and windows under Smart tagging until the levels match your instinct about who is engaged, then use the dashboard for tracking.
## Tips and best practices
* **Match the weights to your model.** A high-volume gym should weight Physical activity; a boutique studio that sells packages might weight Financial and Services. See the [check-in-driven gym example](#weighting-for-a-check-in-driven-gym).
* **Set the recent-activity window deliberately.** It comes from your engagement tag period. A 30-day window suits most clubs; shorten it if your members visit several times a week and you want the score to react faster.
* **Give new data time.** Scores update on the nightly run (or via **Sync now**). Do not expect a payment made an hour ago to move the dashboard immediately.
* **Read the level, not just the number.** A Low member with a high services score (paused membership) is a different case from a Low member with no membership at all - open their profile to see which.
## Troubleshooting
* **Dashboard shows no data at all** - Symptom: cards read 0 and the chart is empty. Cause: engagement scoring is disabled under Smart tagging (the metrics endpoint returns nothing when scoring is off). Fix: enable scoring under [Smart tagging](/settings/smart-tagging).
* **A member shows No score** - Symptom: the per-member panel reads "No score". Cause: the member has no active membership, or the nightly job has not run since they became active. Fix: confirm they have an active membership, then run **Sync now** on the Smart tagging page.
* **Average score looks too high** - Symptom: the average seems better than reality. Cause: members without a score are excluded from the average, so a small pool of engaged members can skew it. Fix: read the **scored members** count under the Average card alongside it.
* **Progression chart is flat or sparse** - Symptom: few points on the line. Cause: the line only plots days on which scores actually changed, and a short history has few events. Fix: widen the time range.
* **Score did not update after activity** - Symptom: a member trained or paid but their score is unchanged. Cause: scores are written by the nightly job, not live. Fix: wait for the next run or use **Sync now**.
* **A paused member still scores as "active service"** - This is intended. A paused membership keeps the services category up because the contractual relationship is intact.
## Limitations
* The dashboard is read-only. To change thresholds or weights, edit [Smart tagging](/settings/smart-tagging) settings.
* Level thresholds (75 / 50 / 25) are fixed and not configurable - only the weights and windows are.
* There is no "no-score-yet" filter - members without a score are silently excluded from the average and chart.
* Scores need data. Brand-new members may not have a stable score until they have accrued a few weeks of activity and the job has run.
## Related
* [Smart tagging](/settings/smart-tagging) - Where engagement scoring is enabled and tuned.
* [Members analytics](/analytics/members) - Member-level metrics not tied to scoring.
* [Analytics overview](/analytics/overview) - Other dashboards and how filters work.
* [Promotions](/sales/promotions) - Turn a Low or Rockstar cohort into a targeted offer.
# Marketing analytics
Source: https://docs.1club.ai/analytics/marketing
The Marketing and Website and Apps analytics categories - lead conversion, email delivery, promotion redemptions, and web traffic.
The **Marketing** category in Analytics reports on how your gym acquires and converts prospects, how your email sends perform, and how your promotions get redeemed. It reads from your leads pipeline, your sent emails, and your promotion redemptions, so the numbers reflect the same records you manage under **Members** > **Leads**, **Marketing**, and **Sales** > **Promotions**. The **Website and Apps** category next to it reports on portal and website activity. Owners and managers use it to judge what their marketing brings in.
## When to use it
Use it to see which marketing efforts turn into members: where leads come from and how they convert, whether your emails get delivered and opened, and who redeems your promotions. Typical questions it answers:
* **Which lead channel converts best?** - a padel gym reads **Lead Sources** over 90 days and finds ads bring the most new leads while walk-ins convert more of theirs.
* **Is an intro offer bringing in new members?** - a jiu-jitsu academy checks how many redeemers of its "first month free" voucher were **New Users**, not existing members.
* **Did a win-back email land without costing you subscribers?** - a yoga studio reads **Delivered** and **Opened** against **Total Sent** for the week, and watches the **Unsubscribed Contacts** table.
See [Worked examples](#worked-examples) for how to read each of these.
## The three dashboards
Open **Analytics** in the main nav and select the **Marketing** category to reach three dashboards:
* **Marketing Dashboard** - new leads, conversions, how fast leads convert, the pipeline by stage, and conversion by source.
* **Email Analytics Dashboard** - delivery and engagement status for sent emails, plus unsubscribed contacts.
* **Promotions** - redemption volume, wallet funds added, discounts given, and per-promotion and per-member breakdowns.
All three require the `analytics.read.club` permission. Each dashboard opens on a filter bar with a date range.
### Filtering
The filter bar at the top of each dashboard controls what the metrics cover:
* **Date range** - one field showing the selected days, with previous and next period arrows. Open it to pick a preset (**Today**, **Yesterday**, **This week**, **Last week**, **This month**, **Last month**, **Last 3 days** through **Last 365 days**, **This year**, **Previous year**) or a custom range on the calendar.
* **Club** - there is no club control on the dashboard. **Promotions** follows the club selected in the breadcrumb at the top of the admin; the **Marketing Dashboard** and **Email Analytics Dashboard** are always organization-wide.
Your last-used range is remembered per dashboard and carried in the URL, so you can bookmark or share a filtered view.
## Marketing dashboard
This dashboard shows how your [leads pipeline](/members/leads) converts. It reads by cohort: of the contacts who entered the pipeline in the selected range, how many converted, and how quickly. A lead converts on their first paid purchase after entering the pipeline, when they become a Member or Drop-in. There is no win rate: a cold lead may still buy next month, so no lead is ever counted as lost. The dashboard is always organization-wide.
### Summary metrics
| Card | What it means |
| - | - |
| **New leads** | Contacts who entered the pipeline in the range. |
| **Converted** | How many of those new leads have converted since. |
| **Conversion rate** | Converted as a share of new leads. |
| **Median time to convert** | The median number of days from entering the pipeline to converting, for the leads that converted. Shows `-` when none have. |
### Charts
* **Pipeline today** - a horizontal bar per pipeline stage, in your stage order and colors, counting the contacts in each stage right now. It does not depend on the date range.
* **Converted within** - the share of the range's new leads that converted within **30 days**, **60 days**, and **90 days** of entering the pipeline.
* **Lead Sources** - new leads and converted leads per source, so you can see which sources bring leads and which of those buy. Leads with no source get their own bar.
* **Lead Trends** - new leads and conversions per week across the range.
A lead who entered the pipeline near the end of the range has had little time to convert, so a recent range reads lower than an older one. Compare **Converted within 30 days** across ranges that are at least 30 days old.
## Email analytics dashboard
This dashboard reports on emails your gym has sent, drawn from your messages and their per-recipient delivery and engagement status. It is organization-wide and filtered only by time range.
### Summary metrics
* **Total Sent** - the count of email recipients in the range.
* A card per status that has activity. The effective status prefers a recipient's engagement status (for example **Opened**, **Clicked**), then delivery status (**Delivered**, **Bounced**, **Dropped**), then the message status. Statuses you may see include **Delivered**, **Opened**, **Clicked**, **Deferred**, **Bounced**, **Dropped**, **Spam Report**, **Unsubscribed**, and **Failed**.
Only statuses with a non-zero count appear, so the cards adapt to what actually happened in the range.
### Charts and tables
* **Sent Emails by Status per Week** - a stacked bar chart with one bar per ISO week, each segment a delivery or engagement status.
* **Unsubscribed Contacts** - a table of contacts who have opted out of marketing email, with their opt-out date. This reads from each contact's marketing email preference, so it also includes people who unsubscribed outside a specific send.
## Promotions
This dashboard measures how your [promotions](/sales/promotions) (vouchers and discounts) get redeemed. The date range filters on when each redemption happened, not when the promotion was created. Redemptions tied to a voided or refunded charge are excluded, so the totals reflect settled activity only.
### Summary metrics
| Card | What it means |
| - | - |
| **Active Promotions** | Promotions currently within their start/end window, shown as `active / total`. |
| **Total Redemptions** | Count of settled redemptions in the range. |
| **Wallet funds added** | Amount added to member wallets by voucher redemptions. |
| **Discounts Given** | Amount taken off orders at checkout, including vouchers applied as a booking discount rather than wallet credit. |
| **Voucher redemptions** | Redemptions in the range for voucher-type promotions, with the configured voucher count in parentheses. |
| **Discount redemptions** | Redemptions in the range for discount-type promotions, with the configured discount count in parentheses. |
A voucher applied directly as money off a booking counts under **Discounts Given**, not **Wallet funds added**, because no wallet was credited. Only a voucher that actually credits a wallet counts there.
### Charts and tables
* **Redemption Trend** - a dual-axis line chart of redemption **count** and redeemed **amount** per day.
* **Per-Promotion Summary** - one row per promotion with its **code**, **Uses**, **Revenue** (total order value on redemptions), **Discount** (amount given), **New Users** (redeemers who joined within 30 days before redeeming), and **Avg Order**.
* **Per-Member Summary** - one row per member with **Uses**, **Promotions Used** (distinct promotions), **Total saved or added to wallets**, and **Last Redemption**. Redemptions with no member are grouped into a single **Anonymous** row.
Both tables are capped at the top 200 rows, and the underlying aggregation reads the most recent 10,000 redemptions, so very high-volume ranges may not include the oldest redemptions.
## Website and Apps
The **Website and Apps** category has one report, **Web Traffic** (**Analytics** > **Website and Apps** > **Web Traffic**). It shows member and visitor activity across the member portal and your website: total events, unique visitors, and the split between portal and website events. Filter by event type and source next to the date range, then read the counts per event and the events by contact. It is organization-wide and not narrowed by club.
## Worked examples
**Padel club measuring lead quality by channel.** A padel club runs paid ads, walk-in enquiries, and a website contact form. On the **Marketing Dashboard** they set the range to **Last 90 days** and read **Lead Sources**: ads bring the most new leads, but walk-ins have the most converted bars relative to their leads. **Median time to convert** shows how long the follow-up should run before a lead is moved to the cold stage.
**Jiu-jitsu academy validating an intro-offer voucher.** The academy issues a "first month free" voucher and wants to know whether it brings in new members rather than rewarding existing ones. On **Promotions**, the **Per-Promotion Summary** row for that voucher shows **New Users** alongside **Uses**, so they can see how many redeemers were genuinely new, and **Revenue** shows the order value those redemptions carried.
**Yoga studio checking a re-engagement email.** After sending a win-back email to lapsed members, the studio opens the **Email analytics dashboard** for the matching week, reads **Delivered** and **Opened** against **Total Sent**, and watches the **Unsubscribed Contacts** table to make sure the campaign is not costing them their list.
## Tips & best practices
* Set a source on every lead (forms can do it for you) so **Lead Sources** stays meaningful and the no-source bar stays small. See [Leads](/members/leads).
* Judge conversion on ranges old enough for leads to have converted; a range that ended yesterday always looks weak.
* Compare like-for-like ranges (for example **This year** versus **Previous year**) rather than mixing a preset with a custom span.
* Read promotion **Revenue** and **Avg Order** together with **Uses**: a promotion with many uses but low revenue may be discounting members who would have paid full price.
* Use the **Unsubscribed Contacts** table as a health check after every large send, not only when open rates drop.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| **New leads** is 0 although the board has leads | The range counts leads that entered the pipeline in that range, not leads sitting in it | Widen the range, or read **Pipeline today** for where leads stand now. |
| Many leads fall under the no-source bar | Those leads have no source set | Set a source on each lead, or have your forms set one. |
| **Converted** looks low for a recent range | New leads have not had time to buy yet | Compare older ranges, or read **Converted within 30 days**. |
| **Wallet funds added** looks lower than expected while **Discounts Given** is high | Vouchers applied as money off a booking count as discounts, not wallet credit | This is expected; only wallet-crediting voucher redemptions count there. |
| Promotion totals seem to miss older redemptions in a wide range | The aggregation reads the most recent 10,000 redemptions | Narrow the date range so the window fits under the cap. |
| A dashboard stays empty | No records match the range, or you lack `analytics.read.club` | Widen the time range; confirm your role grants the analytics read permission. |
| Email counts differ from your send tool | Statuses use the effective per-recipient status (engagement over delivery over message status) | Read the per-status cards together rather than any single number. |
## Related
* [Analytics overview](/analytics/overview) - All analytics categories and how the hub works.
* [Leads](/members/leads) - The pipeline board, stages, and sources behind the Marketing Dashboard.
* [Promotions](/sales/promotions) - Vouchers and discounts measured on the Promotions dashboard.
* [Automations](/marketing/automations) - Event-driven emails whose sends appear in the Email dashboard.
# Members analytics
Source: https://docs.1club.ai/analytics/members
The Members analytics category - customer counts and growth, progression, engagement, and account balances.
The **Members** category in **Analytics** groups the dashboards and reports that describe your people: how the base is growing, how members progress through skill levels, how engaged they are, and who owes or holds money. Each item is a focused page. Use the dashboard for the headline picture and the reports for member-by-member detail. Owners and managers use it to track the health of their member base.
## When to use it
A flat headcount can hide members who have stopped coming. These pages show who is drifting away, how students progress, and who owes you money. Typical questions they answer:
* **Is the customer base really growing?** - a boxing gym reads **New customers** for the month against the **All customers** total to see whether new joins are keeping up with members who lapse.
* **Who should you call before they lapse?** - the **Low engagement alerts** list shows each low-scored member with their last activity date.
* **Which students haven't progressed in a while?** - a jiu-jitsu academy reads each student's belt history in the progression report before its quarterly grading review.
* **Who owes money at month end?** - a climbing gym opens the balance report, with the largest debtors listed first and each unpaid charge one click away.
See [Worked examples](#worked-examples) for more on each.
## Reports in this category
Open **Analytics** and pick the **Members** category to reach:
* **Dashboard** - Customer counts, new customers, growth trends, and top and low-engagement members.
* **Progression Report** - Every member's current level and history across your progression tracks.
* **Member Engagement** - Engagement scoring per member and how scores move over time. Documented in full under [Engagement analytics](/analytics/engagement).
* **Balance Report** - Members with a pending balance, split into amounts owed and wallet balance.
Attendance moved to its own **Check-ins** category, documented under [Schedule and instructor analytics](/analytics/schedule#check-ins).
The dashboard has a **date range** filter (default: the last 30 days) and always covers the whole organization, whichever club the breadcrumb shows. The progression and balance reports are org-wide snapshots and have no filter bar.
## Members dashboard
The dashboard opens with a row of KPI cards, a growth chart, and two member lists. Set a club and date range in the filter bar to scope the activity-based metrics.
### KPI cards
* **All customers** - Members plus Drop-ins, counted right now. This is the number your 1Club subscription is billed on, and the date range does not change it.
* **Members** - Contacts with a recurring membership or season pass. A current count across all clubs.
* **Drop-ins** - Contacts with a pass, or a visit in the last 30 days. After 30 days without a booking or check-in, a Drop-in moves to Lapsed.
* **New customers** - Contacts who became a Member or Drop-in for the first time in the period. Someone who comes back after lapsing, or a Drop-in who takes a membership, is not new.
* **Average engagement** - A gauge showing the average engagement score and level (low, medium, high, or rockstar). This card appears only when engagement scoring is turned on.
Hover the help icon on any card for its exact definition. Only **New customers** reacts to the date range; the other counts are live totals.
### Customer growth trends
A stacked area chart plots **Members** and **Drop-ins** week by week across the range. The series are reconstructed from recorded type changes, so a contact converting from drop-in to member moves between the areas. Use it to see whether the base is genuinely growing or just churning in place.
### Top members and low engagement alerts
Below the chart, two lists surface individuals:
* **Top members** - The most engaged members by score, for recognition or referrals.
* **Low engagement alerts** - Members scored as low engagement, each with a **last activity** date so you can prioritize a check-in call.
Both lists depend on engagement scoring being enabled and populated. If scoring is off, the gauge and these lists have nothing to show. See [Engagement analytics](/analytics/engagement) for how scores are calculated.
## Progression report
If you run skill progression, this report lists every member's level per track. It shows one tab per **progression track**, and within each track a table with three columns:
* **Member** - Links to the member's profile.
* **Current level** - The member's current skill and level, shown as a chip.
* **History** - The chain of level changes over time, oldest to newest, so you can see how far and how fast someone has moved.
Members are ordered by how actively they are progressing. If no tracks are set up, the page shows a "no progression tracks enabled" message. Use the **Export** button (top right) to pull the full report to CSV.
## Balance report
The balance report is a money-owed and money-held snapshot built from pending charges. It has no date filter because it reflects current outstanding balances, and it offers an **Export** button for the full list.
### Summary cards
* **Members with pending balance** - How many members have any pending charge.
* **Members owing** - How many members owe money.
* **Total due** - The sum owed to you across all members.
* **Total wallet balance** - The wallet balance members hold.
Amounts owed and credit held are reported separately and are never netted against each other. A member who owes 100 and also holds 100 in their wallet still appears as owing 100. This keeps genuine debt visible instead of masking it with unrelated credit.
### Member table
Each row shows the member, a count of their **pending charges**, the amount **due**, and their **wallet balance**, with the biggest debtors listed first. Expand a row to see the individual pending charges (date, description, type, and amount); clicking a charge opens it in [Sales > Finance > Charges](/sales/charges).
## Worked examples
### Spotting a retention dip
A boxing gym shows 320 customers under **All customers**, steady for three months. On the members dashboard the owner sets the range to the last 30 days and reads **New customers** at 25, so roughly as many people lapsed as joined. The **Customer growth trends** chart shows Drop-ins shrinking while Members hold. The next step is the **Low engagement alerts** list to call the at-risk members before they lapse.
### Tracking progression in a jiu-jitsu program
A jiu-jitsu academy runs belt progression as a track. The progression report's track tab lists every student with their current belt and the history of promotions. Coaches sort attention toward students who have not moved in a long time, and export the tab to CSV for the quarterly grading review.
### Chasing outstanding balances at a climbing gym
A climbing gym's front desk opens the balance report at month end. **Members owing** and **total due** quantify the debt; the table puts the largest debtors on top. Expanding a row reveals which specific charges are unpaid, and each links straight through to the charge to take payment or follow up.
## Tips and best practices
* **Read new customers against the growth chart.** A flat **All customers** total with steady **New customers** means you are replacing lapsed customers, not growing.
* **Turn on engagement scoring to unlock the people lists.** The engagement gauge, top members, and low-engagement alerts all need scoring enabled.
* **Balance and progression reports ignore the date range** because they are current snapshots. Do not expect them to react to the filter bar.
* **Export for follow-up work.** The progression and balance reports both export to CSV for offline lists, grading reviews, or collections.
* **Cross-check activity against attendance.** For check-in volume and peak times, open **Analytics** > **Check-ins** > **Attendance**.
## Troubleshooting
| Symptom | Likely cause | Fix |
| - | - | - |
| The engagement gauge and member lists are empty | Engagement scoring is off or has not run yet | Enable engagement scoring; see [Engagement analytics](/analytics/engagement) for setup and how scores are produced. |
| Drop-ins look lower than expected | A Drop-in with no booking or check-in for 30 days moves to Lapsed | This is expected. Confirm bookings and check-ins are being recorded. |
| Customer counts ignore the date range or club | **All customers**, **Members**, and **Drop-ins** are live organization-wide counts | Read **New customers** for range-scoped figures. |
| Balance report does not respond to the date filter | It is a current snapshot with no date filter by design | Interpret it as "as of now". Use [Sales > Finance > Charges](/sales/charges) for dated history. |
| Progression report says no tracks are enabled | No progression tracks are set up for the organization | Set up progression tracks before the report can populate. |
| A member owes and holds a wallet balance but still shows as owing | Due and wallet balance are reported separately and never netted | This is expected. Apply their wallet balance to the debt if you want to clear it. |
## Related
* [Analytics overview](/analytics/overview) - How every dashboard is laid out and who can see what.
* [Clubs analytics](/analytics/clubs) - Activity, revenue, and utilization by location.
* [Engagement analytics](/analytics/engagement) - Full detail on member engagement scoring.
* [Members > Member list](/members/member-list) - Manage individual members.
* [Schedule > Attendance](/schedule/attendance) - The operational view of check-ins.
* [Sales > Memberships](/sales/memberships) - Plans, terms, and membership status.
* [Sales > Finance > Charges](/sales/charges) - The dated ledger behind the balance report.
# Analytics overview
Source: https://docs.1club.ai/analytics/overview
Pre-built dashboards and reports for clubs, members, check-ins, bookings, classes, instructors, revenue, marketing, web traffic, and inventory, with live and historical views.
The **Analytics** section is a collection of pre-built dashboards and detail reports. Each report is a fixed page with a few KPI cards at the top and one or more charts below. Some dashboards link out to deeper per-metric reports.
There is no report builder or query interface - every report has predefined filters. If you need raw data to slice yourself, use [Settings > Data Tools > Export](/settings/export).
## Overview
* **Category sidebar** on the left groups every report under a heading (Clubs, Members, Check-ins, and so on). A search box at the top of the sidebar filters the list by report name or description. On a phone, **Analytics** opens on a category index instead.
* **KPI cards** give you the headline numbers at a glance (total revenue, average score, total members).
* **Recharts charts** below the cards are interactive: hover for tooltips, click a legend entry to toggle a series on or off, and pan or zoom on time-based charts.
* **Live and historical views** - the Clubs category has a live **Today's Performance** dashboard that auto-refreshes; every other report reads a date range you pick.
* **Club scoping** - most reports follow the club selected in the breadcrumb at the top of the admin; **All clubs** shows the whole organization.
* **Role-scoped access** - what each report shows respects the viewer's permissions and scope (see [Who can see what](#who-can-see-what)).
## The category sidebar
The left sidebar lists every report, grouped into categories. The exact set you see depends on your permissions and which features are enabled for your organization (for example, the **Inventory** category only appears when inventory is turned on).
| Category | Reports |
| - | - |
| **Clubs** | **Today's Performance** (live hourly view), **Performance Trends** (the same metrics over a date range) |
| **Members** | **Dashboard**, **Progression Report**, **Member Engagement**, **Balance Report** |
| **Check-ins** | **Attendance**, **Check-ins Report** |
| **Bookings** | **Dashboard**, **Audit**, **By Source**, **Utilization**, **Monthly Report** |
| **Classes** | **Classes Dashboard** |
| **Instructors** | **Instructors Dashboard**, **Instructor Pay Report**, **Earnings Statement**, **Earnings by entrance method** |
| **My performance** | Your own **Bookings**, **Classes**, **Earnings Overview**, **Earnings Statement**, **By entrance method** (only for users linked to an instructor record) |
| **Revenue** | **Revenue**, **Revenue Report**, **Payments Report**, **Invoices Dashboard** |
| **Website and Apps** | **Web Traffic** |
| **Marketing** | **Marketing Dashboard**, **Email Analytics Dashboard**, **Promotions** |
| **Inventory** | **Stock Movements** (only when inventory is enabled) |
## What's available
The docs group these categories into a few pages:
* **[Clubs](/analytics/clubs)** - **Today's Performance** (auto-refreshing every five minutes) and **Performance Trends**.
* **[Members](/analytics/members)** - Customer counts and growth, progression, and balances.
* **[Engagement](/analytics/engagement)** - Member engagement scoring (0 to 100 per member) and how scores change over time.
* **[Schedule and instructors](/analytics/schedule)** - The **Check-ins**, **Bookings**, **Classes**, **Instructors**, and **My performance** categories.
* **[Revenue](/analytics/billing)** - Revenue dashboard, revenue report, payments report, and invoices dashboard.
* **[Marketing](/analytics/marketing)** - The **Marketing** and **Website and Apps** categories.
* **Inventory** - **Stock Movements** is a ledger of every receive, sale, adjustment, and waste entry. See [Inventory](/sales/inventory).
## How the dashboards work
Every report shares the same layout:
The **filter bar** at the top holds the date range: one field showing the selected days, with previous and next period arrows. Open it to pick a preset - **Today**, **Yesterday**, **This week**, **Last week**, **This month**, **Last month**, **Last 3 days**, **Last 7 days**, **Last 2 weeks**, **Last 30 days**, **Last 90 days**, **Last 6 months**, **Last 365 days**, **This year**, **Previous year** - or a custom range on the calendar. Reports that trend by month (for example the bookings monthly report and instructor reports) show month presets instead. Some pages add extra filters (billing entity, event type, source) on the same row. There is no club control: the report follows the club in the breadcrumb.
The **KPI cards** sit below the filter bar - usually three or four cards, each with a single number and an icon.
The **charts** are below the cards. They are built with Recharts, so hovering shows tooltips, the legend toggles series on and off, and you can pan or zoom on time-based charts.
Your chosen range is remembered per report (stored in the URL and locally), so returning to a report restores the range you last used there.
## Data freshness
Most dashboards run live queries against your operational data, so as soon as a booking is created or a payment lands, it shows up. Two exceptions:
* The **Clubs** > **Today's Performance** dashboard reads hourly aggregates. It shows the timestamp of the last completed hour at the top of the page and auto-refreshes every five minutes.
* The **Engagement** scoring system writes scores in a background job (it runs nightly at 03:00 UTC as part of member smart-tagging, and on demand from the Smart tagging page). The dashboard reads from that stored score, not the live activity tables, so brand-new activity is reflected only after the next run.
## Time zones
All "today" and "this week" calculations use your organization's time zone (set under **Settings** > **Organization** > **Details**). A booking that is "tomorrow" in your local time may already be "today" in the org's time zone or vice versa.
## Who can see what
Analytics access follows the role's permissions. The gym-wide reports need `analytics.read` at club scope; what each default role gets:
* **Admin** sees everything.
* **Manager** sees every gym-wide report (`analytics.manage.club`).
* **Front desk** has no analytics permission by default.
* **Instructor** sees only the **My performance** category (`analytics.read.own` for bookings and classes, `billing.read.own` for earnings).
**My performance** also requires the user to be linked to an instructor record, whatever their role. The **Inventory** category's **Stock Movements** report requires `inventory.read` instead of an analytics permission, and the category only appears when inventory is enabled.
See [Roles and permissions](/reference/roles-permissions) for the full breakdown.
## AI business report
Separate from these dashboards, 1Club can generate an **AI business report** (business audit) that scores your gym's online presence - Google profile, reviews, AI discoverability, competitors, and website performance - with recommendations. It appears as a card on **Home** and is documented under [Business audit](/home/ai-visibility-report). It is a point-in-time AI-generated report, not one of the live operational dashboards described above.
## Worked examples
### Monday-morning club check (racket sports)
You run a padel and tennis club and want a 30-second read on the weekend. Open **Clubs** > **Today's Performance** with **All clubs** selected in the breadcrumb to see live check-ins and revenue across both venues, then switch to **Clubs** > **Performance Trends** with the range set to **Last 7 days** to compare the weekend against the week. Drill into **Bookings** > **Utilization** to see which courts sat empty.
### Investigating a revenue dip (gym / fitness)
Revenue looks soft this month. Start on **Revenue** > **Revenue** with the range set to **Last 90 days** to confirm the trend, then open the **Revenue report** and **Payments report** to see whether it is fewer memberships, more failed payments, or lower point-of-sale takings. Cross-check **Members** > **Balance Report** for outstanding balances.
### Sizing a win-back campaign (Pilates / yoga studio)
You want to re-engage lapsed reformer clients. Open **Members** > **Member Engagement**, read the **Low** count in the score distribution, and use that as your audience size. See [Engagement analytics](/analytics/engagement) for how to act on it.
## Tips and best practices
* **Set the time range once** at the top of the page. Charts on the same report share the range, so you do not need to set it per chart.
* **Compare clubs side by side** by running **Clubs** > **Performance Trends** for **All clubs**, then switching the breadcrumb to one club at a time.
* **Use the sidebar search** to jump straight to a report by name instead of scanning categories.
* **Bookmark a filtered report.** Because the time range and club are stored in the URL, a bookmark reopens the exact view you set up.
* **For raw data, export instead.** Analytics pages are for reading; CSV exports under **Settings** > **Data Tools** > **Export** are for analysis in Excel, BI tools, or your accountant's software.
## Troubleshooting
* **A category is missing from the sidebar** - the category is feature-gated or permission-gated. **Inventory** only appears when inventory is enabled for your organization, and you only see reports your role's permissions cover. Check with an admin or see [Roles and permissions](/reference/roles-permissions).
* **A report is empty for the range you picked** - there is no data in that window. Widen the time range or switch the breadcrumb to **All clubs**. If a whole dashboard is blank, confirm the underlying feature (bookings, billing, marketing) is in use.
* **The Today dashboard looks a few minutes stale** - that is expected. It reads hourly aggregates and refreshes every five minutes; the header shows the last completed hour.
* **Engagement numbers do not match live activity** - engagement scores are written by a background job, not computed live. See [Engagement analytics](/analytics/engagement) for the refresh schedule.
* **Numbers differ from a CSV export** - the dashboards apply their own date and status filters (for example, excluding voided charges). Exports give you the raw rows so you can apply your own rules.
## Related
* [Home metrics](/home/metrics) - Home rolls up six of these metrics into small charts that link here.
* [Engagement analytics](/analytics/engagement) - Member engagement scoring in depth.
* [Business audit](/home/ai-visibility-report) - The AI business report on your online presence.
* [Settings > Data Tools > Export](/settings/export) - Pull raw CSVs for offline analysis.
* [Roles and permissions](/reference/roles-permissions) - Who can read which reports.
# Schedule and instructor analytics
Source: https://docs.1club.ai/analytics/schedule
The Check-ins, Bookings, Classes, Instructors, and My performance analytics categories - attendance, utilization, booking audit, and instructor pay reports.
These analytics show check-in, booking, class, and instructor performance so you can optimize schedules, capacity, and instructor pay. Owners and managers use them to plan the timetable, reconcile payments, and pay instructors. Instructors use **My performance** to see their own numbers.
## When to use it
Use these reports when you are about to change the schedule, close the month, or run payroll. Typical questions they answer:
* **Which courts sit empty, and when is demand highest?** - a padel gym sees Court 3 at 42% utilization while Courts 1 and 2 run near 95%, with bookings concentrated at 18:00 to 21:00.
* **Which bookings are still unpaid?** - a jiu-jitsu gym filters the booking audit to unpaid class bookings for the last 30 days and reads the total to collect.
* **Which classes get booked but not attended?** - a reformer studio finds a midday class with high bookings but a low attendance rate, while its 07:00 class is at full capacity.
* **What do you owe each instructor this month?** - a CrossFit box checks each coach's month in the instructor report, then exports it for payroll.
See [Worked examples](#worked-examples) for what each gym did next.
## Reports in this category
Open **Analytics** and pick a category in the sidebar:
| Category | Reports |
| - | - |
| **Check-ins** | **Attendance** (check-in volume, peak times, no-show rate), **Check-ins Report** (every check-in for a day or short range) |
| **Bookings** | **Dashboard** (volume, revenue, cancellations, peak times), **Audit** (payment reconciliation per booking), **By Source** (channel, API key, and UTM source), **Utilization** (booked hours versus capacity), **Monthly Report** (month-by-month matrix by type) |
| **Classes** | **Classes Dashboard** (attendance, capacity utilization, popular classes) |
| **Instructors** | **Instructors Dashboard** (sessions and revenue per instructor), **Instructor Pay Report** (monthly compensation), **Earnings Statement** (one instructor's line items), **Earnings by entrance method** (revenue and coach share per entrance method) |
| **My performance** | The signed-in instructor's own **Bookings**, **Classes**, **Earnings Overview**, **Earnings Statement**, and **By entrance method** |
Most reports export to CSV. See [Exporting reports](#exporting-reports).
## Filters
The reports share the same filter bar at the top:
* **Date range** - One field showing the selected days, with previous and next period arrows. Open it to pick a preset or a custom range on the calendar. Dashboards default to the last 30 days; the monthly report, instructors dashboard, and pay report offer month presets and default to the last 6 months; the earnings statement and entrance-method report default to the last month.
* **Club** - There is no club control on the report. Reports follow the club selected in the breadcrumb at the top of the admin; **All clubs** shows the whole organization. The earnings statement, entrance-method report, and **My performance** reports are not narrowed by club.
## Check-ins
The **Check-ins** category covers who actually came in.
**Attendance** (**Analytics** > **Check-ins** > **Attendance**) has KPI cards for **Total check-ins**, **Unique members**, **Average check-ins per member**, and **No-show rate** (the share of bookings that were not attended). Below them are an attendance trend line, **Peak times** by hour, and attendance **By club** and **By area type**. Use no-show rate and peak times together to right-size class capacity and tighten cancellation windows where no-shows are high.
The **Check-ins Report** (**Analytics** > **Check-ins** > **Check-ins Report**) lists every individual check-in, grouped by how the member got in (membership, pass, drop-in, or external program) with a subtotal per group and **Total check-ins** at the bottom. Columns are **Member**, entry method, **Time**, and reference. It opens on **Today** and accepts ranges of up to 31 days, so the front desk can reconcile a shift or a day against the till.
## Bookings dashboard
The **Dashboard** (**Analytics** > **Bookings** > **Dashboard**) is the top-of-funnel view of demand and revenue.
KPI cards:
* **Total bookings**, **Confirmed bookings**, **Cancelled bookings**, and **Pending bookings** - Volume by status.
* **Total revenue** - Amount billed by the bookings in range.
* **Paid bookings amount** - How much of that has been collected.
* **Average booking value** - Revenue divided by bookings.
* **Cancellation rate** - Cancelled bookings as a percentage of the total.
Charts:
* **Booking trends** - Bookings (bars) and revenue (line) over time. The time bucket adapts to your date range (daily, weekly, or monthly).
* **By area type** - Bookings per area type (for example, tennis court, reformer studio, climbing wall).
* **By source** - Bookings per booking source.
* **Peak hours** and **Peak days** - When bookings cluster, by hour of day and day of week.
Use this dashboard to spot demand patterns and set your class and court schedule around peak hours and days.
## Bookings by source
**By Source** (**Analytics** > **Bookings** > **By Source**) splits booking volume two ways:
* **Bookings by source** - The booking channel (for example, admin, member portal, kiosk). Bookings made through the API are split per API key, and bookings made through AI assistants are counted separately.
* **Bookings by marketing source (UTM)** - The `utm_source` attached to the booking, so you can attribute bookings to specific marketing links or campaigns.
This is the same source data summarized on the Bookings dashboard, broken out for attribution work.
## Utilization
The **Utilization** report (**Analytics** > **Bookings** > **Utilization**) compares booked hours against available capacity, so you can see how hard your rooms and instructors are working.
KPI cards:
* **Area utilization** and **Instructor utilization** - Booked hours as a percentage of capacity hours.
* **Area capacity** and **Instructor capacity** - Booked hours over capacity hours (for example, `120 / 400 h`).
Charts:
* **Utilization over time** - Booked hours (bars) and utilization percentage (lines) for areas and instructors, over the period.
* **Utilization by area type** and **Utilization by instructor type** - Where you have headroom or a bottleneck, broken down by type. Each bar is backed by a `booked / capacity h` breakdown underneath.
Low utilization on a peak day means you can add classes or open more court slots; utilization near 100% is a signal to add capacity or staff.
## Booking audit
The **Audit** report (**Analytics** > **Bookings** > **Audit**) is a row-level payment-reconciliation view. It lists every booking in the selected date range, with unpaid bookings highlighted, so you can see at a glance what has been paid, what is outstanding, and how each booking was entered.
Summary cards show **Outstanding**, **Unpaid bookings**, **Total billed**, and **Total bookings**.
Columns: **Date**, **Member**, **Type**, **Resource**, **Club**, **Source**, **Status**, **Entrance**, **Amount**, **Outstanding**, **Payment**, and **Method**.
You can refine the table with three filters, and the filters also apply to the CSV export so the two always match:
* **Payment** - The payment state of the booking.
* **Type** - The resource, grouped by **Area**, **Class**, **Instructor**, and **Other**.
* **Status** - Confirmed, Pending, Checked in, No show, or Cancelled.
**Payment** states:
* **Unpaid** - Nothing collected yet.
* **Partial** - Some but not all of the amount is paid.
* **Paid** - Fully collected.
* **Refunded** - Payment was returned.
* **Covered** - Settled without a direct charge (for example, by a membership).
* **No charge** - No payment was required.
**Entrance** captures how the booking was paid for:
* **Direct payment** - Paid at the time of booking.
* **Membership** - Consumed from a membership allowance.
* **External program** - Redeemed against an external program (these visits carry their own per-visit fee).
Click a row to open the booking detail. Unpaid rows are tinted so you can chase them down.
## Bookings monthly report
The **Monthly Report** (**Analytics** > **Bookings** > **Monthly Report**) is a month-by-month matrix: one row per month, one column per booking type, plus **Total bookings** and **Total revenue** columns and a **Totals** row. A stacked bar chart above the table shows the same breakdown by type.
Use it for period-over-period comparison (for example, "how did padel bookings trend across the last six months?") and export it for board or owner reporting.
## Classes dashboard
The **Classes Dashboard** (**Analytics** > **Classes** > **Classes Dashboard**) covers group class performance.
KPI cards:
* **Total classes**, **Total bookings**, and **Total attendance**.
* **Average attendance** - Average attendees per class.
* **Capacity utilization** - Attendance as a percentage of class capacity.
Views:
* **Classes and attendance trends** - Classes (bars) plus bookings and attendance (lines) over time.
* **By instructor** - Attendance per instructor.
* **Most popular classes** - The top classes by bookings, with their attendance and **attendance rate**.
Watch the gap between bookings and attendance: a high booking count with low attendance points to a no-show problem you can address with [booking policies](/settings/booking-policies).
## Instructor analytics
The **Instructors** category is the gym-wide view of every instructor.
### Instructors dashboard
The **Instructors Dashboard** (**Analytics** > **Instructors** > **Instructors Dashboard**) is a matrix of **sessions** and **revenue** per instructor per month, with **Total instructors**, **Total sessions**, and **Total revenue** cards and a per-month **Totals** row. Each instructor row also shows their club. A direct booking counts as one session; a co-taught class counts once for each instructor, and its revenue is split between them so organization totals are not double-counted.
### Instructor pay report
The **Instructor Pay Report** (**Analytics** > **Instructors** > **Instructor Pay Report**) is a month-by-month matrix of **instructor compensation**, with a **Totals** row and an **Instructor Compensation** total column. Click any month cell to open a drill-down of what made up that month's figure for that instructor.
### Earnings statement
The **Earnings Statement** (**Analytics** > **Instructors** > **Earnings Statement**) is an itemized breakdown of every payment that contributed to a single instructor's compensation. Pick an instructor with the selector, choose a period, and the statement lists each line item; click a line to jump to the underlying class or booking.
### Earnings by entrance method
**Earnings by entrance method** (**Analytics** > **Instructors** > **Earnings by entrance method**) shows, for a selected instructor, the revenue and coach share split by how members got in. Each pass, program, and direct payment gets its own row, with a line-item count and a total.
### My performance
Any user linked to an instructor record - an instructor, or an owner or manager who also teaches - gets a **My performance** category with their own numbers only: **Bookings**, **Classes**, **Earnings Overview**, **Earnings Statement**, and **By entrance method**. The bookings and classes reports need the `analytics.read.own` permission; the three earnings reports need `billing.read.own`, so you can hide pay from instructors without hiding their schedule stats.
## Exporting reports
Reports with an **Export** button in the top-right download a CSV of exactly what is on screen, including any filters you applied. Exportable reports include the **Check-ins Report**, **Audit**, **Monthly Report**, **Classes Dashboard** (by instructor), **Instructors Dashboard**, **Instructor Pay Report**, **Earnings Statement**, and **Earnings by entrance method**. Files are named with the report and date range, and the club id when the report is narrowed to one club. For scheduled or bundled exports, see [Settings > Data Tools > Export](/settings/export).
## Worked examples
**Racket sports - fixing a court that pays for itself.** A padel club looks at **Utilization** and sees Court 3 at 42% while Courts 1 and 2 run near 95%. The bookings **Dashboard** peak-hours chart shows demand concentrated at 18:00-21:00. They move a coaching block off Court 3 into that window and re-run utilization a month later to confirm the lift.
**Martial arts - chasing unpaid drop-ins.** A jiu-jitsu gym runs the bookings **Audit** filtered to **Payment = Unpaid** and **Type = Class** for the last 30 days. The **Outstanding** card shows the total to collect; each unpaid row links straight to the booking so the front desk can take payment.
**Pilates / Yoga - trimming a class that does not fill.** A reformer studio checks the **Classes dashboard**. A midday Pilates class shows high bookings but a low **attendance rate**, while capacity utilization on the 07:00 class is maxed out. They cut the midday slot and add an early class, then watch **Average attendance** and **Capacity utilization** the following month.
**Gym / Fitness - paying instructors correctly.** A CrossFit box closes the month with the **Instructor Pay Report**, clicks each coach's month cell to verify the sessions behind the figure, then exports the CSV for payroll. For a single disputed coach, the **Earnings Statement** shows every contributing line item.
**Climbing - reading demand by source.** A climbing gym uses **By Source** and sees a spike in **Bookings by marketing source (UTM)** tied to an intro-offer link. They compare it against the bookings **Dashboard** revenue trend to confirm the promotion drove paid bookings, not just free trials.
## Tips & best practices
* Set the **Date range** first. A "zero" often just means the range or the selected club excludes the data.
* Pair **Utilization** with the bookings **Dashboard** **peak hours / peak days** charts before changing the schedule - utilization tells you the "how full", peak times tell you the "when".
* Reconcile with the bookings **Audit** weekly using the **Payment = Unpaid** filter, so outstanding amounts do not pile up.
* Use the **Instructor Pay Report** drill-down to answer pay questions before exporting, so the CSV you send to payroll is already reconciled.
* Compare month-over-month with the **Monthly Report** rather than eyeballing the trend chart, when you need exact figures.
## Troubleshooting
* **A report is blank or shows zeros.** The date range is not set, or it excludes the data. Confirm both start and end dates, then check the club selected in the breadcrumb is the one you want.
* **The Export button is disabled.** There are no rows for the current filters. Widen the date range or clear a filter, then export.
* **Audit totals do not match another report.** The audit is invoice/booking based and honors the on-screen **Payment**, **Type**, and **Status** filters. Clear the filters to see the full period, and remember refunds and covered bookings affect the outstanding total.
* **Attendance is lower than bookings.** That gap is your no-show rate. Check the **No-show rate** card on **Check-ins** > **Attendance** and tighten [booking policies](/settings/booking-policies) if it is high.
* **An instructor shows no compensation.** Confirm the instructor had sessions in the selected range and that pay rates are configured for them; the **Earnings Statement** shows whether any line items exist for the period.
## Related
* [Analytics overview](/analytics/overview) - All analytics categories.
* [Revenue analytics](/analytics/billing) - Revenue, payments, and invoices.
* [Bookings](/schedule/bookings) - Manage bookings.
* [Classes](/schedule/classes) - Manage classes.
* [Instructors](/staff/instructors) - Manage instructors and pay.
* [Attendance](/schedule/attendance) - Check-ins and attendance.
* [Booking policies](/settings/booking-policies) - Cancellation windows and no-show handling.
* [Settings > Data Tools > Export](/settings/export) - Scheduled and bundled CSV exports.
# Describe the API key's organization and granted scopes
Source: https://docs.1club.ai/api-reference/account/describe-the-api-keys-organization-and-granted-scopes
/openapi-platform.json get /v1/platform/me
Returns the organization the API key belongs to, the scopes granted to the key, and the catalog of resources/actions the platform API exposes. Requires a valid API key but no specific scope - use it to verify a key and discover what it can access.
A full-access key holds the single wildcard scope `*`. Test a capability against `effectiveScopes`, which expands the wildcard, rather than against `scopes`, which reports the grant verbatim.
# Create an area block
Source: https://docs.1club.ai/api-reference/area-blocks/create-an-area-block
/openapi-platform.json post /v1/platform/area-blocks
Closes a court, or a whole club, for a time range. Send `areaId` to close one court (its club is derived), or `clubId` with `areaId` omitted/null to close every court in that club with a single record. Existing bookings in the window are left alone - a block stops NEW bookings; cancel or move the affected bookings separately.
# Delete an area block
Source: https://docs.1club.ai/api-reference/area-blocks/delete-an-area-block
/openapi-platform.json delete /v1/platform/area-blocks/{blockId}
Removes a block, reopening the court(s) it covered for booking. Idempotent from the caller's perspective only in the sense that a second delete returns 404 - the first one already reopened the slot.
# List area blocks
Source: https://docs.1club.ai/api-reference/area-blocks/list-area-blocks
/openapi-platform.json get /v1/platform/area-blocks
Returns the blocks overlapping a window - the time-ranged unavailability of a court, the counterpart of instructor time off. A block with `areaId: null` is club-wide and closes every area in `clubId`; expand it against the club's areas the same way availability does. Filtering by `areaId` includes the club-wide blocks covering that area, so the result is everything that closes it.
# Availability change feed (reconciliation)
Source: https://docs.1club.ai/api-reference/areas/availability-change-feed-reconciliation
/openapi-platform.json get /v1/platform/availability/changes
Returns the availability changes for your organization with `sequence > since`, in order. Use it to catch up after missed webhooks: pass the last `cursor` you processed, apply the returned changes by re-pulling `/availability` for the affected areas/windows, then store the new `cursor`. This feed is the authoritative backstop for the webhook stream.
# Create an area
Source: https://docs.1club.ai/api-reference/areas/create-an-area
/openapi-platform.json post /v1/platform/areas
Adds a bookable area - a court, a studio, a lane - to one of the organization's clubs. `clubId` comes from GET /v1/platform/clubs and `areaTypeId` from GET /v1/platform/area-types; both must belong to this organization. The area type is what decides which sports the area can be booked for and what it is priced from, so resolve it first rather than guessing.
The new area enters partner availability feeds immediately, with an `availability_changes` entry written in the same transaction - so a partner reconciling against the change cursor sees the court appear.
Created areas default to `active` and `Public`. An area created `maintenance`, `inactive` or non-public is returned here and resolves by id, but will not appear in GET /v1/platform/areas, which lists only what is bookable today. Supports the `Idempotency-Key` header.
Needs `areas:write`, the same scope that blocks and unblocks areas. Existing grants therefore gain this the moment it ships; an OAuth connection made before it will not, because a grant expands the wildcard at consent time - reconnect to pick it up.
# Create an area type
Source: https://docs.1club.ai/api-reference/areas/create-an-area-type
/openapi-platform.json post /v1/platform/area-types
Adds a kind of space the organization books. List the existing types first: an organization is seeded with one per sport its clubs offer, and a second "Padel court" splits inventory that should be one group.
`sports` is what makes areas of this type bookable for a sport at all, and it is canonicalized - send `padel`, not `Padel`. An unknown sport is refused rather than stored as a value nothing will match. `slug` is optional and derived from the name when omitted. Supports the `Idempotency-Key` header.
# Get an area
Source: https://docs.1club.ai/api-reference/areas/get-an-area
/openapi-platform.json get /v1/platform/areas/{areaId}
Returns one area by id, whatever its state. Unlike GET /v1/platform/areas, which shows only what can be booked today, this resolves an area that is under maintenance, closed, or not publicly visible - so an area created or edited through this API can always be read back. `status` and `visibility` say which case it is.
# Get an area type
Source: https://docs.1club.ai/api-reference/areas/get-an-area-type
/openapi-platform.json get /v1/platform/area-types/{id}
Returns one area type and how many areas are of it, whatever their status.
# Get area availability
Source: https://docs.1club.ai/api-reference/areas/get-area-availability
/openapi-platform.json get /v1/platform/areas/{areaId}/availability
Returns the area's club timezone, operating hours, the intervals already taken (`busy`), and computed open `slots` (with remaining capacity) for the window. Send `If-None-Match` with the last `version` you saw to get a cheap `304` when nothing changed. 1club remains the source of truth: booking creation re-checks availability atomically and rejects conflicts.
# Get club-wide availability grid
Source: https://docs.1club.ai/api-reference/areas/get-club-wide-availability-grid
/openapi-platform.json get /v1/platform/availability
Returns computed open slots for every bookable area in one call, so you can render a whole calendar without a request per court. Filter by `clubId` and/or `sport`. Each area carries its own `version`; the top-level `cursor` is the reconciliation cursor to pass to `/availability/changes` next.
# List area types
Source: https://docs.1club.ai/api-reference/areas/list-area-types
/openapi-platform.json get /v1/platform/area-types
Returns the organization's area types - the kinds of space it books, such as "Padel court" or "Studio". Resolve one here, then pass its `id` as `areaTypeId` when creating an area: the type is what decides which sports the new area can be booked for and what it is priced from, so it is not a label.
Every type is returned, `isBookable: false` ones included, because an existing area may be of one and its type still has to be explicable. Do not build bookable inventory on a type that is not bookable.
There is no paging: an organization has a handful of these.
# List bookable areas
Source: https://docs.1club.ai/api-reference/areas/list-bookable-areas
/openapi-platform.json get /v1/platform/areas
Returns the organization's bookable areas (e.g. padel courts). Optionally filter by club or sport (e.g. `padel`). Use the returned area `id` as `areaId` when creating a booking.
Bookable is the filter: an area is listed only while it is `active`, publicly visible, and of a bookable area type (or of no type at all). An area closed for maintenance, or one just created `Private`, is absent here but still readable from the create or update that wrote it.
# Update an area
Source: https://docs.1club.ai/api-reference/areas/update-an-area
/openapi-platform.json patch /v1/platform/areas/{areaId}
Edits an area. Send only what changes; an omitted field is left alone. Setting `status` to `maintenance` or `inactive`, or narrowing `visibility`, takes the area out of availability - which is the durable way to close a court indefinitely, where an area block closes it for a stated window.
Any edit to hours, status, visibility, capacity or area type writes an `availability_changes` entry alongside the row, so partners see it on their next reconciliation.
`clubId` is not editable: moving a court to another club would strand its bookings, class holds and blocks at the old one. There is no delete either - an area carries booking history, so close it with `status` instead. Supports the `Idempotency-Key` header.
# Update an area type
Source: https://docs.1club.ai/api-reference/areas/update-an-area-type
/openapi-platform.json patch /v1/platform/area-types/{id}
Changes an area type. Send only what changes; an omitted field is left alone, `null` clears a nullable one, and at least one field is required.
This is a wide edit. Every area of the type inherits `sports`, `defaultPricePerHour`, `defaultMaxPlayers` and `isBookable`, so changing one of those reprices or re-sports every court on it at once - and each affected area emits an availability change, which partners consuming the reconciliation feed will pull. `sports` replaces the whole list rather than adding to it. A rename leaves `slug` alone.
# Add a player to a booking
Source: https://docs.1club.ai/api-reference/bookings/add-a-player-to-a-booking
/openapi-platform.json post /v1/platform/bookings/{id}/participants
Adds another player to a booking - for a doubles court, the three players joining the organizer. The contact must already exist (create it via POST /v1/platform/contacts). The total, counting the organizer, is capped by the area type's maximum players.
Reaches the same bookings as cancel: an API key may only add to the ones it created, a staff member acting through an authorized 1club assistant to any of the organization's.
# Cancel a booking
Source: https://docs.1club.ai/api-reference/bookings/cancel-a-booking
/openapi-platform.json post /v1/platform/bookings/{id}/cancel
Cancels the booking by its 1club id. Idempotent: cancelling an already-cancelled booking succeeds.
Which bookings are cancellable depends on the credential. An API key may cancel only the bookings it created (`source: api`); anything else is reported as not found. A staff member acting through an authorized 1club assistant may cancel any of the organization's bookings, as they can in the admin portal.
Refunds follow the money. On a booking this API created, 1club never held the payment, so it moves none - but it records what you gave back, so the club's revenue matches yours. Cancelling with an API key records a refund of everything collected unless `refund.amount` says otherwise: send the amount you actually returned to keep a cancellation fee, or `0` if you kept it all, and your own refund id as `refund.reference`. Over a staff credential nothing is recorded unless `refund` is sent. On a booking taken through 1club (member portal, app, front desk), the club is the one cancelling: an unpaid booking owes nothing unless `keepCancellationCharge` is true. A paid booking the cancellation policy would keep money from needs an explicit `keepCancellationCharge` - `false` refunds it in full, `true` applies the policy - and without one the call is refused (409 `CANCELLATION_CHARGE_DECISION_REQUIRED`), exactly as the admin portal asks. `refund` is ignored there.
A booking that has already been checked in cannot be cancelled (409) - voiding a check-in is an admin-portal action. One occurrence of a recurring booking is cancelled, not the series.
# Create a booking
Source: https://docs.1club.ai/api-reference/bookings/create-a-booking
/openapi-platform.json post /v1/platform/bookings
Creates a booking on an area (e.g. a padel court), optionally with an instructor leading the session. Identify the customer with `contactId` (resolve it via GET /v1/platform/contacts) or, for callers that do not track 1club ids, with an inline `customer` matched to a contact by email and created if new - exactly one of the two.
A booking transaction is recorded so the booking counts for revenue: `paid` marks it settled against a payment method named after your API key (no gateway call), so the club can see and reconcile what you collected separately from every other channel; `unpaid` leaves it outstanding. `amount` is the price the customer paid for the booking, before any fee you kept, and defaults to the computed price for the slot - the area's price, plus the instructor's hourly rate when one is attached. Send your own payment id as `payment.reference` and your commission as `payment.fee`: the sale is recorded at `amount`, and the fee is what the club reconciles against your payout statement.
This API assumes the calling system enforces its own booking rules, so 1club's member-facing policy (minimum notice, maximum days in advance, operating-hours envelope, slots that have already ended, instructor availability) is not applied - which is what lets an existing schedule be recorded as-is.
What the club physically has is never overridden, and a booking that breaks one of these is refused with `400` and a populated `details.conflicts[]`: the area's concurrency capacity (`AREA_AT_CAPACITY`), a court the club has blocked for maintenance or a private hire (`AREA_BLOCKED`), a live event on the court (`AREA_RUNNING_EVENT`), the instructor already booked or teaching (`INSTRUCTOR_AT_CAPACITY`, `INSTRUCTOR_TEACHING_CLASS`, `INSTRUCTOR_RUNNING_EVENT`), whether an instructor takes one-on-one bookings, and the slot alignment the area requires. There is no override parameter: a conflict is a refusal, not a warning.
# Get a booking
Source: https://docs.1club.ai/api-reference/bookings/get-a-booking
/openapi-platform.json get /v1/platform/bookings/{id}
Returns the current state of a booking (including payment status) by its 1club id - any of the organization's bookings, whichever channel created it. Nothing here is absent from the list endpoint, which has always returned every source.
# List bookings
Source: https://docs.1club.ai/api-reference/bookings/list-bookings
/openapi-platform.json get /v1/platform/bookings
Returns bookings whose start time falls in the requested window, ordered by start time. Each bound is an ISO date-time, or a bare `YYYY-MM-DD` read as midnight UTC. When `from`/`to` are omitted the window defaults to the next 7 days, and passing one derives the other.
A single request may span at most 93 days. To cover a longer range, walk it in consecutive windows of 93 days or less, paging each window with `limit`/`offset` against the `total` it reports. Ordering is stable (`startTime`, then id), so paging is well defined for a window that is not being written to; bookings created or cancelled mid-walk can still shift rows across page boundaries.
Bookings from every channel are returned, each tagged with `source`. That is deliberate: a caller reconciling its own writes has to be able to see member- and staff-created bookings to know whether a slot was already taken. Whether the writes (cancel, add/remove participant) reach those bookings too depends on the credential: an API key may only mutate the bookings it created (`source: api`), while a staff member acting through an authorized 1club assistant may mutate any of the organization's.
A class booking carries the occurrence it attends as `classId`. Every attendee of a class holds their own booking row against the same `classId`, and `classes` rows are per-occurrence, so that is what tells two groups running in the same hour apart - filter by it, or read the roster from the class side with GET /v1/platform/classes/{id}/bookings.
# List the additional players on a booking
Source: https://docs.1club.ai/api-reference/bookings/list-the-additional-players-on-a-booking
/openapi-platform.json get /v1/platform/bookings/{id}/participants
Returns the extra players on a booking - any of the organization's, whichever channel created it. The person the booking is for (the organizer) is on the booking itself as `contactId` and is not repeated here. Only the add and remove writes are restricted to bookings this API created.
This lists the co-players sharing *one* booking (a doubles court). The attendees of a class each hold their own booking - read those with GET /v1/platform/classes/{id}/bookings.
# Remove a player from a booking
Source: https://docs.1club.ai/api-reference/bookings/remove-a-player-from-a-booking
/openapi-platform.json delete /v1/platform/bookings/{id}/participants/{contactId}
Removes an additional player from a booking. Reaches the same bookings as cancel: an API key may only remove from the ones it created, a staff member acting through an authorized 1club assistant from any of the organization's.
# Update a booking
Source: https://docs.1club.ai/api-reference/bookings/update-a-booking
/openapi-platform.json patch /v1/platform/bookings/{id}
Edits a booking by its 1club id: reschedule it, move it to another area, attach or remove an instructor, reassign the contact, change its notes or price, or make it recur. Send only the fields that change. Reaches the same bookings as cancel: an API key only those it created, a staff member through an authorized 1club assistant any of the organization's.
The edit follows the admin portal's rules. A cancelled booking is final, and a booking on a class takes its time and price from the class. As on create, the club's member-facing policy (operating hours, instructor availability) is not applied, but an occupied or blocked court, or a coach who is already booked, is refused with `details.conflicts[]`.
Money follows the edit. `amount` reprices the booking to your figure. Without it, a booking this API created keeps its price, and a booking taken through 1club is re-quoted when the slot, area, instructor or contact changes. The charge is then reconciled: an increase on a paid booking leaves the difference outstanding, and a decrease below what was paid needs a refund, which this endpoint does not perform. Payment status cannot be changed here.
Recurring bookings. `recurrence` on a one-off booking makes it the first occurrence of a series. On an occurrence of a series, `scope=all_future` applies the edit to every occurrence that has not started yet, from the earliest upcoming one: a new `startTime`/`endTime` gives each that time of day on its own date (the weekday changes only through `recurrence.daysOfWeek`), a new area or instructor is checked against every date, and a new `recurrence` rewrites the cadence or end condition; days dropped from a weekly pattern are cancelled with their refund. A series bound to a recurring class follows the class and cannot be re-timed, and a booking an API key created cannot be made to recur at all (`RECURRENCE_NOT_AVAILABLE_TO_MANAGING_KEY`): 1club would be generating occurrences that key never created, so each one is its own booking. An edit whose series has already run touches nothing.
# Areas and availability
Source: https://docs.1club.ai/api-reference/channel-integration/areas-and-availability
Discover bookable areas (your courts) and read their availability before booking.
Before creating bookings, discover the areas you can sell and read their availability. In 1Club a bookable padel court is an **area**; store the mapping between your courts and 1Club area ids on your side.
## List areas
Returns the organization's bookable areas. Filter by club or sport to get just the padel courts.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/areas?sport=padel" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Query parameters:
* `clubId` (optional) - only return areas belonging to this club
* `sport` (optional) - only return areas whose type lists this sport, for example `padel`
Response:
```json theme={null}
[
{
"id": 51,
"name": "Court 1",
"clubId": 8,
"sports": ["padel"],
"pricePerHour": 24,
"maxConcurrentBookings": 1,
"timezone": "Europe/Madrid",
"operatingHours": {
"monday": [{ "open": "08:00", "close": "22:00" }],
"tuesday": [{ "open": "08:00", "close": "22:00" }]
}
}
]
```
Use each area's `id` as the `areaId` when creating a booking.
`timezone` is the IANA zone the gym keeps, and the one `operatingHours` are
expressed in. Convert a local wall time in it when you send `startTime` and
`endTime` - a booking sent in the wrong zone lands an hour out twice a year, at
the daylight-saving boundary, and looks correct the rest of the time. Map it once
when you map the courts.
## Area availability
Returns everything you need to render a court's calendar for a window: the club
timezone, operating hours, the intervals already taken (`busy`), and ready-made
open `slots` with remaining capacity.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/areas/51/availability?from=2026-07-20T00:00:00Z&to=2026-07-21T00:00:00Z&slotMinutes=60" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Query parameters:
* `from` (required) - window start, ISO date-time
* `to` (required) - window end, ISO date-time, at most 31 days after `from`
* `slotMinutes` (optional) - slot granularity: `15`, `30`, `60` (default), `90`, or `120`
Response:
```json theme={null}
{
"areaId": 51,
"from": "2026-07-20T00:00:00.000Z",
"to": "2026-07-21T00:00:00.000Z",
"timezone": "Europe/Madrid",
"maxConcurrentBookings": 1,
"version": "48213",
"operatingHours": {
"monday": { "enabled": true, "openTime": "08:00", "closeTime": "22:00" }
},
"busy": [
{ "startTime": "2026-07-20T17:00:00.000Z", "endTime": "2026-07-20T18:00:00.000Z", "subject": "member_booking" },
{ "startTime": "2026-07-20T18:00:00.000Z", "endTime": "2026-07-20T19:00:00.000Z", "subject": "class" },
{ "startTime": "2026-07-20T19:00:00.000Z", "endTime": "2026-07-20T20:00:00.000Z", "subject": "area_block" }
],
"slots": [
{ "start": "2026-07-20T06:00:00.000Z", "end": "2026-07-20T07:00:00.000Z", "capacity": 1, "remaining": 1, "bookable": true },
{ "start": "2026-07-20T15:00:00.000Z", "end": "2026-07-20T16:00:00.000Z", "capacity": 1, "remaining": 0, "bookable": false }
]
}
```
* **`timezone`** - the IANA timezone the `operatingHours` are expressed in. Operating hours are per-weekday **local** times; slots are already resolved to absolute UTC instants for you (DST included), so prefer `slots` over re-deriving from `operatingHours`.
* **`slots`** - open slots computed from effective bookable hours (club ∩ area) minus `busy`, honouring `maxConcurrentBookings`. `remaining` is how many of `capacity` are still free; `bookable` is `remaining > 0` and no hard block.
* **`busy`** and **`operatingHours`** are still returned raw if you'd rather compute slots yourself. An area with `maxConcurrentBookings` greater than 1 can hold that many overlapping bookings before a slot is full.
* **`busy[].subject`** - what holds the court for that interval: `member_booking`, `class` (a scheduled class holding its court), `event`, or `area_block` (the gym closed the court for maintenance or a private hire). Same vocabulary as `details.conflicts[].subject` on a [refused booking](/api-reference/channel-integration/bookings#conflicts), so an hour you cannot sell can be explained rather than shown as unattributed busy time. A class is a booking row underneath, so `subject` is the only thing that separates it from a customer's reservation. No id or name is attached - an availability read is not a read of who booked. Use [List bookings](/api-reference/channel-integration/bookings#list-bookings) for the rows you may see.
* **`version`** - a consistency token (the highest change sequence for this area). Compare it to the `sequence` on an [availability webhook](/api-reference/channel-integration/webhooks) to know whether this read already reflects that change.
### Conditional requests
The response sends an `ETag`. Send it back as `If-None-Match` to get a cheap
`304 Not Modified` when nothing has changed - useful as a polling fallback where
you don't run webhooks.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/areas/51/availability?from=...&to=..." \
-H "Authorization: Bearer 1club_sk_live_..." \
-H 'If-None-Match: "v2-48213-1784505600000-1784592000000-60"'
```
**Treat the token as opaque.** It covers the area's `version`, the window you
asked for, the slot size, and the shape of the response itself - so it changes
when we ship a new field, not only when a court's availability moves. Store it
verbatim against the request that produced it and send it back unmodified; do not
parse it or rebuild it from `version`, or a release that adds a field will leave
you on a `304` and the old body.
## Club availability grid
Render a whole club's calendar in one request instead of one call per court.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/availability?clubId=8&sport=padel&from=2026-07-20T00:00:00Z&to=2026-07-21T00:00:00Z&slotMinutes=60" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Query parameters: `clubId` (optional), `sport` (optional), `from`/`to` (required, ≤ 31 days), `slotMinutes` (optional).
```json theme={null}
{
"clubId": 8,
"timezone": "Europe/Madrid",
"from": "2026-07-20T00:00:00.000Z",
"to": "2026-07-21T00:00:00.000Z",
"cursor": "48213",
"areas": [
{
"areaId": 51,
"name": "Court 1",
"sports": ["padel"],
"pricePerHour": 24,
"maxConcurrentBookings": 1,
"version": "48213",
"operatingHours": { "monday": { "enabled": true, "openTime": "08:00", "closeTime": "22:00" } },
"busy": [{ "startTime": "2026-07-20T17:00:00.000Z", "endTime": "2026-07-20T18:00:00.000Z", "subject": "member_booking" }],
"slots": [{ "start": "2026-07-20T06:00:00.000Z", "end": "2026-07-20T07:00:00.000Z", "capacity": 1, "remaining": 1, "bookable": true }]
}
]
}
```
The top-level `cursor` is the reconciliation cursor at read time - store it and pass it to the [change feed](#availability-change-feed) to catch up later.
## Availability change feed
Returns every availability change for your organization after a cursor, in order.
Pair it with [webhooks](/api-reference/channel-integration/webhooks): webhooks push
changes in near real time; this feed lets you catch up exactly after any missed
delivery, so your calendar never drifts.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/availability/changes?since=48000&limit=100" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Query parameters: `since` (required cursor; `0` for the start of retention), `limit` (optional, default 100, max 500).
```json theme={null}
{
"changes": [
{
"sequence": "48213",
"clubId": 8,
"areaId": 51,
"from": "2026-07-20T17:00:00.000Z",
"to": "2026-07-20T18:00:00.000Z",
"reason": "booking.created",
"occurredAt": "2026-07-20T16:32:04.115Z"
}
],
"cursor": "48213",
"hasMore": false
}
```
To reconcile: pass your last stored `cursor`, apply each change by re-pulling
`/availability` for the affected area and window, then store the new `cursor`.
Keep paging while `hasMore` is `true`.
On **first sync** you have no cursor - do a full grid pull and start from the
`cursor` it returns (starting from `0` for a live club replays its whole
history). The feed serves a **rolling history**; if your cursor predates it
after a long outage, re-baseline with a full grid pull. Some changes (e.g.
`area.updated`) carry a **coarse, wide** `from`/`to` window rather than a single
slot - re-pull the whole window. See the
[webhooks guide](/api-reference/channel-integration/webhooks#reconcile-after-downtime)
for the full reconciliation flow.
Availability is a snapshot. 1Club remains the source of truth: booking
creation re-checks availability atomically, so a slot that was open a moment
ago may be rejected if it was taken in between. Always handle a booking
rejection gracefully - and use [webhooks](/api-reference/channel-integration/webhooks)
plus this feed to keep the snapshot fresh.
## Area blocks
A **block** takes an area off the bookable schedule for a period: resurfacing, a holiday, a camp. Availability already reflects blocks, so a blocked slot simply won't appear in `slots`. Read blocks directly when you want the *reason* and the period to show in your own calendar, or when migrating a schedule into 1Club.
A block with `areaId: null` is gym-wide: it blocks every area in its `clubId`. If you key your calendar by court, expand it against that gym's areas.
### List blocks
Requires the `areas:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/area-blocks?from=2026-08-01T00:00:00Z&to=2026-08-08T00:00:00Z" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Query parameters:
* `from` (required) - window start, ISO date-time
* `to` (required) - window end, at most 31 days after `from`
* `clubId` (optional) - only blocks for this club
* `areaId` (optional) - only blocks affecting this area, gym-wide ones included
Any block that *overlaps* the window is returned, so a week-long block still appears when you ask about one day inside it.
Response:
```json theme={null}
[
{
"id": 12,
"clubId": 8,
"areaId": 51,
"areaName": "Court 1",
"startTime": "2026-08-03T07:00:00.000Z",
"endTime": "2026-08-03T19:00:00.000Z",
"note": "Resurfacing",
"createdAt": "2026-07-28T09:12:44.001Z"
},
{
"id": 13,
"clubId": 8,
"areaId": null,
"areaName": null,
"startTime": "2026-08-05T00:00:00.000Z",
"endTime": "2026-08-06T00:00:00.000Z",
"note": "Public holiday",
"createdAt": "2026-07-28T09:14:02.884Z"
}
]
```
### Create a block
Requires the `areas:write` scope.
Send `areaId` to close one court, and its gym is derived for you. To close a whole gym, send `clubId` and leave `areaId` out - one record rather than one per court, and areas added later are covered too.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/area-blocks" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"areaId": 51,
"startTime": "2026-08-03T07:00:00Z",
"endTime": "2026-08-03T19:00:00Z",
"note": "Resurfacing"
}'
```
Body:
* `startTime`, `endTime` (required) - ISO date-times with offset. `endTime` must be after `startTime`.
* `areaId` (optional) - the court to block. Omit or send `null` for a gym-wide block.
* `clubId` - required when `areaId` is omitted or `null`; otherwise derived from the area.
* `note` (optional) - up to 500 characters, shown on the gym's schedule.
Returns `201` with the created block.
A block stops **new** bookings. Bookings already inside the period are not
cancelled, so migrate or cancel those separately if you need the period clear.
### Delete a block
Requires the `areas:write` scope. Deleting a block reopens the period for booking.
```bash theme={null}
curl -X DELETE "https://api.1club.ai/v1/platform/area-blocks/12" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Returns `{ "success": true }`. A second delete of the same id returns `404`.
Creating or deleting a block emits an availability change for every affected
area, so the
[changes feed](/api-reference/channel-integration/areas-and-availability#availability-change-feed)
and [webhooks](/api-reference/channel-integration/webhooks) will tell you to
re-pull those courts. A gym-wide block emits one change per area in the gym.
# Authentication
Source: https://docs.1club.ai/api-reference/channel-integration/authentication
Create a scoped API key to sync bookings through the channel integration.
The channel integration uses the same API key mechanism as the rest of the [Platform API](/api-reference/introduction) - no separate enablement step is needed.
## Create an API key
API keys are created in the admin dashboard under **Settings** > **Organization** > **APIs & webhooks**. See [Creating an API token](/api-reference/introduction#creating-an-api-token) for the full flow.
API access is a paid feature. Organizations on the free plan or in trial can't
create API keys, and existing keys stop working if an organization downgrades
(requests return `403` with `code: "PLAN_FEATURE_LOCKED"`).
Grant only the scopes the integration needs:
* `areas:read` - list areas and read their availability
* `bookings:read` - read a booking and list bookings in a window
* `bookings:write` - create and cancel bookings, and manage players on them
* `contacts:read` - search customers and open one by id
* `contacts:write` - create a customer
* `instructors:read` - search coaches and open one by id
* `plans:read` - find the membership plans a gym sells
* `plans:write` - create, update, retire, and delete plans
* `classes:read` - list and read classes
* `classes:write` - create, update, and delete classes
* `memberships:read` - read memberships
* `memberships:write` - create memberships, and pause, resume, or cancel them
* `checkins:read` - list and read attendance records
* `checkins:write` - check in an existing booking
* `products:read` - search the product catalogue and its categories, and read how many are left to sell
* `products:write` - create and update products and product categories
A booking-only integration needs the first three. Add the contacts and instructors scopes when you want to resolve a customer or a coach by name before booking - see [Contacts](/api-reference/channel-integration/contacts) and [Instructors](/api-reference/channel-integration/instructors).
`bookings:write` is required to create or cancel bookings. A read-only key
cannot write bookings, and the legacy broad read grant does not satisfy write
scopes.
Pass the key as a Bearer token on every request:
```
Authorization: Bearer 1club_sk_live_...
```
The key is tied to one organization, so all areas and bookings are scoped to that organization automatically.
## Verify a key
`GET /v1/platform/me` needs a valid key but **no scope of its own**, which makes it
the right call to run when a gym connects your integration: it confirms the key
works, names the organization, and tells you what the key may do.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/me" \
-H "Authorization: Bearer 1club_sk_live_..."
```
```json theme={null}
{
"organization": { "id": 1435, "name": "Tennis & Padel Club", "slug": "tennis-padel", "currency": "EUR" },
"scopes": ["areas:read", "bookings:read", "bookings:write"],
"fullAccess": false,
"effectiveScopes": ["areas:read", "bookings:read", "bookings:write"],
"resources": [{ "resource": "bookings", "actions": ["read", "write"] }]
}
```
* **Check a capability against `effectiveScopes`, not `scopes`.** A full-access key
holds the single wildcard scope `*` and reports `scopes: ["*"]` with
`fullAccess: true`. Looking for `"bookings:write"` in `scopes` would reject the
one key that can do everything; `effectiveScopes` expands the wildcard for you.
* **`organization.currency`** is the ISO 4217 code the gym trades in, and the only
place this API states it. Every monetary amount elsewhere - a booking's
`payment.amount`, plan and product prices, transaction totals - is a bare number
in this currency. Read it once and store it against the connection rather than
assuming it matches your own.
* **`organization.id`** is the same id that arrives as `organizationId` on an
[availability webhook](/api-reference/channel-integration/webhooks), so a
per-organization connection can be keyed on it.
## Rate limits and errors
The channel endpoints share the Platform API's limits and error model:
* **100 requests per minute** per organization. A `429` response includes a `Retry-After` header.
* Errors return a JSON body of the shape `{ "error": "...", "message": "...", "code": "..." }`.
See [Rate limiting](/api-reference/introduction#rate-limiting) and [Errors](/api-reference/introduction#errors) for the full reference.
# Bookings
Source: https://docs.1club.ai/api-reference/channel-integration/bookings
Create, cancel, and look up bookings through the platform API.
Create a booking when a customer books a court in your app, and cancel it when they cancel on your side. 1Club returns a `bookingId` you use to cancel or look the booking up later.
## Create a booking
Requires the `bookings:write` scope.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/bookings" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: booking-8f2c1a" \
-d '{
"areaId": 51,
"startTime": "2026-07-20T17:00:00Z",
"endTime": "2026-07-20T18:00:00Z",
"contactId": 300,
"instructorId": 42,
"payment": { "status": "paid", "amount": 54 },
"channel": "Padel Community",
"notes": "Booked via Padel Community"
}'
```
Body fields:
* `areaId` (required) - the area id from [List areas](/api-reference/channel-integration/areas-and-availability#list-areas)
* `startTime`, `endTime` (required) - ISO date-times, `endTime` after `startTime`
* `contactId` - the customer, resolved through [Contacts](/api-reference/channel-integration/contacts)
* `customer` - an inline customer matched to a contact by email, instead of `contactId`
* `instructorId` (optional) - the coach leading the session, from [Instructors](/api-reference/channel-integration/instructors)
* `payment.status` (required) - `paid` (you collected the money) or `unpaid` (outstanding)
* `payment.amount` (optional) - amount collected; defaults to the computed price for the slot (max 1,000,000)
* `channel` (optional) - a label stored for attribution
* `notes` (optional)
* `notifyCustomer` (optional) - send the customer a 1Club confirmation email; defaults to `false`
Send **exactly one** of `contactId` or `customer`. Sending both, or neither, is a `400`.
Response (`201 Created`):
```json theme={null}
{
"bookingId": 48213,
"status": "confirmed",
"areaId": 51,
"instructorId": 42,
"contactId": 300,
"startTime": "2026-07-20T17:00:00.000Z",
"endTime": "2026-07-20T18:00:00.000Z",
"payment": { "status": "paid", "amount": 54 }
}
```
Creating a booking accepts an `Idempotency-Key`, so a timeout cannot become two bookings. See [Idempotency](/api-reference/channel-integration/idempotency).
### Identifying the customer
`contactId` is the recommended path: resolve the customer with [Search contacts](/api-reference/channel-integration/contacts#search-contacts), create them if new, then book against the id. It is explicit about which person the booking is for, and it works for customers you only know by name.
The inline `customer` object remains supported for integrations that never hold 1Club ids. It is matched to a contact by email, and a contact is created when there is no match - see [How contacts sync](#how-contacts-sync).
### Adding a coach
Pass `instructorId` when the session is coached rather than a bare court hire. The coach must take one-on-one bookings (`isBookable`), and their own calendar is respected, so a booking that would double-book them is rejected. Attaching a coach also feeds the gym's instructor pay records automatically.
### How the amount is calculated
`payment.amount` is optional. When you omit it, 1Club prices the slot itself: the **area price per hour plus the instructor hourly rate, times the duration**. A one hour court at 24 with a coach at 30 is priced at 54. Without an instructor it is just the area price. Send `payment.amount` explicitly whenever your side is the authority on what the customer paid.
### How payment is recorded
* `paid` - 1Club records the booking's charge as paid (a Charge in the 1Club admin, a `transaction` in the Platform API), settled against a dedicated **External / API** payment method (manual channel, no card charged). Counts as revenue and paid.
* `unpaid` - 1Club records the booking's charge as outstanding. Counts as accrued revenue with nothing paid.
The `payment.status` you get **back** can also be `no_charge`, which means nothing was ever owed - a free area, or an explicit amount of `0`. There is no charge to settle in that case, so it is reported as `no_charge` rather than as whichever of `paid` or `unpaid` you sent.
You keep ownership of collection and refunds. 1Club never charges a card for these bookings.
### Booking policy is not applied to this API
1Club has member-facing booking rules a gym can configure: a minimum notice period, a maximum number of days in advance, an opening-hours envelope, and a block on slots that have already ended. **None of them are enforced for this API**, because the system calling it runs its own booking rules.
That is what lets you record a schedule as it stands, including sessions in the past when you are migrating an existing calendar into 1Club.
Physical constraints always apply and are never overridden: a genuine
double-booking, the area's concurrency capacity, a court the gym has **blocked**
for maintenance or a private hire, a live event on the court, the coach's own
calendar, whether an instructor takes one-on-one bookings, and the slot
alignment the area requires. A booking that breaks one of these is rejected -
see [Conflicts](#conflicts). There is no override parameter.
## How contacts sync
Every booking belongs to a **contact** in the gym's 1Club CRM. When you send an
inline `customer` object instead of a `contactId`, that customer is resolved to a
contact automatically, as a by-product of booking sync. Sync is one-way:
**partner to 1Club**. 1Club never pushes contact changes back to you.
This section describes the inline `customer` path. To resolve a customer yourself
before booking - by name, email, or phone - use
[Contacts](/api-reference/channel-integration/contacts) and pass the resulting
`contactId` instead.
### Matching and creation
1Club resolves the contact from `customer.email`:
* **Email is the identity key.** It's normalized (trimmed and lower-cased) and matched within the club's organization. Matching is **case-insensitive** - `Alex@Example.com` and `alex@example.com` are the same contact.
* **Scoped per organization.** Contacts belong to one organization. The same email booking at two different clubs (organizations) is two separate contacts - there is no global customer identity across clubs.
* **Match wins over create.** If a contact with that email already exists, the booking links to it. If not, a new contact is created from the `customer` fields (`firstName`, `lastName`, `phone`; `name` is derived from first + last, falling back to the email when no name is given).
* **The booking stores the resolved `contactId`.** From then on the booking, its revenue, and its history all roll up to that one contact in the CRM.
For an **existing** contact, 1Club matches by email and does **not** overwrite the
stored name or phone with what you send. Treat contact profile fields as
create-time only - sending a new phone on a later booking for the same email will
not change the contact. (To correct contact details, edit the contact in 1Club.)
### Guarantees
* **No duplicates.** A unique constraint on `(organization, lower(email))` plus a create-race retry means two bookings racing with the same new email still produce exactly one contact.
* **Consistent identity.** Because matching is by normalized email, repeat customers always land on the same contact - their bookings accumulate rather than fragmenting.
* **Searchable immediately.** A newly created contact is available in the CRM (searchable by name and email) as soon as the booking succeeds.
### The full create flow
The `areaId` must be bookable through this API (active, public, bookable type).
If not → `400`.
Normalize `customer.email` (trim + lower-case), then **match** an existing
contact in the organization or **create** one from the `customer` fields. An
existing contact is reused as-is (name/phone not overwritten). When you send a
`contactId` instead, it is checked against your organization and must not be
archived.
When `instructorId` is present, the coach must be active in your organization
and must take one-on-one bookings.
Link the resolved `contactId` and write the booking, re-checking availability
**atomically**. If the slot was taken in the meantime → `400` with a populated
`details.conflicts` - see [Conflicts](#conflicts).
Record the booking's charge (`paid` → settled, `unpaid` → outstanding) and return
`201` with `{ bookingId, status, payment }`.
Contact resolution runs **before** the booking write. So if the slot turns out to
be taken (`400`) or the request is otherwise rejected, a contact just created for a
first-time customer is **kept** - it isn't rolled back. That's harmless: it carries
no booking, stays matched by email, and is reused on retry. Only the booking write
re-checks availability atomically and rejects conflicts.
### Conflicts
A slot that cannot be sold is refused with **`400`**, not `409`. The body carries a
machine-readable `code` and a `details.conflicts` array naming what is in the way:
```json theme={null}
{
"error": "The area is already booked for the selected time",
"message": "The area is already booked for the selected time",
"code": "AREA_AT_CAPACITY",
"details": {
"conflicts": [
{
"resource": "area",
"resourceId": 1028,
"subject": "member_booking",
"id": null,
"label": null,
"startTime": "2026-09-15T15:00:00.000Z",
"endTime": "2026-09-15T16:00:00.000Z"
}
],
"capacity": 1,
"booked": 1
}
}
```
`subject` says what holds the slot, in the same vocabulary the
[availability](/api-reference/channel-integration/areas-and-availability) response
uses for `busy[].subject`. A court conflict is `member_booking`, `class`, `event`,
or `area_block`; a coach conflict adds `time_off` and `shift_break`. The counterpart's own `startTime`/`endTime` may be wider
than the slot you asked for.
`id` and `label` are always `null` on this API. They carry the counterpart's row id
and the member's name for the gym's own staff calendar, where someone is entitled to
see who they would be displacing - an integration is not, so it is told that the hour
is taken and no more.
The codes you can see today are `AREA_AT_CAPACITY`, `AREA_BLOCKED`,
`AREA_RUNNING_EVENT`, `INSTRUCTOR_AT_CAPACITY`, `INSTRUCTOR_TEACHING_CLASS`, and
`INSTRUCTOR_RUNNING_EVENT`.
**Detect a taken slot on the presence of a populated `details.conflicts`, not on
a list of codes.** The list grows as new kinds of occupancy are modelled, and a
code you do not recognise would otherwise read as a generic error - which at
checkout means confirming a booking the gym rejected.
Retrying a conflict does not help, and there is no parameter that forces one
through. Re-read availability for the window and offer another slot.
### Common errors
* `400` - the slot cannot be sold (see [Conflicts](#conflicts)), the area is not bookable through this API, the contact or instructor was not found in your organization, the contact is archived, the instructor does not take one-on-one bookings, or you sent both `contactId` and `customer` (or neither).
* `409` - an `Idempotency-Key` was reused with a different request body. Scheduling conflicts are `400`.
* `403` - the API key is missing the `bookings:write` scope.
* `429` - rate limit exceeded (booking writes are throttled in addition to the standard per-org limit).
Opening hours, minimum notice, and how far ahead you may book are **not** enforced for this API - see [Booking policy is not applied to this API](#booking-policy-is-not-applied-to-this-api).
## Cancel a booking
Requires the `bookings:write` scope. Cancels the booking by its 1Club id. No refund is issued by 1Club.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/bookings/48213/cancel" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Response:
```json theme={null}
{ "bookingId": 48213, "status": "cancelled" }
```
Cancelling is idempotent: cancelling an already-cancelled booking succeeds and returns the same response.
## Look up a booking
Requires the `bookings:read` scope. Returns the current state of a booking, including its payment status.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/bookings/48213" \
-H "Authorization: Bearer 1club_sk_live_..."
```
```json theme={null}
{
"bookingId": 48213,
"status": "confirmed",
"areaId": 51,
"instructorId": 42,
"contactId": 300,
"startTime": "2026-07-20T17:00:00.000Z",
"endTime": "2026-07-20T18:00:00.000Z",
"payment": { "status": "paid", "amount": 54 }
}
```
A `404` means no booking with that id exists in your organization. Pass
`include=customer` to embed the contact, as on
[List bookings](#embedding-the-customer).
Look-up and cancel only reach bookings **this API created**. That is deliberate:
cancelling issues no refund, so a key must not be able to cancel a booking a
member made in the gym's own app. To see bookings from every channel, use
[List bookings](#list-bookings).
## List bookings
Requires the `bookings:read` scope. Returns bookings whose start time falls in a window, so you can verify what you created and reconcile against what else is on the calendar.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/bookings?from=2026-07-20T00:00:00Z&to=2026-07-21T00:00:00Z&areaId=51" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Query parameters:
* `from`, `to` (optional) - ISO date-times bounding the booking start time. The window may span at most 93 days. Omit both for the next 7 days; pass one and the other is derived from it.
* `clubId`, `areaId`, `instructorId`, `contactId` (optional) - narrow to one gym, court, coach, or customer
* `status` (optional) - for example `confirmed`, `pending`, `cancelled`
* `include` (optional) - `customer` embeds each booking's customer, see [Embedding the customer](#embedding-the-customer)
* `limit` (optional) - 1 to 100, defaults to 50
* `offset` (optional) - defaults to 0
Response:
```json theme={null}
{
"data": [
{
"bookingId": 48213,
"status": "confirmed",
"areaId": 51,
"instructorId": 42,
"contactId": 300,
"clubId": 5,
"source": "api",
"notes": null,
"createdAt": "2026-07-18T08:30:00.000Z",
"startTime": "2026-07-20T17:00:00.000Z",
"endTime": "2026-07-20T18:00:00.000Z",
"payment": { "status": "paid", "amount": 54 }
}
],
"total": 1,
"limit": 50,
"offset": 0
}
```
Unlike look-up by id, this returns bookings from **every** channel, each tagged with `source` (`api` for yours, plus `admin`, `portal`, `app`, and others). That is what makes reconciliation possible: a caller that cannot see member and staff bookings cannot tell that a slot it thinks is free was taken in the gym's own tools.
### Embedding the customer
A booking carries the customer as a bare `contactId`. Pass `include=customer` on
either read to get the whole contact inline, so importing a window of bookings
costs one request instead of a [contact look-up](/api-reference/channel-integration/contacts)
per row - which matters against the 100 requests per minute budget.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/bookings?from=2026-07-20T00:00:00Z&include=customer" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Each row in `data` then carries a `customer` alongside its `contactId`:
```json theme={null}
{
"bookingId": 48213,
"contactId": 300,
"customer": {
"id": 300,
"uuid": "0199a1c2-...",
"name": "Alex Petrov",
"firstName": "Alex",
"lastName": "Petrov",
"email": "alex@example.com",
"phone": "+359881234567",
"type": "member",
"createdAt": "2026-05-02T09:14:00.000Z"
}
}
```
The object is exactly what [`GET /contacts/{id}`](/api-reference/channel-integration/contacts)
returns, so nothing is lost by skipping that call. `customer` is `null` when the
booking has no contact.
Requires the **`contacts:read`** scope in addition to `bookings:read`. A key
without it gets a `403` rather than a page with the field quietly missing -
which would read as "these bookings have no customer". `phone` can be `null`:
1Club requires an email **or** a phone on a contact, not both.
If your key deliberately holds no contacts scope, attendee **names** are still
available for class bookings: `GET /v1/platform/classes/{id}/bookings` returns
`contactName` under `bookings:read` alone, and gates only the email on
`contacts:read`. A name identifies who is on a roster; the whole contact record is
CRM data.
## Players on a booking
A doubles court is booked by one person but played by several. The customer the booking is for stays on the booking itself as `contactId`; the others are **players** on it.
### List players
Requires the `bookings:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/bookings/48213/participants" \
-H "Authorization: Bearer 1club_sk_live_..."
```
```json theme={null}
[{ "contactId": 301, "name": "Max Ivanov", "status": "confirmed" }]
```
### Add a player
Requires the `bookings:write` scope. The contact must already exist - create it through [Contacts](/api-reference/channel-integration/contacts#create-a-contact) first.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/bookings/48213/participants" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "contactId": 301 }'
```
### Remove a player
Requires the `bookings:write` scope. Returns `204` and does not affect the person the booking is for.
```bash theme={null}
curl -X DELETE "https://api.1club.ai/v1/platform/bookings/48213/participants/301" \
-H "Authorization: Bearer 1club_sk_live_..."
```
### Limits
The total, counting the person the booking is for, is capped by the area type's maximum players. Adding beyond it returns `400`. On an area that allows only one player, no additional players can be added at all.
Adding someone who is already the booking's customer, or already a player, returns `409`. Archived contacts cannot be added.
# Check-ins
Source: https://docs.1club.ai/api-reference/channel-integration/checkins
List attendance records and check in an existing booking through the Platform API.
Reading attendance requires `checkins:read`. Recording attendance requires `checkins:write`.
## List check-ins
```bash theme={null}
curl "https://api.1club.ai/v1/platform/checkins?classId=312&limit=25" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Optional filters:
* `contactId`
* `clubId`
* `classId`
* `bookingId`
* `startDate` and `endDate` as ISO 8601 date-times
* `limit` from 1 to 100 and `offset`
When both dates are present, the range cannot exceed 92 days. Results are ordered by attendance date, newest first.
Each record includes its customer, gym, class, booking, membership, and external program summaries when they exist.
## Get a check-in
```bash theme={null}
curl "https://api.1club.ai/v1/platform/checkins/8841" \
-H "Authorization: Bearer 1club_sk_live_..."
```
## Check in a booking
Resolve the booking with the bookings endpoint, then send its ID:
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/checkins" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: checkin-booking-9102" \
-d '{ "bookingId": 9102 }'
```
Body fields:
* `bookingId` (required) - the booking to check in.
* `clubId` (optional) - the gym the attendance is recorded against. 1Club derives the club from the booking, or from its class, so you almost never send it. Supply it only for a booking on an organization-wide event, which carries no club of its own; the club must belong to the authorized organization.
1Club applies the same rules as a dashboard check-in:
* The booking must belong to the authorized organization.
* The booking must be `confirmed` or `pending`. Any other status - cancelled, already `checked_in`, or a no-show - is rejected with `400`.
* Check-in must be inside the configured window.
* Required payment must already be settled.
* The booking cannot already have a check-in. A second attempt returns `409`.
A successful request returns `201 Created` with the attendance record and changes the booking status to `checked_in` atomically.
This endpoint checks in an existing booking. Use the 1Club dashboard for
walk-ins, direct membership entry, payment collection, and anonymous entry.
# Classes
Source: https://docs.1club.ai/api-reference/channel-integration/classes
List, create, update, and delete class occurrences through the Platform API.
Class reads require `classes:read`. Class management requires `classes:write`.
## List classes
```bash theme={null}
curl "https://api.1club.ai/v1/platform/classes?startDate=2026-08-01T00:00:00%2B03:00&endDate=2026-09-01T00:00:00%2B03:00" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Use `startDate`, `endDate`, and `locale` to filter and translate the response. The endpoint returns class schedule, price, capacity, gym, area, instructors, and status fields.
## Get a class
```bash theme={null}
curl "https://api.1club.ai/v1/platform/classes/312" \
-H "Authorization: Bearer 1club_sk_live_..."
```
The class must belong to the API key's organization.
## Create a class
Send one class occurrence. Recurrence creation remains in the 1Club dashboard.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/classes" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: morning-yoga-2026-08-12" \
-d '{
"clubId": 5,
"areaId": 18,
"instructorIds": [42],
"name": "Morning yoga",
"startTime": "2026-08-12T08:00:00+03:00",
"endTime": "2026-08-12T09:00:00+03:00",
"maxCapacity": 16,
"priceBeforeTax": 15,
"visibility": "Public"
}'
```
Times must include `Z` or an explicit UTC offset. All referenced gyms, areas, class types, and instructors must belong to the authorized organization. An area must belong to the selected gym.
Write `priceBeforeTax`, not `price`. A class reads back with both: `price`
includes your organization's tax and is what a member is charged, while
`priceBeforeTax` is the figure actually stored. Sending `price` back is
rejected with a `400` naming the right field - if it were accepted, reading a
class and posting it straight back would re-apply tax every time (20 becomes
24, then 28.80 at 20% exclusive tax). Read `priceBeforeTax`, send
`priceBeforeTax`, and a round trip changes nothing.
## Update a class
Send only fields that should change:
```bash theme={null}
curl -X PATCH "https://api.1club.ai/v1/platform/classes/312" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: class-312-capacity-20" \
-d '{ "maxCapacity": 20 }'
```
Changing the area, instructors, or time updates the related schedule reservations. Conflicting reservations return `409 Conflict`.
## Delete a class
Delete one occurrence:
```bash theme={null}
curl -X DELETE "https://api.1club.ai/v1/platform/classes/312" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Idempotency-Key: delete-class-312"
```
For a recurring class, add `?scope=all_future` to delete this and later occurrences. The default is `this_instance`.
Deletion returns `409 Conflict` when affected future classes have active customer bookings or waitlist entries. Cancel or move those records before retrying.
# Contacts
Source: https://docs.1club.ai/api-reference/channel-integration/contacts
Search, open, and create the customers your bookings belong to.
Every booking belongs to a **contact** in the gym's 1Club CRM. Use these endpoints to
resolve a customer you only know by name into the `contactId` you pass when
[creating a booking](/api-reference/channel-integration/bookings#create-a-booking),
and to create the customer when they are new.
You can still create a booking without touching these endpoints by sending an
inline `customer` object, which is matched to a contact by email. That path is
unchanged and remains supported. Prefer `contactId` whenever you can resolve
it, because it is explicit about which person the booking is for.
## Search contacts
Requires the `contacts:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/contacts?search=ivan&limit=10" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Query parameters:
* `search` (optional) - matches on name, email, or phone. An empty value is treated as no search.
* `limit` (optional) - 1 to 100, defaults to 25
* `offset` (optional) - defaults to 0
Response:
```json theme={null}
{
"data": [
{
"id": 300,
"name": "Ivan Petrov",
"firstName": "Ivan",
"lastName": "Petrov",
"email": "ivan@example.com",
"phone": "+359888123456",
"type": "member",
"createdAt": "2026-07-01T09:12:00.000Z"
}
],
"total": 1,
"limit": 25,
"offset": 0
}
```
`total` counts every contact matching the filters, ignoring pagination, so you can
tell whether more pages exist.
### Matching is case-insensitive and cross-script
Search normalizes the query and also matches transliterated forms, so `ivan` finds
`Иван` and `Иван` finds `Ivan`. Accents are stripped too, so `pena` matches `Peña`.
This is the same matching the gym's staff see in the admin CRM, so an integration
resolving a name lands on the same people a staff member would.
Archived contacts are never returned.
A single name often matches several people. Treat more than one result as a
question for a human rather than picking the first row - a booking made for
the wrong customer is not visible to them until they turn up.
## Open a contact
Requires the `contacts:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/contacts/300" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Returns the same object as one row of the search response. A `404` means no contact
with that id exists in your organization.
## Create a contact
Requires the `contacts:write` scope.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/contacts" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: contact-ivan-2026-07-20" \
-d '{
"firstName": "Ivan",
"lastName": "Petrov",
"email": "ivan@example.com",
"phone": "+359888123456"
}'
```
Body fields:
* `firstName` (required)
* `lastName` (optional)
* `email` (optional)
* `phone` (optional)
At least one of `email` or `phone` is required, so the person stays reachable and
findable. Email is normalized to lower case.
Response (`201 Created`) is the created contact.
Search before you create. Creating a contact whose email already exists
returns `409`, and duplicate people are awkward to merge afterwards. The
recommended flow is search, then create only when there is no match.
Creating a contact accepts an `Idempotency-Key`, so a retry after a timeout cannot
produce a second record. See [Idempotency](/api-reference/channel-integration/idempotency).
## Resolving a customer before booking
Call **Search contacts** with the customer's name, email, or phone.
One match is your customer. Several matches need a human to choose. No match
means they are new.
Call **Create a contact** with at least an email or a phone.
Pass the resolved `contactId` on [Create a
booking](/api-reference/channel-integration/bookings#create-a-booking).
## What this API does not do
* **No contact updates or deletes.** Profile fields are edited by the gym in 1Club. This keeps the CRM record authoritative on the gym's side.
* **No merging.** If duplicates appear, they are resolved inside 1Club.
* **One-way sync.** 1Club never pushes contact changes back to you.
* **Archived contacts cannot be booked.** Archiving is how a gym retires a contact. An archived contact is hidden from search and rejected as the person a booking is for, or as a player on one.
# Idempotency
Source: https://docs.1club.ai/api-reference/channel-integration/idempotency
Retry any write safely with an Idempotency-Key, so a timeout never becomes a duplicate booking.
A request that times out leaves you with a real problem: the booking may or may not
exist, and retrying blind risks creating it twice. Send an `Idempotency-Key` on any
write and 1Club replays the original response instead, so a retry is always safe.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/bookings" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Idempotency-Key: booking-8f2c1a-2026-07-20T17" \
-H "Content-Type: application/json" \
-d '{ "areaId": 51, "startTime": "2026-07-20T17:00:00Z", "endTime": "2026-07-20T18:00:00Z", "contactId": 300, "payment": { "status": "paid" } }'
```
The header is optional. Without it, every request executes normally, so existing
integrations are unaffected.
## Choosing a key
Use a value that identifies **the operation**, not the attempt - a UUID you generate
once and reuse across retries, or something derived from your own booking id. Keys
are scoped to your organization and can be up to 255 characters.
Do not reuse a key for a different operation. The key is remembered together with a
fingerprint of the request, so reusing it with a different body is treated as a
mistake rather than a new write.
## What each outcome means
| Situation | Response |
| - | - |
| First request with this key | Executes normally |
| Same key, same request, already finished | The original response, with `Idempotent-Replay: true` |
| Same key, still in flight | `409` - retry shortly |
| Same key, different request body | `409` - the key was already used for something else |
| Previous attempt failed | Executes normally; a failed attempt frees the key |
A replayed response carries the original status code and body, including the
original `bookingId`. Field order in your JSON body does not matter - a reordered
but otherwise identical body still replays.
## Failed attempts free the key
If a write fails, the key is released, so you can retry it - including with a
corrected body after a validation error. This is what makes an interrupted bulk sync
resumable: rerun the whole batch with the same keys, and the writes that already
succeeded replay while the ones that failed are attempted again.
## When a connection drops
If the connection is lost before the response is written, 1Club deliberately keeps
the key claimed, because the write itself may have committed. A retry then returns
`409` rather than risking a duplicate. Confirm what actually landed with
[List bookings](/api-reference/channel-integration/bookings#list-bookings) before
deciding what to do.
## Retention
Stored responses are kept for 24 hours. After that a reused key is treated as a new
request, so retries should happen well inside that window.
Cancelling a booking is already idempotent without a key: cancelling an
already-cancelled booking succeeds and returns the same response.
# Instructors
Source: https://docs.1club.ai/api-reference/channel-integration/instructors
Find the coach leading a session and get the instructorId a booking needs.
An **instructor** is a coach on the gym's staff. Attach one to a booking when the
session is coached rather than a bare court hire - a lesson, a personal training
slot, a padel clinic.
These endpoints are read-only. Instructors are staff records created and managed by
the gym in 1Club, so this API never creates or edits them.
## Search instructors
Requires the `instructors:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/instructors?search=maya&bookableOnly=true" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Query parameters:
* `search` (optional) - matches on the instructor's name
* `clubId` (optional) - only instructors assigned to that gym
* `sport` (optional) - only instructors who list that sport, for example `padel`
* `bookableOnly` (optional) - `true` or `false`; when `true`, only instructors who take one-on-one bookings
* `limit` (optional) - 1 to 100, defaults to 25
* `offset` (optional) - defaults to 0
Response:
```json theme={null}
{
"data": [
{
"id": 42,
"name": "Maya Ivanova",
"clubId": 5,
"sports": ["padel"],
"hourlyRate": 30,
"isBookable": true,
"maxConcurrentBookings": 1,
"operatingHours": {}
}
],
"total": 1,
"limit": 25,
"offset": 0
}
```
Name matching works the same way as
[contact search](/api-reference/channel-integration/contacts#matching-is-case-insensitive-and-cross-script):
case-insensitive and cross-script, so `maya` finds `Мая`. Only active instructors
are returned.
## Open an instructor
Requires the `instructors:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/instructors/42" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Returns the same object as one row of the search response. A `404` means no active
instructor with that id exists in your organization.
## Fields that affect booking
**`isBookable`** decides whether the coach can be attached to a booking at
all. An instructor with `isBookable: false` does not take one-on-one bookings,
and a booking naming them is always rejected. Nothing overrides this, so
filter on it before offering a coach for sale.
**`hourlyRate`** is added to the area's price when 1Club prices the booking. A one
hour court at 24 with a coach at 30 is priced at 54 unless you send an explicit
`payment.amount`. See
[How the amount is calculated](/api-reference/channel-integration/bookings#how-the-amount-is-calculated).
**`clubId`** is the gym the instructor belongs to, or `null` when they work across
the whole organization.
**`maxConcurrentBookings`** is how many sessions the coach can run at once. Booking
beyond it is a genuine conflict and is rejected.
## Booking a coached session
Search by name with `bookableOnly=true`, so unbookable staff never reach
your booking flow.
Read the area's availability as usual. See [Areas and
availability](/api-reference/channel-integration/areas-and-availability).
Pass `instructorId` alongside `areaId` on [Create a
booking](/api-reference/channel-integration/bookings#create-a-booking).
The coach's own calendar is respected: a booking that would double-book them, or
exceed `maxConcurrentBookings`, is rejected with a `409`.
Attaching an instructor also feeds the gym's instructor pay records
automatically, so a coached booking you create is compensated the same way one
booked in 1Club is. You do not need to report anything extra.
# Memberships
Source: https://docs.1club.ai/api-reference/channel-integration/memberships
Put a customer on a plan, then pause, resume, or cancel it.
A **membership** puts a customer on a [plan](/api-reference/channel-integration/plans).
The plan supplies the price, joining fee, billing frequency and session allowance, so
a normal sale sends only the customer, the plan, and whether you collected the money.
## Create a membership
Requires the `memberships:write` scope.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/memberships" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: membership-ivan-2026-08" \
-d '{
"contactId": 300,
"planId": 9,
"payment": { "status": "paid" }
}'
```
Body fields:
* `contactId` (required) - the customer, from [Contacts](/api-reference/channel-integration/contacts)
* `planId` (required) - the plan, from [Plans](/api-reference/channel-integration/plans)
* `startDate` (optional) - ISO date-time; defaults to now
* `endDate` (optional) - omit to let the plan's billing frequency decide
* `autoRenew` (optional) - defaults to the plan's setting
* `payment.status` (required) - `paid` (you collected the money) or `unpaid` (outstanding)
* `overrides` (optional) - see [Overriding the plan](#overriding-the-plan)
Response (`201 Created`):
```json theme={null}
{
"membershipId": 7412,
"contactId": 300,
"planId": 9,
"planName": "Unlimited",
"clubId": 5,
"status": "active",
"startDate": "2026-08-01T00:00:00.000Z",
"endDate": "2026-09-01T00:00:00.000Z",
"renewalDate": "2026-09-01T00:00:00.000Z",
"autoRenew": true,
"price": 49,
"billingFrequency": "monthly",
"maxUses": null,
"usesRemaining": null,
"createdAt": "2026-08-01T09:12:00.000Z"
}
```
Creating a membership accepts an `Idempotency-Key`, so a timeout cannot produce two.
See [Idempotency](/api-reference/channel-integration/idempotency).
### How payment decides the status
The membership starts life according to what is owed, not according to what you
declare:
* **`paid`** - 1Club records a payment against the **External / API** payment method (no card is charged) and the membership becomes `active`, or `pending` when it starts in the future.
* **`unpaid`** - the charge is left outstanding and the membership sits at `pending_payment`. It grants no access until the money is settled in 1Club.
* **Nothing owed** - a free plan with no joining fee is `active` straight away. There is no payment to wait for.
An `unpaid` membership does not give the customer access. If they are already
training, either send `paid` or settle the charge in 1Club. This differs from
bookings, where an unpaid booking still stands - a membership grants ongoing
access, so it waits for the money.
Collection and refunds stay with you. 1Club never charges a card for these
memberships.
### Overriding the plan
A membership keeps its own copy of the plan's commercial terms, so a negotiated sale
is a normal case. Send only what differs; anything omitted takes the plan's value.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/memberships" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"contactId": 300,
"planId": 9,
"payment": { "status": "paid" },
"overrides": {
"amount": 35,
"signupFee": 0,
"maxUses": 12,
"usesRemaining": 4,
"sessionUnit": "hour"
}
}'
```
* `amount` - charged each period, instead of the plan's price
* `signupFee` - the joining fee; `0` waives it
* `maxUses` - session allowance; `null` for unlimited
* `usesRemaining` - sessions left right now, which is how you migrate a package the customer has already partly used. `null` means unlimited usage, so it is only valid when the allowance is unlimited too.
* `sessionUnit` - `booking` or `hour`
`usesRemaining` can never exceed the allowance it draws from, whether that is a
`maxUses` you sent or the plan's own. The same holds on update, against the state the
membership will be left in - so you cannot lower `maxUses` below a balance already on
it, and you cannot leave the balance unlimited under a finite allowance.
The two nulls are not the same. `maxUses: null` is an unlimited allowance, which
nothing can exceed. `usesRemaining: null` is unlimited **usage**, so a ten-session
allowance rejects it - otherwise the allowance would mean nothing. Send both as
`null` for a genuinely unlimited membership.
This is the same set of fields the gym's staff can change in the **Add membership**
dialog, so an API sale and a front-desk sale can express the same deal.
Read the plan first. Overriding blindly is how a membership ends up cheaper -
or more generous - than anyone intended.
## List memberships
Requires the `memberships:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/memberships?contactId=300&status=active" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Query parameters:
* `contactId` (optional) - only this customer's memberships
* `planId` (optional) - only memberships on this plan
* `status` (optional) - one of `pending`, `active`, `expired`, `cancelled`, `used`, `paused`, `pending_payment`
* `limit` (optional) - 1 to 100, defaults to 25
* `offset` (optional) - defaults to 0
Returns a page in the usual shape, newest start date first. An unrecognised `status`
is a `400`, not an empty list.
## Get a membership
Requires the `memberships:read` scope. Returns one membership by its 1Club id,
including `usesRemaining` - the sessions the customer has left.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/memberships/7412" \
-H "Authorization: Bearer 1club_sk_live_..."
```
## Update a membership
Requires the `memberships:write` scope.
```bash theme={null}
curl -X PATCH "https://api.1club.ai/v1/platform/memberships/7412" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "status": "cancelled", "cancelMode": "end_of_period" }'
```
Two kinds of change, and they cannot be combined:
**A status transition** - `status` on its own, optionally with the field that
configures it:
| `status` | Companion field | What happens |
| - | - | - |
| `cancelled` | `cancelMode` | `end_of_period` (default) keeps access until the paid period ends and stops the renewal. `immediate` ends access today. |
| `paused` | `resumeAt` | Pauses the membership. `resumeAt` must be in the future; the pause lifts by itself then. |
| `active` | - | Resumes a paused membership. Only valid from `paused`. |
**A detail change** - any of `endDate`, `autoRenew`, `maxUses`, `usesRemaining`. Use
`usesRemaining` to correct a balance after a migration.
`cancelMode` only applies to a cancellation and `resumeAt` only to a pause; sending
either with the wrong status is a `400` rather than being quietly ignored.
Statuses 1Club works out for itself - `expired`, `used`, `pending_payment` -
cannot be set here. They follow from dates, usage, and payment.
## What this API does not do
* **No plan changes.** Moving a customer to a different plan is a priced change needing proration, invoicing, and possibly a refund. Do it in 1Club.
* **No refunds.** Cancelling releases the membership; it never returns money.
* **No deletes.** Cancel it instead, so the history stays intact.
# Channel integration overview
Source: https://docs.1club.ai/api-reference/channel-integration/overview
Sync bookings and contacts from a partner booking platform into 1Club as a channel.
The channel integration lets a partner booking platform (for example a padel community app with court booking, player ratings, and matchmaking) sell your courts and push the resulting bookings into 1Club. 1Club stays the single source of truth for court inventory, so the same court can be booked from your own tools and the partner channel without double-booking.
This is a channel-manager model: the partner is one sales channel, and 1Club is the hub that owns the inventory and the booking record. The API is generic - any integration with an API key can use it.
In 1Club a bookable court is an **area** (areas are typed by sport, e.g.
padel). The API uses the term **areas** throughout, so the padel courts you
sell are areas of a padel type.
## How it fits together
1Club is the hub that owns court inventory; your platform is one sales channel
alongside the club's own tools, classes, and events. Everything a partner does maps
to three things it holds in 1Club:
Your bookable courts and their computed open slots - the shared inventory,
changed by every channel.
Each booking you create, recorded against revenue on your chosen payment status.
The customer on each booking, matched to (or created as) a CRM contact by email.
Fetch computed slots for one court or a whole-club grid.
See [Areas and availability](/api-reference/channel-integration/areas-and-availability).
Send the slot and a `contactId` (or an inline `customer`, matched to a contact
by email). 1Club records the booking and its revenue.
See [Contacts](/api-reference/channel-integration/contacts).
Any change on **any** channel emits a signed `availability.changed` webhook;
you re-pull the affected window and reconcile against the
[change feed](/api-reference/channel-integration/areas-and-availability#availability-change-feed)
so your calendar never drifts. See [Webhooks](/api-reference/channel-integration/webhooks).
## What syncs
* **Bookings** - the partner creates, cancels, and lists bookings through the API, optionally with a coach attached and extra players on them.
* **Contacts** - the customer on each booking. Resolve them yourself by name, email, or phone through [Contacts](/api-reference/channel-integration/contacts) and book against a `contactId`, or send an inline `customer` and let 1Club match by email as a by-product of booking sync - see [How contacts sync](/api-reference/channel-integration/bookings#how-contacts-sync) for matching rules, guarantees, and the full flow diagram.
* **Instructors** - read-only. Look up the gym's coaches to attach one to a coached session. See [Instructors](/api-reference/channel-integration/instructors).
## How money is recorded
You collect payment on your side; 1Club records the booking so it counts for revenue. On each booking you tell us the payment status:
* **`paid`** - 1Club records the booking's charge as paid (a Charge in the 1Club admin, a `transaction` in the Platform API), settled against a dedicated **External / API** payment method (on the `manual` channel, so no card is charged). It shows in revenue as paid.
* **`unpaid`** - 1Club records the booking's charge as outstanding. It shows in revenue as accrued, with nothing paid yet.
`amount` is optional and defaults to the computed price for the slot: the area's price, plus the coach's hourly rate when the booking names one. A booking that carries no charge at all comes back as `no_charge` rather than `paid` or `unpaid`. Refund handling and collection stay on your side - cancelling a booking releases the area and issues no refund from 1Club.
Amounts are bare numbers in the gym's own currency. Read it once from
`organization.currency` on [`GET /me`](/api-reference/channel-integration/authentication#verify-a-key)
and store it with the connection: nothing else on this API repeats it, so an
integration that assumes its own currency misprices every booking without ever
erroring.
## How it works
1. **Create an API key** under **Settings** > **Organization** > **APIs & webhooks** with the `areas:read`, `bookings:read`, and `bookings:write` scopes, adding `contacts:read`, `contacts:write`, and `instructors:read` if you resolve customers or coaches by name. See [Authentication](/api-reference/channel-integration/authentication).
2. **Discover areas** (your courts) and read their availability - per court or as a club-wide grid of computed slots. See [Areas and availability](/api-reference/channel-integration/areas-and-availability).
3. **Create bookings** with a payment status and optional amount, optionally naming the coach leading the session. Send an `Idempotency-Key` so a retry is safe. See [Bookings](/api-reference/channel-integration/bookings) and [Idempotency](/api-reference/channel-integration/idempotency).
4. **Cancel or look up** a booking by the 1Club booking id returned on create, or **list** a window to reconcile against every channel.
5. **Stay in sync** by registering a webhook endpoint under **Settings** > **Organization** > **APIs & webhooks** and reconciling with the change feed. See [Webhooks](/api-reference/channel-integration/webhooks).
## Staying in sync
1Club is a shared inventory: the same court can be booked from your app, the club's own tools, another channel, a class, or an event. To keep your calendar live you need to know when availability changes - not just discover it at booking time. Three mechanisms work together:
* **Availability change webhooks (push).** Register an HTTPS endpoint and 1Club sends a signed `availability.changed` event whenever a court's bookable availability changes on **any** channel, typically within half a minute - see [Delivery timing](/api-reference/channel-integration/webhooks#delivery-timing). The event is a thin invalidation - it tells you *which area and window changed*, and you re-pull the authoritative availability for that window. See [Webhooks](/api-reference/channel-integration/webhooks).
* **The read API is the source of truth (pull).** Webhooks tell you *when* to read; [`GET /availability`](/api-reference/channel-integration/areas-and-availability) tells you *what's true*. Every read carries a `version`/`cursor` so you can tell whether it already reflects a webhook you're holding. Booking creation still re-checks atomically and rejects conflicts - including a court the gym has blocked - so a stale calendar can never cause a double-booking. It is a backstop, not a substitute for honouring `bookable` and `remaining` yourself.
* **The change feed is the backstop (reconcile).** Webhooks are delivered at-least-once but can be missed (your endpoint is down, a deploy, a network blip). [`GET /availability/changes?since=`](/api-reference/channel-integration/areas-and-availability#availability-change-feed) returns every change since a cursor, so you can catch up exactly and never drift. Poll it periodically (e.g. every few minutes) and after any downtime.
Treat webhooks as a latency optimization and the change feed as the correctness
guarantee. A calendar that pushes on webhooks and reconciles on the feed stays
correct even if individual webhooks are lost.
# Plans
Source: https://docs.1club.ai/api-reference/channel-integration/plans
Find, create, update, retire, and delete membership plans.
A **plan** is what a membership is sold on. It carries its own price, joining fee,
billing frequency and session allowance, so creating a membership only has to name
the plan and the customer - the plan supplies the rest.
Read operations require `plans:read`. Create, update, retire, and delete operations require `plans:write`.
## Search plans
Requires the `plans:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/plans?search=unlimited" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Query parameters:
* `search` (optional) - matches on plan name or description
* `clubId` (optional) - only plans limited to that gym
* `sport` (optional) - only plans listing that sport, for example `padel`
* `includeInactive` (optional) - `true` to include retired plans, which are hidden by default
* `limit` (optional) - 1 to 100, defaults to 25
* `offset` (optional) - defaults to 0
Response:
```json theme={null}
{
"data": [
{
"planId": 9,
"name": "Unlimited",
"description": "All courts, all week",
"clubId": 5,
"price": 49,
"signupFee": 20,
"billingFrequency": "monthly",
"isActive": true,
"maxUses": null,
"sessionUnit": null,
"validityPeriod": null,
"autoRenews": true,
"sports": ["padel"]
}
],
"total": 1,
"limit": 25,
"offset": 0
}
```
By default only plans the gym still sells are returned, so everything here can be
sold. Add `includeInactive=true` when you need retired plans as well - to find one
you retired and want back, for example. Name
matching works like
[contact search](/api-reference/channel-integration/contacts#matching-is-case-insensitive-and-cross-script):
case-insensitive and cross-script.
## Open a plan
Requires the `plans:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/plans/9" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Returns the same object as one row of the search response.
Get-by-id returns retired plans too. Resolving an id you already hold is not the
same as offering a plan for sale, so a plan you retired stays readable here - and
reachable for a `PATCH` that un-retires it. Only [plan search](#search-plans)
hides retired plans, and only until you pass `includeInactive=true`.
## Create a plan
Requires `plans:write`. Send an `Idempotency-Key` so a retry cannot create a duplicate.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/plans" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: plan-unlimited-2026-08" \
-d '{
"name": "Unlimited",
"description": "All classes and courts",
"price": 49,
"signupFee": 20,
"billingFrequency": "monthly",
"autoRenews": true,
"maxUses": null,
"validFor": {
"scopes": ["classes", "areas"]
},
"visibility": "Public"
}'
```
`validFor.scopes` controls which booking resources the plan can cover. Omit a type list to cover every type in that scope. For example, `scopes: ["classes"]` covers every class type. Add `classTypes: [3, 7]` to limit it to those class type IDs.
An empty `validFor` creates a generic plan for direct gym entry. It does not make class, area, or instructor bookings free.
For a custom billing frequency, send both `customPeriod` and `customPeriodUnit`.
## Update or retire a plan
Requires `plans:write`. Send only fields that should change.
```bash theme={null}
curl -X PATCH "https://api.1club.ai/v1/platform/plans/9" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: retire-plan-9" \
-d '{ "isActive": false }'
```
Set `isActive` to `false` to retire a plan. Existing memberships keep their snapshotted plan details, but the plan is no longer something the gym sells.
A retired plan drops out of [plan search](#search-plans), so an integration that lists plans stops offering it. It does not disappear:
* `GET /v1/platform/plans/{id}` still returns it, with `isActive: false`.
* `GET /v1/platform/plans?includeInactive=true` lists it again alongside the active ones.
* `PATCH` with `{ "isActive": true }` puts it back on sale.
## Delete a plan
Requires `plans:write`.
```bash theme={null}
curl -X DELETE "https://api.1club.ai/v1/platform/plans/9" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Idempotency-Key: delete-plan-9"
```
Deletion returns `204 No Content`. If memberships or other records still reference the plan, the API returns `409 Conflict`. Retire an in-use plan instead of deleting it.
## What the fields mean for a sale
| Field | Why it matters |
| - | - |
| `price` | Charged each billing period. |
| `signupFee` | Charged **once** when the membership starts, on top of `price`. A plan can charge one even when `price` is 0, so the total can exceed the price. |
| `billingFrequency` | How often `price` recurs, and what the membership's end date is computed from when you do not set one. |
| `maxUses` | Session allowance a membership gets. `null` means unlimited. |
| `sessionUnit` | What a use is counted in - a booking or an hour. |
| `validityPeriod` | Days the membership stays valid, when the plan is time-limited. |
| `autoRenews` | Whether a membership on this plan renews by default. |
Read the plan before selling it and you can show the customer the real total -
`price` plus `signupFee` - rather than discovering the joining fee after the
charge lands.
## Next step
Pass the `planId` to
[Create a membership](/api-reference/channel-integration/memberships#create-a-membership).
# Products
Source: https://docs.1club.ai/api-reference/channel-integration/products
Search, create, and update the products a gym sells over the counter, and read how many are left to sell.
A **product** is something the gym sells over the counter: a bottle of water, a
tube of grips, a towel. Products are grouped into **product categories**, which are
the tabs on the till and the sections of a shop page.
Read operations require `products:read`. Create and update operations require `products:write`.
Scopes carry no hierarchy. A key holding `products:write` cannot read the
catalogue - grant both if the integration needs to look a product up before
changing it.
## Search products
Requires the `products:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/products?search=isotonic" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Query parameters:
* `search` (optional) - part of a name or description, or a whole SKU or barcode
* `clubId` (optional) - only products limited to that gym
* `categoryId` (optional) - only products in that category
* `brandId` (optional) - only products of that brand
* `isActive` (optional) - `true` for what is currently on sale, `false` for what has been retired
* `limit` (optional) - 1 to 100, defaults to 25
* `offset` (optional) - defaults to 0
Response:
```json theme={null}
{
"data": [
{
"productId": 12,
"name": "Isotonic drink",
"description": "500ml",
"clubId": 3,
"categoryId": 4,
"brandId": null,
"sku": "ISO-500",
"barcode": "3800123456789",
"price": 3.5,
"costPrice": 1.2,
"isActive": true,
"visibility": "Public",
"images": [],
"features": [],
"taxRateId": null,
"revenueAccountId": null,
"createdAt": "2026-01-01T09:00:00.000Z",
"updatedAt": "2026-01-02T11:30:00.000Z"
}
],
"total": 1,
"limit": 25,
"offset": 0
}
```
`search` matches `name` and `description` as a substring, and `sku` and `barcode`
**exactly**. Half a barcode finds nothing, because a scan code is only useful whole.
Name matching works like
[contact search](/api-reference/channel-integration/contacts#matching-is-case-insensitive-and-cross-script):
case-insensitive and cross-script.
Unlike [plans](/api-reference/channel-integration/plans), retired products are
returned too. An integration that manages a catalogue has to be able to find
what it switched off in order to reprice it or bring it back. Filter with
`isActive=true` for what the till currently sells.
## Open a product
Requires the `products:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/products/12" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Returns the same object as one row of the search response.
## Create a product
Requires `products:write`. Only `name` and `price` are required. Send an
`Idempotency-Key` so a retry cannot create a duplicate.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/products" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: product-iso-500-2026-08" \
-d '{
"name": "Isotonic drink",
"description": "500ml",
"price": 3.5,
"costPrice": 1.2,
"categoryId": 4,
"sku": "ISO-500",
"barcode": "3800123456789",
"visibility": "Public"
}'
```
Write bodies are strict: an unknown field is a `400` rather than a silent drop, so
a misspelled `costPrice` tells you instead of leaving the margin unset.
`clubId`, `categoryId`, `brandId`, `taxRateId`, and `revenueAccountId` must
reference rows in your own organization. A `400` names the first one that does not.
A `barcode` **or `sku`** already used by another product, package, pass, or
enrolled access card is a `409`. Both are scannable: the till matches a
scanned code against every barcode first and then against product SKUs,
because retail imports print the SKU on the shelf label. A scanned code has to
mean exactly one thing, so 1Club refuses the write rather than letting the new
code shadow the old one at the till. The error names what kind of thing holds
it, not which one - that record may sit behind a scope your key does not have.
Creating a product does not give it stock. Putting units on the shelf is done in
the 1Club dashboard - see [Stock is not writable here](#stock-is-not-writable-here).
## Update a product
Requires `products:write`. Send only the fields that should change.
```bash theme={null}
curl -X PATCH "https://api.1club.ai/v1/platform/products/12" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "price": 3.9 }'
```
Nothing is required. Send `null` to clear a nullable field, and omit it to leave it
alone. `images` and `features` are arrays and replace the whole list, so read the
product first if you are adding to one.
A `barcode` or `sku` is only checked for collisions when you actually send it, so
re-saving a product without touching its codes never conflicts with itself.
## Retire a product
Set `isActive` to `false`. The product comes off the till and keeps its sales
history.
```bash theme={null}
curl -X PATCH "https://api.1club.ai/v1/platform/products/12" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "isActive": false }'
```
There is no delete. Removing a product destroys its sales history, and a
product that still holds stock or sits in a package cannot be removed at all.
Retiring is what an integration should do; deleting outright stays in the
dashboard, where you can see what you are about to lose - see
[Products](/sales/products).
## How many are left to sell
Requires the `products:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/products/12/availability" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Response:
```json theme={null}
{
"productId": 12,
"data": [
{ "clubId": 3, "available": 7 },
{ "clubId": 5, "available": null }
]
}
```
This is not simply a shelf count. A product assembled from a recipe has no shelf,
and its figure is the limit its scarcest ingredient imposes, floored to whole units.
`available: null` means the product is not stock-tracked at that gym, so the
till will sell it freely. It does **not** mean the product has run out. Treat
`null` as unlimited, not as zero.
### Stock is not writable here
Availability is the only view of stock on this API. Booking a delivery in,
correcting a count after a stocktake, and writing off damaged stock all stay in the
dashboard - see [Inventory and stock](/sales/inventory). Stock held as ingredients
that recipes draw down is not exposed here at all.
## Product categories
Categories are how products are grouped. They are small and flat, so there is no
paging and no search - the list endpoint returns all of them, in display order.
### List categories
Requires the `products:read` scope.
```bash theme={null}
curl "https://api.1club.ai/v1/platform/product-categories" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Response:
```json theme={null}
{
"data": [
{
"categoryId": 4,
"name": "Drinks",
"sortOrder": 1,
"productCount": 6,
"createdAt": "2026-01-01T09:00:00.000Z",
"updatedAt": "2026-01-01T09:00:00.000Z"
}
]
}
```
`GET /v1/platform/product-categories/{id}` opens one category and returns the same
object.
### Create or update a category
Requires `products:write`.
```bash theme={null}
curl -X POST "https://api.1club.ai/v1/platform/product-categories" \
-H "Authorization: Bearer 1club_sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: category-snacks-2026-08" \
-d '{ "name": "Snacks" }'
```
Omit `sortOrder` and the category goes to the end of the list, which is what you
want unless you are rebuilding the whole ordering. `PATCH` renames a category or
moves it in the order; products in it are unaffected.
Categories cannot be deleted through the API either. `productCount` tells you
whether one is still in use.
## What the fields mean at the till
| Field | Why it matters |
| - | - |
| `price` | What the customer pays, in the organization's currency. |
| `costPrice` | What the gym pays for it, for margin reporting. Never shown to customers. |
| `barcode` | Scanned at the till. Unique across every product, package, pass, and access card in the organization. |
| `sku` | Stock-keeping code. Scannable at the till after every barcode, so it is unique on the same terms. |
| `taxRateId` | Per-product VAT rate. Takes precedence over the revenue account's default at the point of sale. |
| `visibility` | Who sees it on customer-facing surfaces: `Public`, `Member_only`, or `Private`. |
| `clubId` | Limits the product to one gym. `null` sells it at every gym. |
| `isActive` | `false` takes it off the till without deleting its sales history. |
Read the category list before creating products in bulk. Nothing stops you
from creating two categories with the same name, and a duplicate is easier to
avoid than to merge afterwards.
# Webhooks
Source: https://docs.1club.ai/api-reference/channel-integration/webhooks
Receive real-time availability changes so your calendar stays live.
Webhooks push availability changes to your app in near real time, so you can show
a live calendar instead of discovering conflicts only when a booking is rejected.
1Club sends a signed HTTP `POST` to your endpoint whenever a court's
bookable availability changes on **any** channel - your app, the club's own tools,
another partner, a class, or an event.
Webhooks are the low-latency signal; the [change feed](/api-reference/channel-integration/areas-and-availability#availability-change-feed)
is the correctness backstop. Build for both (see [Staying in sync](/api-reference/channel-integration/overview#staying-in-sync)).
## Quickstart
Build a live calendar in five steps:
1. **Baseline.** Pull the club grid once to render the calendar and store the returned `cursor`:
`GET /v1/platform/availability?clubId=8&from=…&to=…`.
2. **Register** an HTTPS endpoint (below) and store the `whsec_…` signing secret.
3. **Receive & verify.** On each `POST`, check the `1Club-Signature` header against the raw body ([verify the signature](#verify-the-signature)) and respond `2xx` within 10 seconds.
4. **Re-pull.** Fetch the affected window named in the event's `data` and update those slots:
`GET /v1/platform/areas/{areaId}/availability?from=…&to=…`. Keep the highest `version`/`sequence` you've applied.
5. **Reconcile.** On a slow timer - and after any downtime - call `GET /v1/platform/availability/changes?since=`, apply the deltas, and store the new `cursor`.
Steps 3-4 keep the calendar fresh in seconds; steps 1 and 5 keep it correct even if a webhook is never delivered.
## Register an endpoint
In the admin dashboard under **Settings** > **Organization** > **APIs & webhooks**, add a webhook endpoint:
an HTTPS URL to receive events. On creation you get a **signing secret**
(`whsec_...`) shown **once** - store it securely; you'll use it to verify every
delivery. You can rotate the secret or disable the endpoint at any time.
Webhooks are part of the paid API feature. Endpoints must use `https`.
## The `availability.changed` event
The only event today. Its payload is a **thin invalidation**: it names the area and
window that changed and why - not the new availability itself. On receipt, re-pull
[`GET /availability`](/api-reference/channel-integration/areas-and-availability) for
that area/window to get the authoritative state. This keeps 1Club the source of truth
and makes events safe to process out of order.
```json theme={null}
{
"id": "evt_9f2c4a1b8d7e4c2fa0b1c2d3e4f5a6b7",
"type": "availability.changed",
"occurredAt": "2026-07-20T16:32:04.115Z",
"sequence": "48213",
"organizationId": 8,
"data": {
"clubId": 8,
"areaId": 51,
"window": { "from": "2026-07-20T17:00:00.000Z", "to": "2026-07-20T18:00:00.000Z" },
"reason": "booking.created"
}
}
```
* **`id`** - unique per event. Deduplicate on it (delivery is at-least-once).
* **`sequence`** - the change's position in your org's change feed. Compare it to the `version` on an availability read to know whether your last read already reflects this change; use it as the `since` cursor for the [change feed](/api-reference/channel-integration/areas-and-availability#availability-change-feed).
* **`data.reason`** - a coarse cause label, e.g. `booking.created`, `booking.cancelled`, `booking.updated`, `booking.released`, `class.reserved`, `class.reservation_removed`, `event.published`, `event.unpublished`, `event.cancelled`, `event.updated`, `area.updated`. Treat it as a hint; always re-pull for truth.
`data.window` is the affected time range, but for configuration changes (such
as `area.updated`, when operating hours or capacity change) it is a **coarse,
forward-looking window** of up to \~60 days - not a single slot. Always re-pull
`/availability` for the window rather than assuming exactly one slot changed.
### `sequence`, `version`, and `cursor`
Three related tokens tie reads, webhooks, and the change feed together. All are
monotonically increasing integers **sent as strings** (they can exceed `2^53`, so
don't parse them into a JS `number` - compare as strings of equal length, or as
`BigInt`).
| Token | Scope | Where you see it | Use it to |
| - | - | - | - |
| `sequence` | one change | webhook `sequence`, change-feed `sequence` | order changes / detect gaps; pass as the `since` cursor |
| `version` | one area | availability read (`version`, and the `ETag`) | tell whether a read already reflects a given `sequence` |
| `cursor` | whole org | grid `cursor`, change-feed `cursor` | your reconciliation checkpoint |
## Verify the signature
Every delivery includes a timestamped HMAC-SHA256 signature over the raw request
body, so you can confirm it came from 1Club and reject replays.
Headers:
| Header | Description |
| - | - |
| `1Club-Signature` | `t=,v1=` |
| `1Club-Event-Id` | The event `id` (also in the body) |
| `1Club-Event-Type` | `availability.changed` |
| `1Club-Delivery-Attempt` | Attempt number, starting at 1 |
The `v1=` element is the signature-scheme version. If a future scheme is added it
will be sent as an extra element (e.g. `v2=`) alongside `v1=`, so parse by name and
verify the scheme you support rather than assuming a fixed layout.
The signed string is `"."`. Verify with a constant-time compare and
reject timestamps outside a tolerance (e.g. 5 minutes):
```ts theme={null}
import crypto from "node:crypto";
function verify(rawBody: string, header: string, secret: string, toleranceSec = 300): boolean {
const parts = Object.fromEntries(header.split(",").map(kv => kv.split("=")));
const t = Number.parseInt(parts.t, 10);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(parts.v1, "hex");
const b = Buffer.from(expected, "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```
Verify against the **raw** request body, before any JSON parsing/re-serialization
* a re-encoded body will not match the signature.
## Delivery, retries, and idempotency
### Delivery timing
Changes are dispatched by a worker that runs **every 30 seconds**, so a first
delivery normally arrives within about half a minute of the change, plus network
time. Treat the integration as near-real-time rather than instant: design the
booking flow so a court can be sold on either side during that window, and let the
[atomic re-check on create](/api-reference/channel-integration/bookings#conflicts)
be what stops a double-booking.
Do not measure delivery latency against a **staging** or trial environment. Those
run with CPU allocated only while serving requests, which starves the dispatch
worker between ticks and inflates the figure well past what production does.
### Reliability
* **Respond within 10 seconds.** Any `2xx` marks the delivery successful. A non-`2xx`, a connection error, or a response slower than the **10-second** timeout counts as a failure. Do the minimum synchronously (verify, enqueue, ack) and re-pull availability asynchronously.
* **At-least-once.** You may receive the same `id` more than once. Deduplicate on `id` and make handling idempotent (re-pulling availability already is).
* **Retries with backoff.** A failed delivery is retried up to **7 attempts total** - the first immediately, then after roughly 1m, 5m, 30m, 2h, 6h, and 24h (about **33 hours** end to end). Timing has a few seconds of jitter.
* **Auto-disable.** After the 7th attempt fails, the endpoint is **disabled automatically** and the reason is shown under **Settings** > **Organization** > **APIs & webhooks**. Re-enable it (and rotate the secret if needed) once healthy, then reconcile with the change feed to catch up on everything sent while it was down.
* **Ordering is not guaranteed.** Use `sequence` if you need to order or detect gaps; because events are invalidations you re-pull for, exact order rarely matters.
## Reconcile after downtime
If your endpoint was down (or auto-disabled), don't try to replay individual
webhooks - pull the [change feed](/api-reference/channel-integration/areas-and-availability#availability-change-feed)
from your last stored `cursor`, apply the changes, and store the new cursor. Polling
the feed on a slow timer (e.g. every few minutes) in addition to webhooks means your
calendar self-heals from any missed delivery.
**First sync (no cursor yet):** do the baseline grid pull ([Quickstart](#quickstart)
step 1) and start from the `cursor` it returns - don't start from `0` for a live
club, or you'll replay the entire history.
**Very long outages:** the feed serves a rolling history. If your stored `cursor`
is older than the retained history, the feed can't close the whole gap - discard
the cursor and re-baseline with a full grid pull, exactly like first sync. When in
doubt, a full grid re-pull is always a safe way to resynchronise.
## Troubleshooting
* **Deliveries stopped arriving.** The endpoint was likely auto-disabled after repeated failures - check **Settings** > **Organization** > **APIs & webhooks** for the disable reason, fix your endpoint, re-enable it, then reconcile from your last `cursor`.
* **Signature always fails.** You're almost certainly hashing a re-serialized body - verify against the exact bytes received (see the note above), and confirm you're using the current secret (rotating it invalidates the old one immediately).
* **Correlate and dedupe on your side.** Log the `1Club-Event-Id` and `1Club-Delivery-Attempt` headers: the event id lets you dedupe, and the attempt number tells first deliveries apart from retries.
# Check in an existing booking
Source: https://docs.1club.ai/api-reference/check-ins/check-in-an-existing-booking
/openapi-platform.json post /v1/platform/checkins
Records attendance for a booking during its configured check-in window. The booking must be `confirmed` or `pending`, and its payment and duplicate-attendance rules still apply. Supports the `Idempotency-Key` header.
# Get a check-in
Source: https://docs.1club.ai/api-reference/check-ins/get-a-check-in
/openapi-platform.json get /v1/platform/checkins/{id}
# List check-ins
Source: https://docs.1club.ai/api-reference/check-ins/list-check-ins
/openapi-platform.json get /v1/platform/checkins
Lists attendance records for the organization, newest first.
# Create a class
Source: https://docs.1club.ai/api-reference/classes/create-a-class
/openapi-platform.json post /v1/platform/classes
Creates a class. Times must be ISO 8601 instants with an explicit offset. Supports the `Idempotency-Key` header.
Without `recurrence` this creates one occurrence. With it, one request creates the whole repeating series - do not call this once per week. `startTime`/`endTime` stay required either way: they are the template slot, giving every occurrence its time of day and its duration, and the series starts from `startTime` (so there is no separate start date). A weekly rule whose `daysOfWeek` do not include that day puts the first occurrence on the first listed day at or after it.
A bounded series (`ends.type` of `until` or `after`) is materialized in full inside this request, so it is capped at 520 occurrences - over that the request is refused with `RECURRENCE_TOO_MANY_OCCURRENCES` rather than silently truncated. An open-ended series (`ends` omitted, or `ends.type: "never"`) materializes a rolling window and extends itself, so only the first weeks exist immediately.
`ends.occurrences` counts **occurrences, not weeks**, across every day in `daysOfWeek` - twelve weeks of a Monday-and-Wednesday class is `24`. To end on a date instead, use `ends.type: "until"` with an `endDate` and skip the arithmetic.
The area is checked for a clash on the **first** occurrence only - the same rule the admin dashboard applies. Later occurrences are written without re-checking the court, so a series can land on a slot that is already held in a later week; read GET /v1/platform/areas/{areaId}/availability for the window first if that matters.
The response is the first occurrence, and for a recurring create it also carries `series.occurrencesCreated` - what this request actually wrote. Compare it with what you asked for: an occurrence at a club that is closed, or past its closing date, is skipped rather than failing the create. Read the rest of the series back with GET /v1/platform/classes filtered on the `recurrenceId` it carries.
# Create a class type
Source: https://docs.1club.ai/api-reference/classes/create-a-class-type
/openapi-platform.json post /v1/platform/class-types
Adds a kind of class the organization runs. List the existing types first - a club rarely wants two types meaning the same thing, and this endpoint will happily create a duplicate name.
`slug` is optional and derived from the name when omitted, suffixed if that is taken. Send one explicitly only when something outside 1club already refers to it; an explicit slug that clashes is a 409 rather than a row quietly filed under a different key. Supports the `Idempotency-Key` header.
# Delete a class
Source: https://docs.1club.ai/api-reference/classes/delete-a-class
/openapi-platform.json delete /v1/platform/classes/{id}
Deletes one occurrence, or this and all future occurrences when `scope=all_future`.
# Get a class by ID
Source: https://docs.1club.ai/api-reference/classes/get-a-class-by-id
/openapi-platform.json get /v1/platform/classes/{id}
Returns a single class occurrence. The class must belong to the organization the API key belongs to. Read its roster with GET /v1/platform/classes/{id}/bookings.
# Get a class type
Source: https://docs.1club.ai/api-reference/classes/get-a-class-type
/openapi-platform.json get /v1/platform/class-types/{id}
Returns one class type and how many classes are run as it, past and future.
# List a class's roster
Source: https://docs.1club.ai/api-reference/classes/list-a-classs-roster
/openapi-platform.json get /v1/platform/classes/{id}/bookings
Returns who is booked into one class occurrence, whether they were checked in, and what they paid - the roster, from the class side. This is the answer to "two groups run at 10:00, who is in which": every attendee holds their own booking row against this class's id.
Attendees only: the occurrence's own area hold and any instructor slot are excluded by the same predicate that decides capacity, so the roster is drawn from exactly the population the class's `bookedCount` counts.
Do not expect `total` to equal `bookedCount`. Capacity counts `confirmed`, `pending` and `checked_in`, and adds standalone walk-in check-ins that have no booking to list; the roster also keeps `no_show`, because whether someone was enrolled and failed to turn up is what an attendance reconciliation is asking. Cancelled seats are the one status left out, unless `status=cancelled` asks for them.
Requires both `classes:read` and `bookings:read`: it is a class sub-resource that returns booking data. `contactEmail` additionally needs `contacts:read` and is null without it - a name identifies who is on the roster, an email address is contact PII and stays behind the scope that guards it elsewhere.
# List class types
Source: https://docs.1club.ai/api-reference/classes/list-class-types
/openapi-platform.json get /v1/platform/class-types
Returns the organization's class types - the kinds of class it runs, such as "Padel clinic" or "Yoga". Resolve one here to fill in `classTypeId` when creating a class, or `classTypeIds` when scoping a class pay rate policy to particular kinds of class.
A class type is not a label: it decides how many people one booking may bring, whether an empty class cancels itself and how long beforehand, and which account the class posts to - so read the whole record before choosing one rather than matching on the name.
There is no paging: an organization has a handful of these.
# List classes
Source: https://docs.1club.ai/api-reference/classes/list-classes
/openapi-platform.json get /v1/platform/classes
Returns the organization's class occurrences, ordered by start time. One row is one occurrence - a weekly class materializes a row per session, linked by `recurrenceId` and `occurrenceIndex` - so a row's `id` is what a booking's `classId` points at.
The window is open by default: with neither bound the whole schedule is in scope, past included, because this endpoint serves operators reconciling what happened rather than members shopping for a slot. `startDate` keeps occurrences still running at or after it, `endDate` keeps occurrences starting before it (exclusive), and each accepts an ISO date-time or a bare `YYYY-MM-DD` read as midnight UTC. Overlap, not containment, so a long or multi-day class shows up on every day it covers.
Page with `limit`/`offset` against the `total` the response reports. Ordering is stable (`startTime`, then id).
Every class the organization owns is in scope - member-only and private occurrences included, and those at a deactivated club - because the key holder is the operator, not a visitor. `locale` resolves `name` and `description`; the full `translations` blob is returned either way.
# List progression tracks
Source: https://docs.1club.ai/api-reference/classes/list-progression-tracks
/openapi-platform.json get /v1/platform/progression-tracks
Returns the progression tracks the organization uses - its own and the global templates it has enabled - each a ladder of ranks in one discipline (a belt system, a swim-stage scheme). Resolve a track here, then pass its `id` as `progressionTrackId` when creating or updating a class type to make that type's sessions count toward its ranks.
Each track lists the class types already counting toward it in `classTypeIds`. An empty list means every session counts; once any class type is linked, only sessions of the linked types do. Read-only: tracks and their levels are set up in the 1club admin dashboard. There is no paging: an organization has a handful of these. Carries the `classes` scopes rather than one of its own.
# Update a class
Source: https://docs.1club.ai/api-reference/classes/update-a-class
/openapi-platform.json patch /v1/platform/classes/{id}
Updates one occurrence, or this and all future occurrences when `scope=all_future`. A recurring class defaults to `this_instance`, so a series-wide edit must ask for it explicitly.
With `scope=all_future`, `recurrence` moves where the series ends - `ends` plus `endDate`, the same fields a create takes. Send it on its own: a body that also carries class fields is refused, so an end change never half-applies. Only the end: a `frequency`, `interval` or `daysOfWeek` is refused, and to change a cadence you delete the remaining series and create it again. A later end writes the new occurrences; an earlier one removes the occurrences past it, and is refused with 409 when any of those has an active booking or waitlist entry (cancel them first) - nothing a member holds is cancelled as a side effect. The occurrence named must be one the series keeps, and the end cannot fall before a session that has already run.
The response to an end change carries `series.occurrencesCreated` and `series.occurrencesRemoved`. As on a create, an occurrence at a club that is closed is skipped rather than failing the request, so compare the count with what you expected.
# Update a class type
Source: https://docs.1club.ai/api-reference/classes/update-a-class-type
/openapi-platform.json patch /v1/platform/class-types/{id}
Changes a class type. Send only what changes; an omitted field is left alone, `null` clears a nullable one, and at least one field is required.
Every field here applies to classes already scheduled as this type, not only to new ones: lowering `maxPartySize` does not evict anybody already booked, but it does cap the next booking, and changing `revenueAccountId` moves where future sales post without rewriting the ones already taken. A rename leaves `slug` alone - it is a stable key, so it moves only when you send one.
# Get club details by slug
Source: https://docs.1club.ai/api-reference/clubs/get-club-details-by-slug
/openapi-platform.json get /v1/platform/clubs/{slug}
Returns club details including amenities, rating, and images. The club must belong to the organization associated with the API token.
# Archive a contact
Source: https://docs.1club.ai/api-reference/contacts/archive-a-contact
/openapi-platform.json delete /v1/platform/contacts/{id}
Retires a contact. Nothing is destroyed: the contact is archived, which drops them out of the contacts list and out of every search, while their transaction history stays intact - so the response is the contact itself, now with `type` `archived`, rather than an empty body. Archiving also releases everything they hold on the forward schedule - active memberships, bookings they organized, and bookings they are only a player on - and none of that is restored by un-archiving, which is done in 1club.
To preview the impact, `GET /v1/platform/memberships?contactId={id}` lists the memberships that will be cancelled, and `GET /v1/platform/bookings?contactId={id}` the bookings that will be. Neither is an exhaustive preview, so treat them as indicative: the bookings list matches only bookings the contact **organized** - never the ones they are an additional player on, which are released too - and it defaults to a **7-day** window, so pass `from` and `to` (up to 93 days per request, consecutive windows beyond that) to see further out.
A release can fail on an individual membership or booking without blocking the archive - the contact is retired either way, so a stuck booking cannot leave it active. That answers **409 `ARCHIVE_INCOMPLETE`** (not a 200) with the unreleased items in `details.outstanding`, precisely so the result is never cached against an `Idempotency-Key`: **repeat the same request to reconcile.** On an already archived contact the release runs again instead of short-circuiting, which is what makes the retry finish the job. A repeat call after a 200 is a no-op.
Needs its own `contacts:delete` scope for that reason; `contacts:write` alone cannot retire a member. Two contacts are refused rather than archived, because each needs a decision the call cannot make: a **staff member or owner** who can reach the admin portal (archiving would revoke that login - remove their access in 1club first); an **instructor** whose profile still has upcoming classes or recurring templates (reassign those first); and a contact **paying part of a split bill** on a future booking (settle that split first, since taking someone off a bill moves money and archiving moves none). An ordinary member with a portal login is *not* refused - that is most of the people worth archiving.
# Create a contact
Source: https://docs.1club.ai/api-reference/contacts/create-a-contact
/openapi-platform.json post /v1/platform/contacts
Creates a contact in the organization. At least one of `email` or `phone` is required so the person stays reachable and findable. Creating a contact whose email already exists returns 409 (`code` CONFLICT), and so does one whose phone number already belongs to a contact with the same name (`code` CONTACT_EXISTS; a shared number under a different name - a household - is accepted). Both name the existing contact in `details.contactId`. Search first, then reference the existing id. A `phone` is stored in international form (`+359877762016`) when it is a valid number for the organization's country, and as given otherwise. Supports the `Idempotency-Key` header.
# Get a contact
Source: https://docs.1club.ai/api-reference/contacts/get-a-contact
/openapi-platform.json get /v1/platform/contacts/{id}
Returns a single contact by its 1club id. With `include=overview` it also returns the contact's overview as `overview` - what staff see on the contact's profile before serving them: the alerts (an Amount Due, a Wallet below its Required Deposit, gear overdue, a required document unsigned, a required profile field missing, access suspended, archived), the money (Amount Due, Wallet balance, deposit, billed and paid to date), activity counts and the last visit, and the contact details the record lacks.
Money needs `transactions:read` and gear needs `products:read` on top of `contacts:read`. Without them the rest is still returned, and `withheld` names what was left out and the scope it needs, so a missing alert is never mistaken for nothing to report.
# List and search contacts
Source: https://docs.1club.ai/api-reference/contacts/list-and-search-contacts
/openapi-platform.json get /v1/platform/contacts
Returns the organization's contacts ordered by name, excluding archived ones. Use `search` to match on name, email, or phone - matching is case-insensitive and cross-script, so `ivan` also matches `Иван`. Resolve a customer here and pass the returned `id` as `contactId` when creating a booking.
# Update a contact
Source: https://docs.1club.ai/api-reference/contacts/update-a-contact
/openapi-platform.json patch /v1/platform/contacts/{id}
Edits a contact's name and contact details. Send only what changes - `null` clears `lastName`, `email` or `phone`, and an omitted field is left alone. The display name is always recomputed from `firstName` and `lastName`, the phone is canonicalized, and a contact must keep at least one of `email` or `phone`, so an edit cannot leave the person unreachable. Clearing `lastName` stores an empty string rather than null, so it reads back as `""`.
Refused edits: an `email` already used by another contact in the organization (409); any change to the `email` of a contact whose address is also their 1club login (400) - that address is changed by the person themselves; and clearing a field the organization has marked required for staff (400, naming the `field`). An archived contact cannot be edited.
# Create content
Source: https://docs.1club.ai/api-reference/content/create-content
/openapi-platform.json post /v1/platform/content
Creates a post, FAQ, document, block or how-to guide.
Send the body as either `contentBlocks` (Editor.js blocks, what the admin editor writes) or `contentHtml` - not both. A `block` created here is what a website `block` section renders: reference it by the slug this returns.
The slug is derived from the title when omitted, and de-duplicated with a numeric suffix rather than rejected - read the returned `slug` rather than assuming the one you sent. Non-Latin titles transliterate ("Работно време" becomes `rabotno-vreme`), so a title in any language yields a usable URL.
To translate an item, create a second one with the **same slug** and a `language`. The two are one piece of content in two languages, and the public site serves whichever matches the visitor's locale.
# Delete content
Source: https://docs.1club.ai/api-reference/content/delete-content
/openapi-platform.json delete /v1/platform/content/{contentId}
Permanently deletes a content item. A website `block` section still referencing its slug will render nothing, and a `document` with signatures against it should be taken out of use with `status: draft` instead - deleting it takes the signed text with it.
# Get published content by slug
Source: https://docs.1club.ai/api-reference/content/get-published-content-by-slug
/openapi-platform.json get /v1/platform/content/{slug}
Returns a single published, publicly-visible content item by slug - the public view. Use the list endpoint with `status=all` to reach drafts.
# List content
Source: https://docs.1club.ai/api-reference/content/list-content
/openapi-platform.json get /v1/platform/content
Returns content for the organization. By default this is the public view: published items with `Public` visibility, the same thing a website visitor could see.
To manage content you need the rest of it - pass `status=all` for drafts and `visibility=all` for member-only and private items. Both require the `content:write` scope, since they expose material the organization has not published.
Translations are separate items sharing one slug, each with its own `language` (see `POST /v1/platform/content`). `locale` *resolves* them - it returns one item per slug, the best match for that locale. To see every variant instead, omit `locale` and read each item's `language`; to list one language only, pass `language` (`language=none` for the untagged base items).
# Update content
Source: https://docs.1club.ai/api-reference/content/update-content
/openapi-platform.json patch /v1/platform/content/{contentId}
Updates a content item by id - the numeric `id` from the list endpoint, not the slug. Only the fields you send change.
`tags` and `images` replace their arrays wholesale. Switching `status` to `draft` takes the item off the public site without deleting it.
# Create a form
Source: https://docs.1club.ai/api-reference/forms/create-a-form
/openapi-platform.json post /v1/platform/forms
Creates a form. It is **live immediately** at the returned `publicUrl`: there is no draft, so confirm the content with the club before creating one. `publicUrl` is null when the organization has neither a member portal nor a live website: the form is created, but nothing can open it until one is switched on.
`createsAccount` is required and permanent. `true` makes a sign-up form, which registers whoever completes it and sells what it offers: completing one with a plan creates a real membership, or asks for payment when the plan is paid up front. Only active, publicly visible, non-season plans and active, publicly visible products can be offered; anything else is a 400 naming each refused id and why. `false` makes a fill-in form, which offers nothing, collects answers to its questions (set them with PUT /v1/platform/forms/{id}/questions), and creates a lead for a visitor it does not know.
`slug` is optional and derived from the name when left out. A slug you name that is already taken is a 409 rather than a quiet `-2`. Read the `slug` and `publicUrl` from the response. Supports the `Idempotency-Key` header.
Needs the `forms:write` scope, which is new. An API key holding the `*` wildcard picks it up automatically; an OAuth connection made before it shipped has to be re-authorized.
# Get a form
Source: https://docs.1club.ai/api-reference/forms/get-a-form
/openapi-platform.json get /v1/platform/forms/{id}
One form with its questions, in order. Keep each question's `id`: to change the questions you resend every one you are keeping by id. `submissionCount` is how many people have completed it; the answers themselves are not served by this API.
# List forms
Source: https://docs.1club.ai/api-reference/forms/list-forms
/openapi-platform.json get /v1/platform/forms
The organization's forms, newest first. A form with `createsAccount: true` is a sign-up form: it registers whoever completes it and can sell plans and products. One with `createsAccount: false` is a fill-in form that only collects answers. `publicUrl` is where each one is filled in, and every form is live there. Questions are not included; read a single form for them.
# Set a fill-in form's questions
Source: https://docs.1club.ai/api-reference/forms/set-a-fill-in-forms-questions
/openapi-platform.json put /v1/platform/forms/{id}/questions
Sets the questions of a fill-in form (`createsAccount: false`), in the order given. The change is live immediately.
Resend every question you are keeping **with its `id`**, and list the ones to delete in `removeQuestionIds`. A question is never deleted by being left out: a list that is missing an existing question is a 400 (`FORM_QUESTION_OMITTED`) naming it. The ids matter because answers are stored against them, so a question resent without its id is a new question and the answers already given stop being linked to it. Deleting a question keeps past submissions readable with the wording they were asked under.
A sign-up form has no questions of its own (`FORM_QUESTIONS_FILL_IN_ONLY`).
# Update a form
Source: https://docs.1club.ai/api-reference/forms/update-a-form
/openapi-platform.json patch /v1/platform/forms/{id}
Edits a form. Send only what changes; an omitted field is left alone. The change is live immediately.
`slug` and `createsAccount` cannot be changed: the slug is the address people were given, and the kind decides what the form is. Offerings on a sign-up form are held to the same bar as on create, and a fill-in form offers nothing. There is no delete: deleting a form deletes every submission made through it, so it stays in the 1club admin portal.
# Create an instructor
Source: https://docs.1club.ai/api-reference/instructors/create-an-instructor
/openapi-platform.json post /v1/platform/instructors
Makes a bookable instructor (a coach) out of an existing contact. Resolve or create that contact first with the contacts resource, then pass its id as `contactId` - an instructor profile hangs off a person the organization already knows, and the name on it is theirs.
**This grants no access to the staff portal.** The instructor gets no login, no role, no `organization_users` row and no invitation email; they are inventory that can be booked and paid, not a user. `sendInvitation`, `organizationUserId`, `role` and `color` are refused rather than ignored, so a payload copied from the admin API fails loudly. Invite a coach to the portal from the 1club admin portal instead.
`hourlyRate` is what a **member pays** for an hour of one-to-one time, not what the club pays the coach - see POST /v1/platform/pay-rate-policies for the latter.
One instructor per contact. A contact that already has one is a 409 naming the existing profile, including when that profile is inactive: bringing a retired coach back is a PATCH, so whoever does it can see the rates and hours they are restoring. Supports the `Idempotency-Key` header.
Needs the `instructors:write` scope, which is new. An API key holding the `*` wildcard picks it up automatically; an OAuth connection does not, because the wildcard is expanded at consent time - that connection has to be re-authorized before this endpoint is reachable.
# Create an instructor type
Source: https://docs.1club.ai/api-reference/instructors/create-an-instructor-type
/openapi-platform.json post /v1/platform/instructor-types
Adds a kind of coach. List the existing types first - most organizations already have one for each sport their clubs offer, and this endpoint will happily create a near-duplicate.
`slug` is optional and derived from the name when omitted, suffixed if that is taken; an explicit slug that clashes is a 409. Supports the `Idempotency-Key` header.
# Get an instructor
Source: https://docs.1club.ai/api-reference/instructors/get-an-instructor
/openapi-platform.json get /v1/platform/instructors/{id}
Returns a single instructor by its 1club id. Retired instructors resolve here too - check `isActive` before offering one, since the list hides them by default.
# Get an instructor type
Source: https://docs.1club.ai/api-reference/instructors/get-an-instructor-type
/openapi-platform.json get /v1/platform/instructor-types/{id}
Returns one instructor type and how many coaches are filed under it, active and retired.
# List and search instructors
Source: https://docs.1club.ai/api-reference/instructors/list-and-search-instructors
/openapi-platform.json get /v1/platform/instructors
Returns the organization's active instructors. Use `search` to match on name - case-insensitive and cross-script, so `maya` also matches `Мая`. Resolve a coach here and pass the returned `id` as `instructorId` when creating a booking. `bookability` says who may book them: `Admin_only` means the club reserves those bookings to its own staff, and an API key acts for the organization itself, so it may still create one. Create one with POST /v1/platform/instructors - which makes a bookable coach and nothing more: no staff-portal login comes with it.
Only active instructors are listed by default. One retired with `isActive: false` is hidden here until `includeInactive=true` asks for it, and always resolves by id - so the caller that just retired a coach can read them back and undo it. Every row reports `isActive` and `visibility`.
# List instructor types
Source: https://docs.1club.ai/api-reference/instructors/list-instructor-types
/openapi-platform.json get /v1/platform/instructor-types
Returns the organization's instructor types - the kinds of coach it has, such as "Padel coach" or "Personal trainer". Resolve one here to fill in `instructorTypeId` when creating or updating an instructor.
An organization is seeded with a set derived from the sports its clubs offer, so read this list before adding to it: a second "Tennis coach" under a slightly different name splits a roster that should be one group. There is no paging - an organization has a handful of these.
# Update an instructor
Source: https://docs.1club.ai/api-reference/instructors/update-an-instructor
/openapi-platform.json patch /v1/platform/instructors/{id}
Edits an instructor's bookable profile - their sports, their member-facing rate, their availability, who may book them. Send only what changes; an omitted field is left alone.
Retire a coach with `isActive: false` and bring them back with `isActive: true`; there is no delete, because an instructor carries bookings, classes and pay history. Two invariants are applied on the way through, so read the response rather than assuming the payload landed verbatim: deactivating always retires `bookability` to `Admin_only`, and `bookability` never outruns `visibility` - making someone `Private` pulls a `Public` bookability down with it.
`contactId` is not editable: the profile is bound to one person, and re-pointing it would silently transfer their bookings and their pay. The staff-portal fields are refused here for the same reasons as on the create.
# Update an instructor type
Source: https://docs.1club.ai/api-reference/instructors/update-an-instructor-type
/openapi-platform.json patch /v1/platform/instructor-types/{id}
Changes an instructor type. Send only what changes; an omitted field is left alone, `null` clears a nullable one, and at least one field is required. A rename leaves `slug` alone - it is a stable key, so it moves only when you send one.
`maxConcurrentBookings` here is the shape of the type. What a booking is actually checked against is the instructor's own value, so changing this does not retroactively widen or narrow the coaches already on it.
# API Reference
Source: https://docs.1club.ai/api-reference/introduction
Integrate with the 1Club platform to access clubs, classes, content, and transactions.
The 1Club Platform API lets you programmatically access and manage your organization's data - clubs, classes, content, and transactions - from your own websites, integrations, and back-office scripts.
## Base URL
All API requests are made to:
```
https://api.1club.ai
```
## Official API contract
This documentation is the official source of truth for the 1Club Platform API.
If an integration relies on undocumented endpoints, fields, response shapes, or internal behavior outside this spec, we can't guarantee backward compatibility.
Build against what's documented here to stay stable as the platform evolves.
## Authentication
All requests require a customer API key passed as a Bearer token:
```
Authorization: Bearer 1club_sk_live_...
```
The key is tied to your organization, and all responses are scoped to your organization's data.
### Creating an API token
API tokens are created from the admin dashboard at [admin.1club.ai](https://admin.1club.ai).
1. Sign in to the admin dashboard
2. Open **Settings** > **Organization** > **APIs & webhooks**
3. Click **Create Token**
4. Fill in the token details:
* **Name** - a label to identify where this token is used (e.g. "Website Integration")
* **Description** - optional note on what the token is used for
* **Scopes** - pick the resources the token can access, and whether each is **Read** or **Write**
* **Expires** - set an expiry date, or leave empty for a token that never expires
5. Click **Save**
6. **Copy the token immediately** - it is shown only once and cannot be retrieved later
Creating and revoking API tokens requires the **settings.manage.organization**
permission. Admins and users with full settings management can access this
page; managers and other roles cannot.
For the full guide - including scope selection, expiry options, revocation, and security best practices - see the [API Tokens](/settings/api-tokens) page.
### Token scopes
Each token is granted access to specific resources with either **Read** or **Write** permission:
* **Read** - allows fetching data (GET requests)
* **Write** - allows creating and updating data (POST/PUT/PATCH requests)
Grant only the scopes the integration actually needs. The Platform API currently supports these scopes:
* `classes:read` - list and read classes
* `content:read` - list and read published content
* `clubs:read` - list your clubs and look one up by slug
* `transactions:read` - list and read billing transactions
* `transactions:write` - record a settlement (revenue collected outside 1club, reported as one sum for a gym) and refunds of it
* `areas:read` - list areas and read their availability
* `bookings:read` - read a booking by external id
* `bookings:write` - create and cancel bookings
A transaction in this API is the record the 1Club admin and member apps call a
**Charge**: what a payer owes for one thing, such as a booking, a membership
period, or a product. `/v1/platform/transactions` returns the same records
staff see under **Sales** > **Finance** > **Charges**. See
[Charges](/sales/charges).
To sync bookings from a partner booking platform, see [Channel integration](/api-reference/channel-integration/overview).
### Revoking a token
If a token is leaked or no longer needed, revoke it from **Settings** > **Organization** > **APIs & webhooks**. Revocation is immediate - any integration using that token will stop working on its next request.
## Quick start
Fetch the list of clubs:
```bash theme={null}
curl https://api.1club.ai/v1/platform/clubs \
-H "Authorization: Bearer 1club_sk_live_..."
```
List recent paid transactions (the last 24 hours by default):
```bash theme={null}
curl "https://api.1club.ai/v1/platform/transactions?paymentStatus=paid" \
-H "Authorization: Bearer 1club_sk_live_..."
```
Browse class schedules:
```bash theme={null}
curl "https://api.1club.ai/v1/platform/classes?startDate=2026-05-01" \
-H "Authorization: Bearer 1club_sk_live_..."
```
## Rate limiting
* **100 requests per minute** per organization
* When exceeded, the API returns `429 Too Many Requests` with a `Retry-After` header
* Rate limit headers are included in every response:
* `X-RateLimit-Limit` - max requests per window
* `X-RateLimit-Remaining` - requests remaining
* `X-RateLimit-Reset` - seconds until the window resets
## Errors
| Status | Meaning |
| - | - |
| `400` | Invalid request parameters |
| `401` | Missing or invalid API key |
| `403` | Token is missing the required scope for this endpoint |
| `404` | Resource not found (or doesn't belong to your organization) |
| `429` | Rate limit exceeded |
| `500` | Internal server error |
## Security best practices
* **Never commit tokens to source control.** Store them in environment variables or a secrets manager.
* **Use the minimum required scopes.** Don't grant write access if only read is needed.
* **Set an expiry** on tokens used for short-term scripts or testing.
* **Rotate tokens regularly** and revoke any that are no longer in use.
* **Never share tokens** in emails, chat messages, or public channels.
## Using this API from an AI assistant
The same Platform API is exposed over the Model Context Protocol at `https://mcp.1club.ai/admin`. The normal way to connect a remote MCP client is 1Club OAuth: you sign in on 1Club's authorization page and nothing is pasted into the assistant. An API key created above is still accepted on the same endpoint when the client sends it as `Authorization: Bearer 1club_sk_live_...`, which is how developer-managed clients that cannot complete an interactive sign-in connect. The MCP tools are thin wrappers over these endpoints, with the same scopes, rate limits, and error shapes. See [Connect an assistant](/mcp/connect).
See the [MCP overview](/mcp/overview) for the two servers 1Club runs, and [Connect an assistant](/mcp/connect) for setup.
## Need help?
Contact us at [support@1club.ai](mailto:support@1club.ai) for API access questions or integration support.
# Get an invoice
Source: https://docs.1club.ai/api-reference/invoices/get-an-invoice
/openapi-platform.json get /v1/platform/invoices/{id}
One fiscal invoice with its line items and the payments recorded against it.
# List invoices
Source: https://docs.1club.ai/api-reference/invoices/list-invoices
/openapi-platform.json get /v1/platform/invoices
Lists the organization's fiscal invoices over a range of calendar days, newest first. The days are read exactly as `GET /invoices/summary` reads them, so for the same range and filters this returns the invoices the summary counted. Pro-formas are never included.
# Summarize what was invoiced
Source: https://docs.1club.ai/api-reference/invoices/summarize-what-was-invoiced
/openapi-platform.json get /v1/platform/invoices/summary
Totals of the organization's fiscal invoices over a range of calendar days on the organization's own clock (`timezone` in the response). Both bounds are inclusive `YYYY-MM-DD` dates; with neither, the range is today, and with one, it is that single day. A range may span at most 93 days.
An invoice belongs to the day its invoice date names: a date entered without a time files on that date, and an invoice raised from a sale files on the organization's local day. Pro-formas are never included.
`invoiced` excludes `void` and `cancelled` invoices; `byStatus` lists every status, those included. Every amount is grouped by currency - totals in different currencies are never added together. `overdue` counts unpaid invoices in the range whose due date has passed. `byBillingEntity` splits `invoiced` by the company that issued each invoice.
# Delete a media item
Source: https://docs.1club.ai/api-reference/media/delete-a-media-item
/openapi-platform.json delete /v1/platform/media/{mediaId}
Removes the item from the library **and deletes the file from Cloudinary**. This cannot be undone, and any website section or content item still referencing the URL will render a broken image - check before deleting.
Needs its own `media:delete` scope for that reason; `media:write` alone is not enough.
# Get one media item
Source: https://docs.1club.ai/api-reference/media/get-one-media-item
/openapi-platform.json get /v1/platform/media/{mediaId}
# List media
Source: https://docs.1club.ai/api-reference/media/list-media
/openapi-platform.json get /v1/platform/media
The organization's media library, newest first. Pass `search` to match by name or alt text, which is how you find the URL for an image to put on a website section - never invent an image URL.
A `search` returns the best matches ranked by relevance rather than a page of results, so `offset` does not apply to it and `total` reports how many matched.
# Register a file in the media library
Source: https://docs.1club.ai/api-reference/media/register-a-file-in-the-media-library
/openapi-platform.json post /v1/platform/media
Adds an already-hosted file to the library so website sections and content can reference it. The API does not accept file uploads - upload to your image host first (the admin editor uses Cloudinary) and register the resulting URL here.
Idempotent per URL: registering one the organization already has updates whatever metadata you send and returns the existing item, with `created: false`.
# Update media metadata
Source: https://docs.1club.ai/api-reference/media/update-media-metadata
/openapi-platform.json patch /v1/platform/media/{mediaId}
Changes the name, alt text or caption. The file itself and its URL are immutable - register a new item to replace a file.
Alt text is worth filling in: it is what screen readers announce, and it is searched alongside the name when finding an image to place.
# Create a membership
Source: https://docs.1club.ai/api-reference/memberships/create-a-membership
/openapi-platform.json post /v1/platform/memberships
Puts a customer on a plan. The plan is snapshotted onto the membership exactly as it is when sold in 1club - club, session allowance, coverage, billing frequency and renewal window - so the membership draws down booking allowance identically.
`paid` records a payment against the External / API method with no gateway call; `unpaid` leaves the membership's transactions outstanding. The total charged is the period amount plus any joining fee, both taken from the plan unless overridden. Collection and refunds stay with you.
When nothing is owed at all - a free plan with no joining fee, or an `amount` of 0 on such a plan - the membership is created active straight away rather than awaiting a payment that will never come.
Supports the `Idempotency-Key` header.
# Get a membership
Source: https://docs.1club.ai/api-reference/memberships/get-a-membership
/openapi-platform.json get /v1/platform/memberships/{id}
Returns a single membership by its 1club id, including its remaining session allowance.
# List memberships
Source: https://docs.1club.ai/api-reference/memberships/list-memberships
/openapi-platform.json get /v1/platform/memberships
Returns the organization's memberships, most recently started first. Filter by customer, plan, or status to find the one you need.
# Update a membership
Source: https://docs.1club.ai/api-reference/memberships/update-a-membership
/openapi-platform.json patch /v1/platform/memberships/{id}
Changes whether the membership renews, when it ends, and whether it is active, paused, or cancelled. Cancelling stamps the cancellation date, as cancelling in 1club does.
Changing the plan is not supported: that is a priced change needing proration, invoicing and possibly a refund. Move the customer to another plan in 1club instead.
# Create a pay rate policy
Source: https://docs.1club.ai/api-reference/pay-rate-policies/create-a-pay-rate-policy
/openapi-platform.json post /v1/platform/pay-rate-policies
Creates a rule for what the club pays its coaches. `earnings.kind` picks what the policy is about and which rates it accepts: `individual` for one-to-one sessions (`perSession`, `sessionRevenuePercentage`) and `class` for group classes (`perClass`, `perBooking`, `classRevenuePercentage`). At least one rate must be non-zero, and an individual policy cannot be scoped by class type.
`instructorIds` must be sent. An empty list makes this the organization's default policy - it then applies to every coach who has no policy of their own, and creating it re-resolves every coach's pay - so that has to be asked for, never fallen into by omitting the field. `classTypeIds` and `entranceScopes` may be omitted; empty covers every class type or every way in.
Two policies may not claim the same coach for overlapping work. That is a 409 with code `PAY_RATE_POLICY_COLLISION`, carrying the coaches involved and the policy each one is already on, so the caller can say which assignment to undo. Supports the `Idempotency-Key` header.
Needs `instructors:write` - the same scope that creates a coach, because both are "how this organization staffs and pays its sessions". An OAuth connection made before that scope shipped has to be re-authorized.
# Get a pay rate policy
Source: https://docs.1club.ai/api-reference/pay-rate-policies/get-a-pay-rate-policy
/openapi-platform.json get /v1/platform/pay-rate-policies/{id}
Returns one pay rate policy, with the coaches, class types and entrance routes it covers.
# List pay rate policies
Source: https://docs.1club.ai/api-reference/pay-rate-policies/list-pay-rate-policies
/openapi-platform.json get /v1/platform/pay-rate-policies
Returns the organization's pay rate policies - the rules for what the club pays its coaches. This is not an instructor's `hourlyRate`, which is what a member pays for an hour of their time; the two travel in opposite directions.
A policy earns a coach either a flat amount per unit of work (a private session, a class, an attendee) or a percentage of what that work took, and covers a slice of the schedule described by three independent filters. Each one means **everything** when it is empty: no `instructors` named makes it the organization's default policy, applying to every coach who has none of their own; no `classTypes` covers every kind of class; no `entranceScopes` covers every way an attendee got in.
Filtering by `instructorId` returns the policies that actually decide that coach's pay - the ones naming them, plus the default policies that would otherwise apply.
There is no paging: an organization has a handful of these. The pay ledger - what a coach has actually accrued - is not on this API.
# Put instructors on a pay rate policy
Source: https://docs.1club.ai/api-reference/pay-rate-policies/put-instructors-on-a-pay-rate-policy
/openapi-platform.json post /v1/platform/pay-rate-policies/{id}/instructors
Adds coaches to a policy, leaving the ones already on it alone - the additive counterpart of PATCH, which replaces the whole list. Adding a coach who is already covered by another policy for the same work is a 409 with code `PAY_RATE_POLICY_COLLISION`.
Note that adding the first coach to a policy that named none narrows it: an empty list means "every coach without a policy of their own", so it stops being the organization's default.
# Take an instructor off a pay rate policy
Source: https://docs.1club.ai/api-reference/pay-rate-policies/take-an-instructor-off-a-pay-rate-policy
/openapi-platform.json delete /v1/platform/pay-rate-policies/{id}/instructors/{instructorId}
Removes one coach from a policy. Their not-yet-finalized pay re-resolves to whichever policy covers them next, or to nothing if none does; days already closed stay frozen.
Removing the last named coach does not disable the policy - it turns it into the organization's default, applying to every coach without one of their own. The response shows the policy as it now stands, which is where that is visible.
# Update a pay rate policy
Source: https://docs.1club.ai/api-reference/pay-rate-policies/update-a-pay-rate-policy
/openapi-platform.json patch /v1/platform/pay-rate-policies/{id}
Edits a pay rate policy. Send only what changes; an omitted field is left alone. `instructorIds`, `classTypeIds` and `entranceScopes` each **replace** the whole list when sent - there is no element-wise merge for a set, so read the policy first and send the list you want.
`earnings.kind` cannot change once the policy exists: an individual policy and a class policy earn against different units of work, so switching one would silently re-price everything it covers. Create the other kind instead. Sending `classTypeIds` to an individual policy is refused for the same reason it is on create. Both are 400s.
A change that puts a coach on two overlapping policies is a 409 with code `PAY_RATE_POLICY_COLLISION`. Changing rates re-resolves the coach's not-yet-finalized pay from today onward; days already closed stay frozen.
# Create a membership plan
Source: https://docs.1club.ai/api-reference/plans/create-a-membership-plan
/openapi-platform.json post /v1/platform/plans
Creates a membership plan for the organization. Supports the `Idempotency-Key` header.
# Create a membership plan category
Source: https://docs.1club.ai/api-reference/plans/create-a-membership-plan-category
/openapi-platform.json post /v1/platform/plan-categories
Adds a membership plan category. Omit `sortOrder` and it goes to the end of the list, which is what you want unless you are rebuilding the whole ordering. Supports the `Idempotency-Key` header.
# Delete a membership plan
Source: https://docs.1club.ai/api-reference/plans/delete-a-membership-plan
/openapi-platform.json delete /v1/platform/plans/{id}
Delete a plan with no dependent records. Set `isActive` to false to retire a plan that is already in use - a retired plan is still readable by id, and `includeInactive=true` brings it back into the list.
# Get a membership plan
Source: https://docs.1club.ai/api-reference/plans/get-a-membership-plan
/openapi-platform.json get /v1/platform/plans/{id}
Returns a single plan, including what a membership on it costs and what it grants. Retired plans resolve here too - hiding them is the list's job, and a caller that just retired a plan still needs to read it back or undo it.
# Get a membership plan category
Source: https://docs.1club.ai/api-reference/plans/get-a-membership-plan-category
/openapi-platform.json get /v1/platform/plan-categories/{id}
Returns a single plan category and how many membership plans sit in it.
# List and search membership plans
Source: https://docs.1club.ai/api-reference/plans/list-and-search-membership-plans
/openapi-platform.json get /v1/platform/plans
Returns the organization's membership plans. Resolve a plan here, then pass its `planId` to POST /v1/platform/memberships - the plan carries its own price, joining fee, billing frequency and session allowance, so the sale does not restate any of it.
Only plans the gym still sells are returned by default - a retired plan is not something an integration should surface or sell. Pass `includeInactive=true` to see retired plans as well, which is how you find a plan again after retiring it. Use the write endpoints below to create, update, retire, or delete plans.
# List membership plan categories
Source: https://docs.1club.ai/api-reference/plans/list-membership-plan-categories
/openapi-platform.json get /v1/platform/plan-categories
Returns every membership plan category in the organization, in display order - the headings on a price list and the sections of a plans page. Resolve a category here, then pass its `categoryId` as `planCategoryId` when creating or updating a plan.
There is no paging: an organization has a handful of these, and a caller composing a price list wants all of them. Carries the `plans` scopes rather than one of its own - nobody grants "may edit categories but not plans".
# Update a membership plan
Source: https://docs.1club.ai/api-reference/plans/update-a-membership-plan
/openapi-platform.json patch /v1/platform/plans/{id}
Updates a membership plan. Send only what changes; an omitted field is left alone.
A **season plan** can be reached here even though GET /v1/platform/plans never lists one, because an id resolves by GET /v1/platform/plans/{id}. Those carry rules an ordinary plan does not: a season plan must bill `one_time`, belong to the season's own club, and count sessions in hours. An edit that would break one is refused with a 400 naming the rule (`SEASON_PLAN_BILLING_FREQUENCY`, `SEASON_PLAN_CLUB_MISMATCH`, `SEASON_PLAN_SESSION_UNIT`, …) rather than saved - a mis-set season plan does not error later, it sells a season pass with the wrong renewal behaviour or at the wrong venue. Renaming one, or retiring it with `isActive`, is unaffected.
# Update a membership plan category
Source: https://docs.1club.ai/api-reference/plans/update-a-membership-plan-category
/openapi-platform.json patch /v1/platform/plan-categories/{id}
Renames a category or moves it in the display order. The plans in it are unaffected.
# Create a product
Source: https://docs.1club.ai/api-reference/products/create-a-product
/openapi-platform.json post /v1/platform/products
Adds a product to the catalogue. `name` and `price` are all that is required; everything else refines how it behaves at the till and on customer-facing surfaces.
`clubId`, `categoryId`, `brandId`, `taxRateId` and `revenueAccountId` must reference rows in this organization - a `400` names the first one that does not. A `barcode` **or `sku`** already claimed by another product, package, plan or access card is a `409`: both are scannable at the till, so a scanned code has to mean exactly one thing.
Creating a product does not give it stock - putting units on the shelf is done in the admin portal. Supports the `Idempotency-Key` header.
# Create a product category
Source: https://docs.1club.ai/api-reference/products/create-a-product-category
/openapi-platform.json post /v1/platform/product-categories
Adds a product category. Omit `sortOrder` and it goes to the end of the list, which is what you want unless you are rebuilding the whole ordering. Supports the `Idempotency-Key` header.
# Get a product
Source: https://docs.1club.ai/api-reference/products/get-a-product
/openapi-platform.json get /v1/platform/products/{id}
Returns a single product, including its price, cost price, category, and stock-keeping codes.
# Get a product category
Source: https://docs.1club.ai/api-reference/products/get-a-product-category
/openapi-platform.json get /v1/platform/product-categories/{id}
Returns a single product category and how many products sit in it.
# Get how many are left to sell
Source: https://docs.1club.ai/api-reference/products/get-how-many-are-left-to-sell
/openapi-platform.json get /v1/platform/products/{id}/availability
Returns the number of units of a product that can be sold right now at each club.
This is the only view of stock on this API - receiving deliveries, corrections and the ledger behind them stay in the admin portal. It is also not simply the shelf count: a product assembled from a recipe has no shelf, and its figure is the limit its scarcest ingredient imposes, floored to whole units.
`available: null` means the product is not stock-tracked at that club, so the till will sell it freely - it does not mean the product has run out.
# List and search products
Source: https://docs.1club.ai/api-reference/products/list-and-search-products
/openapi-platform.json get /v1/platform/products
Returns the organization's products - everything it sells over the counter, from drinks to racket grips. `search` matches name and description as a substring, and `sku` and `barcode` exactly - a partial code matches nothing, since a scan code is only useful whole.
Unlike plans, deactivated products are returned too: an integration managing a catalogue has to be able to find what it switched off in order to reprice or restore it. Filter with `isActive=true` for what is currently on sale.
# List product categories
Source: https://docs.1club.ai/api-reference/products/list-product-categories
/openapi-platform.json get /v1/platform/product-categories
Returns every product category in the organization, in display order - the tabs on the till and the sections of a shop page. Resolve a category here, then pass its `categoryId` when creating a product or filtering the catalogue.
There is no paging: an organization has a handful of these, and a caller composing a menu wants all of them.
# Update a product
Source: https://docs.1club.ai/api-reference/products/update-a-product
/openapi-platform.json patch /v1/platform/products/{id}
Changes the fields you send and leaves the rest alone - nothing is required. Send `null` to clear a nullable field (`categoryId`, `barcode`, `costPrice`, ...); omit it to keep what is there. `images` and `features` are arrays and replace wholesale, so read the product first if you are adding to one.
A `barcode` or `sku` is only checked for collisions when you actually send it, so re-saving a product without touching its codes never conflicts with itself.
Set `isActive: false` to retire a product rather than deleting it - that keeps its sales history intact and takes it off the till.
# Update a product category
Source: https://docs.1club.ai/api-reference/products/update-a-product-category
/openapi-platform.json patch /v1/platform/product-categories/{id}
Renames a category or moves it in the display order. Products in it are unaffected.
# Revenue report for a period
Source: https://docs.1club.ai/api-reference/reports/revenue-report-for-a-period
/openapi-platform.json get /v1/platform/reports/revenue
What the organization earned over a period, and from what, where and how it compares - in one request rather than a walk of the transactions list.
Revenue is recognised ex-tax on the day of the sale, on the organization's own clock, net of refunds and of anything Wallet credit paid for. `collected` is the part of it already paid, on the same basis, so `uncollected` is what the period sold that has not been paid yet. These are the figures of the admin's daily revenue chart for the same days.
The period is compared with the same number of days just before it (`compare=previous_period`, the default) or the same dates a year earlier (`previous_year`); every breakdown row then carries its revenue in that period and the change, and `movers` lists the streams, clubs and items that moved most. Breakdowns: by revenue stream (bookings, memberships, passes, drop-ins, products, other), by club (unless `clubId` narrows the report to one), by the payment method that collected the money, and the top items sold (a class or court, a plan, a product). `trend` buckets the period by day, week (starting Monday) or month - by default a month or less by day, up to six months by week, anything longer by month.
Amounts are in the organization's `currency`. A period may span at most 366 days. Reports have their own limit of 20 requests a minute, on top of the platform's.
# Create a revenue account
Source: https://docs.1club.ai/api-reference/revenue-accounts/create-a-revenue-account
/openapi-platform.json post /v1/platform/revenue-accounts
Adds an account to the chart of accounts. `code` is what the club's bookkeeper reconciles against and must be free within the organization - a clash is a 409, not a second account under the same code.
Sending `isDefault: true` moves the default off whichever account currently holds it, in the same transaction and behind a per-organization lock, so concurrent promotions cannot both win. The default is where a sale posts when the item names no account, resolved at the time of the sale rather than frozen onto the item, so moving it changes where future unattributed sales land and leaves the ones already taken alone.
Two things about the default are decided for you. An organization whose chart has no default yet gets one from this call whatever `isDefault` says, since creating an account is the only way to give it one - so the first account in an empty chart is always the default, and is therefore always created active. And a default can never be inactive, so `isActive: false` is refused for any request that would land one, whether `isDefault` was sent or inferred.
Requires `revenue-accounts:write`. Supports the `Idempotency-Key` header.
# Get a revenue account
Source: https://docs.1club.ai/api-reference/revenue-accounts/get-a-revenue-account
/openapi-platform.json get /v1/platform/revenue-accounts/{id}
Returns one account from the chart of accounts, retired ones included. Readable with any scope that carries an account id, exactly as the list is - including `revenue-accounts:write`, so a key that maintains the chart can read back what it wrote.
# List revenue accounts
Source: https://docs.1club.ai/api-reference/revenue-accounts/list-revenue-accounts
/openapi-platform.json get /v1/platform/revenue-accounts
Returns the organization's chart of accounts, ordered by code. This is how a `revenueAccountId` is resolved before creating or updating a membership plan or a product - a sale of that item then posts to the account named here.
Adding or renaming an account changes how every future sale is reported and what the club's accountant reconciles against, so the writes are gated on `revenue-accounts:write`, which nothing else grants. There is no delete: every historical row pointing at an account would be stranded, so an account is taken out of use with `isActive: false`.
Retired accounts are included, flagged with `isActive: false`: an existing plan or product may still point at one, so the id has to stay explicable. Do not attach one to something new.
Readable with any scope that carries a revenue account id: the `plans`, `products`, `classes`, `areas` and `instructors` scopes (read *and* write - they are granted independently), plus `transactions:read` and `revenue-accounts:write`. Every one of those resources either reports `revenueAccountId` or accepts it, so requiring one scope in particular would leave a key able to set an account it cannot look up. There is no `revenue-accounts:read`.
Leaving `revenueAccountId` unset is a valid choice: the sale then posts to whichever account the organization has marked `isDefault`, resolved at the time of the sale rather than frozen onto the item.
# Update a revenue account
Source: https://docs.1club.ai/api-reference/revenue-accounts/update-a-revenue-account
/openapi-platform.json patch /v1/platform/revenue-accounts/{id}
Changes an account. Send only what changes; an omitted field is left alone, and at least one field is required.
Three things this will refuse. A `code` another account already uses is a 409. `isDefault: false` on the *last* account holding the default is a 400: an organization with no default has nowhere to post a sale that names no account. (An organization carrying more than one default - possible from before these rules, or from a clone - can have the extras cleared, which is how that state is repaired.) And anything that would leave the default inactive - retiring the current default, or promoting a retired account - is a 400, because the readers that resolve the default match on `isDefault` alone and would go on posting sales to an account this API calls retired. Move the default onto another account first, or reactivate and promote in the same call.
Retiring an account (`isActive: false`) is otherwise how one is taken out of use. Everything already pointing at it keeps pointing at it - the flag is what tells a caller not to attach it to something new.
Requires `revenue-accounts:write`.
# Get a transaction by ID
Source: https://docs.1club.ai/api-reference/transactions/get-a-transaction-by-id
/openapi-platform.json get /v1/platform/transactions/{id}
Returns a single transaction. The transaction must belong to the organization associated with the API token.
# List transactions for the organization
Source: https://docs.1club.ai/api-reference/transactions/list-transactions-for-the-organization
/openapi-platform.json get /v1/platform/transactions
Returns transactions for the organization associated with the API token, filtered by transaction creation time and ordered by `createdAt` descending, then by id. Each bound is an ISO date-time, or a bare `YYYY-MM-DD` read as midnight UTC. `startDate` is inclusive and `endDate` is exclusive. When both are omitted, the window defaults to the last 24 hours (`endDate = now`, `startDate = now - 24h`); when only one is given, the other is derived from that same 24-hour span.
A single request may span at most 93 days. Cover a longer range in consecutive windows, passing the previous window's `endDate` as the next one's `startDate` - because the upper bound is exclusive, that chains without counting a boundary transaction twice.
Filtering by `contactId`, `bookingId` or `membershipId` lists that record's transactions. With one of those and no `startDate`/`endDate`, there is no window at all: every transaction of the contact, booking or membership comes back, newest first. Send a bound to window it as usual.
Note that this endpoint returns a bare array with no total - a full page (`limit` rows) means there are probably more, so keep advancing `offset` until a short page comes back.
Ordering is total (`createdAt` descending, then id), so `offset` paging is well defined for a fixed set of rows. It is not stable against concurrent writes: rows are ordered newest first, so a transaction created part-way through a walk lands on the first page and shifts every later row down by one, which can push an unread row past an `offset` already passed. Pass an explicit `endDate` in the past rather than relying on the `now` default - anything written after it falls outside the window and cannot shift the pages underneath you.
# Record a settlement
Source: https://docs.1club.ai/api-reference/transactions/record-a-settlement
/openapi-platform.json post /v1/platform/transactions
Records revenue you collected outside 1club as one sum for a club - for example a period's takings for bookings you recorded at 0. It is a paid transaction of type `settlement` on the club, settled against your key's payment method, and counts in the club's revenue under the revenue account you name. It is not split across bookings or customers.
`amount` is what customers paid, tax included; the tax is taken out at the revenue account's rate. A `payment.reference` your key already recorded is refused with `409 DUPLICATE_REFERENCE` (`details.transactionId` names the existing one). No receipt is sent. Supports the `Idempotency-Key` header.
# Refund a settlement
Source: https://docs.1club.ai/api-reference/transactions/refund-a-settlement
/openapi-platform.json post /v1/platform/transactions/{id}/refund
Records money given back out of a settlement your key recorded, reducing the club's revenue by it. Nothing is moved. Omit `amount` to refund everything still refundable. A `reference` already recorded on this settlement is refused with `409 DUPLICATE_REFERENCE`; send an `Idempotency-Key` too, so two retries in flight at once cannot both be recorded.
# Add a page
Source: https://docs.1club.ai/api-reference/website/add-a-page
/openapi-platform.json post /v1/platform/website/pages
Creates a page on the draft. `slug` becomes its URL path segment and must be unique on the site; it is lowercased and stripped of surrounding slashes.
Adding a page does not link to it - update the header's `navItems` and the footer's links afterwards, or visitors will never find it.
# Add a section to a page
Source: https://docs.1club.ai/api-reference/website/add-a-section-to-a-page
/openapi-platform.json post /v1/platform/website/pages/{slug}/sections
Appends a section, or inserts it at `position`. A position past the end appends rather than failing.
For rebuilding a page, prefer `PUT /pages/{slug}` with the full `sections` array - it is one request instead of many, and cannot leave the page half-built.
# Delete a page
Source: https://docs.1club.ai/api-reference/website/delete-a-page
/openapi-platform.json delete /v1/platform/website/pages/{slug}
Removes a page from the draft. A website must keep at least one page, so the last one cannot be deleted - delete the website itself instead. The homepage cannot be deleted while other pages remain - make another page the homepage first. Remember to drop the page from the header and footer links afterwards.
# Get one page
Source: https://docs.1club.ai/api-reference/website/get-one-page
/openapi-platform.json get /v1/platform/website/pages/{slug}
A single page with its full section list, config and translations.
# Get the design-token catalog
Source: https://docs.1club.ai/api-reference/website/get-the-design-token-catalog
/openapi-platform.json get /v1/platform/website/theme-tokens
Every design token the website accepts, what it means, what it falls back to when unset, and the named looks on offer.
Read this before writing a theme. It is the machine-readable counterpart to `/section-types`: that one says what may go IN a page, this one says how the site LOOKS. Tokens are added over time, so read it rather than hardcoding the values.
Two things worth knowing before you write anything. **Unset is the normal state** — every token falls back to the organization's branding or to a value derived from the tokens above it, so a good theme sets the fewest tokens it needs and leaves the rest inherited. And **the tokens are only coherent in combinations**, which is what `presets` are for: start from one and adjust two or three tokens, rather than composing 32 from scratch.
# Get the section-type catalog
Source: https://docs.1club.ai/api-reference/website/get-the-section-type-catalog
/openapi-platform.json get /v1/platform/website/section-types
Every section type a page or layout may use, with its config fields, their allowed values, and authoring notes.
Read this before composing sections: it is the machine-readable definition of what a `hero` or a `plans` block accepts, and section types are added over time. `content` types belong in a page's `sections`; `layout` types (header, footer) belong to the site layout and appear on every page.
# Get the website
Source: https://docs.1club.ai/api-reference/website/get-the-website
/openapi-platform.json get /v1/platform/website
Returns one website with its page list, layout, publish state and public URLs. Omit `siteId` for the organization's default website.
`previewUrl` renders the unpublished draft, which is how you review edits made through this API before publishing them; `url` is what the public sees and only reflects the last published release.
# Get the website's custom CSS
Source: https://docs.1club.ai/api-reference/website/get-the-websites-custom-css
/openapi-platform.json get /v1/platform/website/css
Returns the site-wide custom stylesheet, or an empty string when none is set.
# Get the website's theme
Source: https://docs.1club.ai/api-reference/website/get-the-websites-theme
/openapi-platform.json get /v1/platform/website/theme
What the site has chosen, what that resolves to once the organization's branding and the declared defaults are applied, and which of the three supplied each value.
`sources` is the field to read before changing anything: it tells you whether a colour is one the customer picked for this website (`site`), the organization's brand colour (`branding`), or a platform default. Overwriting a `branding` value is how a site quietly stops matching the rest of the customer's brand, so prefer leaving those alone unless asked.
A token missing from `resolved` is one whose fallback is computed by the stylesheet from the palette — the surface, muted-text and border colours — rather than one that has no value.
# List publish history
Source: https://docs.1club.ai/api-reference/website/list-publish-history
/openapi-platform.json get /v1/platform/website/releases
Releases newest first. The one with `isActive: true` is what the public site is serving; its id is what `POST /releases/{releaseId}/rollback` takes to undo a publish.
# List the organization's websites
Source: https://docs.1club.ai/api-reference/website/list-the-organizations-websites
/openapi-platform.json get /v1/platform/website/sites
Every website the organization owns, its default one first. Use this when the organization runs more than one site to find the `siteId` the other endpoints take.
# List the website's pages
Source: https://docs.1club.ai/api-reference/website/list-the-websites-pages
/openapi-platform.json get /v1/platform/website/pages
Every page on the site with its section types and SEO fields, but not the section bodies - fetch one page for those.
# Publish the website
Source: https://docs.1club.ai/api-reference/website/publish-the-website
/openapi-platform.json post /v1/platform/website/releases
Makes the current draft live. Until this is called, edits made through this API are invisible to the public - this is the one endpoint that changes what visitors see, which is why it needs `website:publish` rather than `website:write`.
`amend` (the default) overwrites the active release in place, so routine edits do not litter the history. `snapshot` cuts a new version, which is what you want before a large change so there is a version to roll back to.
Requires the Website plan addon, the same as publishing from the admin editor.
# Remove a section from a page
Source: https://docs.1club.ai/api-reference/website/remove-a-section-from-a-page
/openapi-platform.json delete /v1/platform/website/pages/{slug}/sections/{index}
Deletes the section at `index`. Later sections shift down by one, so re-read the page before deleting another.
# Replace the website's custom CSS
Source: https://docs.1club.ai/api-reference/website/replace-the-websites-custom-css
/openapi-platform.json put /v1/platform/website/css
Sets the site-wide stylesheet, replacing it entirely - read `GET /css` first and send the whole thing back with your changes.
Applied globally on the public site. Target sections with `.section-{type}` (`.section-hero`, `.section-footer`) and prefer theme variables such as `var(--primary-color)` so styles survive a light/dark switch. Script tags are rejected.
# Roll back to an earlier release
Source: https://docs.1club.ai/api-reference/website/roll-back-to-an-earlier-release
/openapi-platform.json post /v1/platform/website/releases/{releaseId}/rollback
Restores a previous release: its content becomes the draft and is published as a new version, so the rollback is itself recorded rather than rewriting history. Use `GET /releases` to find the id.
This discards unpublished draft edits - the draft is overwritten with the release's content.
# Set the website's design tokens
Source: https://docs.1club.ai/api-reference/website/set-the-websites-design-tokens
/openapi-platform.json put /v1/platform/website/theme
Merges a flat token patch into the site's theme, optionally on top of a named preset. Read `GET /theme-tokens` first for the tokens, their allowed values and the presets.
**This writes the draft**, like every other edit here — nothing a visitor sees changes until `POST /releases` publishes it. That is the point of tokens living on the website rather than on the organization's branding: a palette change is previewable, versioned in the release, and revertible.
Merges field by field, so setting the heading font leaves the palette alone. Send `null` for a token to return it to what it inherits — from the organization's branding, from another token, or from the platform default. That is the only way back once a value has been written; there is no "unset" string.
A `preset` is the exception to that merge: it REPLACES the look rather than adding to it. Every token any preset owns is cleared first, then the chosen preset's values are written. Otherwise switching from `night` to `editorial` would keep night's whole dark palette underneath and leave the site describing a hybrid nobody chose. Tokens no preset owns — a hand-picked brand colour — are untouched.
When `preset` and `tokens` are both sent the preset is applied first and the tokens land on top, so "use the editorial look but keep our heading font" is one call.
Every problem with a patch is reported at once rather than one at a time. Tokens are validated against the catalog: an unknown key, a value outside a token's options, a font the site cannot load, or a colour that is not a hex are all refused rather than stored — a colour CSS cannot parse does not fall back, it paints the surface transparent.
# Update a page
Source: https://docs.1club.ai/api-reference/website/update-a-page
/openapi-platform.json put /v1/platform/website/pages/{slug}
Updates a page on the draft. Omitted fields are left alone, so this doubles as a partial update.
`sections`, when present, **replaces the entire list** - which is the intended way to rebuild a page: one request that either applies whole or fails whole, rather than a sequence of per-section calls that can leave the page half-edited. `translations` replaces the whole locale map for the same reason. Send `metaTitle`/`metaDescription`/`ogImage` as `null` to clear them.
# Update a section's config
Source: https://docs.1club.ai/api-reference/website/update-a-sections-config
/openapi-platform.json patch /v1/platform/website/pages/{slug}/sections/{index}
Merges the given fields into the section's existing config; fields you do not send keep their current values. Array fields are replaced wholesale, so send the whole array when changing one entry.
`index` is the section's 0-based position as returned by `GET /pages/{slug}`. Indexes shift when sections are added or removed, so re-read the page rather than reusing a stale one.
`translations` is how one section's copy is localized without rewriting the page. Unlike `config` it **replaces** the whole locale map, so read the section first and send back every locale you are keeping.
# Update the site header or footer
Source: https://docs.1club.ai/api-reference/website/update-the-site-header-or-footer
/openapi-platform.json put /v1/platform/website/layout/{slot}
The header and footer are site-level and render on every page. Config is merged into what is there, creating the slot if the site has none.
The link arrays (`navItems` on the header, `linkCategories`/`links` on the footer) **replace** rather than merge - read the current layout from `GET /v1/platform/website` and send the complete array, or you will drop the links you left out.
# Update website-level settings
Source: https://docs.1club.ai/api-reference/website/update-website-level-settings
/openapi-platform.json patch /v1/platform/website
Changes the website's name, theme behaviour and site-wide SEO defaults. Every field is optional; omitted fields are left alone.
`supportedLocales` and `defaultLocale` are **organization-level** and need the additional `organization:write` scope. They also decide which languages the member portal, the mobile app and outbound email offer, and they take effect immediately rather than on publish - so they are a separate grant from editing the draft. They are reachable here at all because they are what decides which localized URLs the website serves: a page or section translated into an unlisted locale has no URL to be served at and no `hreflang` alternate pointing to it. A `defaultLocale` outside `supportedLocales` is a 400.
Everything else in this body edits the **draft**, `seoSettings` included - it is stored in the site's settings, so a release carries it and the public site keeps serving the previously published values until you publish.
Deliberately cannot change the site's address (`slug`, `domain`), which website is the organization's default, or whether the site is switched on - those move a customer's public presence and are not side effects a content edit should have.
# Changelog
Source: https://docs.1club.ai/changelog
New features and improvements in 1club.
## A sales pipeline that lives in Members
Work your leads on a board in Members, let forms and your website add them, and see which ones turn into paying customers.
Your pipeline has moved to Members > Leads. See your leads on a board with a
column for each stage, or as a list, and drag a card to move it. Every card
shows where the lead came from, who is following up, how long they have
waited and their next booking.
Leads now come in on their own. Choose a stage and a source for any form, and
people who submit it join your pipeline. Enquiries from your website's contact
form arrive in it too. When a lead pays for a membership or a pass, they
leave the pipeline and become a customer automatically, with nothing to close
by hand.
Nobody is lost for good. A lead who goes quiet moves to Cold, by hand or
after a number of days you choose, and can warm up again. Automations can
start when a lead changes stage, move leads between stages, assign them to a
member of staff and notify whoever they are assigned to. The marketing
dashboard shows how many new leads became customers, how quickly, and which
sources bring the ones who buy.
Two smart tags help with the people who already bought. Frozen marks members
whose memberships are all paused, so they stay members you can see and
message. Trial now starts with any first plan, a pass included, and covers a
family on a shared plan together.
## Age groups and levels on class types
Tag a class type with its sport, age group and level. Members see them on every schedule, 1club.me finds your club for "kids tennis", and belt progress counts only the classes that train for it.
A class type can now say who it is for and how hard it is. Give it a sport,
an age group - Infants, Kids (5–12), Juniors (13–17), Adults, Seniors or all
ages - and a level: Beginner, Intermediate, Advanced or all levels. Set them
once under Settings → Class types and every class of that type carries them.
New classes of the type also start with its sport.
Members see the age group and level under each class on your schedule - on
1club.me, in the member portal and on your website - and on the Book a
session list on your club page. The schedule's Filters menu can narrow it to
one age group or level.
People searching 1club.me for "kids tennis" or "advanced jiu jitsu", or
picking an age group and level in the search filters, now find clubs with an
upcoming public class of that kind. Opening a club from those results shows
its schedule already narrowed to it. AI assistants searching through the
1club MCP server get the same answer.
A class type can also say which progression track its sessions count toward.
Once a track has class types linked, only their sessions move a member toward
the next belt, so a strength class no longer counts toward a jiu-jitsu
grading. A track with nothing linked keeps counting every session, as before.
The age groups use the same bands as the Age smart tags, so "Kids" means 5–12
everywhere you see it.
[Learn more](/settings/class-types)
## Lessons for two or more
Members can book a coach for a group - a couple, two friends - and you price and pay the session by how many are coming.
Set how many people a coach takes per session in their profile, for example
2 for a lesson for two. When a member books that coach without a court, they
add the people who are coming, finding them by name, phone or email, with the
people they have played with listed first, and the session is for that group.
Everyone on the booking gets the details and sees it in their own bookings.
Each person pays the full session price unless you say otherwise: the new
Number of people pricing rule condition lets you take, say, 25% off for two,
or set one price for a group, for one coach or for all of them. The checkout
shows the total and what it comes to per person. Pay rates can add an amount
for each extra person, so a coach earns more for a bigger group.
## A clearer admin menu
A slim menu down the side, each section's pages beside it, and new Sales and Events sections.
The admin has a new menu. Each section is an icon with its name in a slim bar
down the side, and a section's own pages list in a column beside it, so moving
between Plans and Memberships, or Transactions and Invoices, is one click.
Sections are grouped by what you are doing. Sales holds what you sell and what
selling brings in - plans, memberships, products, orders, Finance
(transactions, payments, invoices and wallets), the cash register, promotions
and your pipeline. Events has its own section, with tournaments, matches and
ratings beside it, and a club that does not run events can switch the
section off in Settings > Events. Areas are now under Settings > Clubs.
Your notifications moved to your profile menu, where the unread count now
shows.
## Text members from the inbox
Send SMS from the inbox and automations, from your club's own name or number.
SMS is now a channel in the inbox, beside email and WhatsApp. Pick SMS when you
compose, choose who to text or a segment, and see how many SMS a message takes
before you send. Automations can text members too.
Your club texts as itself. Members in Europe see your club's name instead of a
number once we have approved it, and once it is registered in the countries whose
carriers require that. In the US and Canada your club gets its own toll-free
number, verified with your company's details from your billing entity. Set it all up in Settings >
Communication > SMS.
A member who wants no more texts replies STOP to your number, or you record it
on their profile with Stop SMS.
[Learn more](/settings/sms)
## An easier editor for website content, emails and descriptions
The editor you write website content, emails and descriptions in now works like a document, and pasting from Google Docs or Word keeps your formatting.
Every place you write rich text in 1club - website content, email templates and
campaigns, club, instructor and event descriptions - has a clearer editor.
One toolbar across the top holds everything: undo, the text style (text,
headings, quote), bold, italic, underline and links, lists, alignment, and an
Insert menu for images, videos, buttons, tables and the rest. The controls
that used to appear only when you hovered in the left margin are gone, so
your text starts at the edge like any other field, and no second toolbar
pops up over the text you select. Images, videos, tables and buttons get a
delete button in the corner.
Pasting works the way you expect. Content copied from Google Docs, Word or
a web page keeps its headings, lists, bold, italic and links, and leaves the
fonts and colours behind. Paste a link over selected text to link it.
The editor's menus now appear in your language.
## Add your players to a booking
Members add the people they are playing with while booking a court, and each player gets the booking in their own account.
Booking a court for four no longer means one name on the schedule. While
booking, members can add the people they are playing with: on your portal
and website they search your club's members by name or add
anyone by email, and on 1club.me they add players by email. Staff can pick the
players when creating a booking in the admin, and everyone can still add or
remove players from the booking afterwards.
Each player gets an email with the booking details and a calendar invite, and
the booking appears in their own bookings, where they can see who they are
playing with and leave if their plans change. The person who booked keeps
paying for the court as before, and staff can still split the payment between
the players.
How many people a court takes comes from the Default Max Players setting on
its area type, so set it above 1 for the courts you want members to fill.
## Waivers required for entry
Keep anyone who has not signed your waiver from checking themselves in, and let them sign on the check-in tablet.
Mark a waiver rule as required for entry and nobody it covers can check themselves
in - on the self-service tablet, with their member QR code or from the member
portal - until they have signed, and a booking alone no longer opens a door for
them. Scope it to specific memberships and passes, or apply it to everyone.
On the check-in tablet, a member who enters their full PIN or scans their QR code and
still has to sign is shown the waiver right there, signs it and carries straight on.
At the front desk you stay in charge: the member's profile and the card check at the
till tell you who has not signed yet.
New visitors can sign on a tablet too: leave a form's station link open in the
browser and they fill it in one after another. Nobody stays signed in, the form starts
over for the next person, and anyone already registered is asked to use their own
phone. You can also show the documents first, before people type in their details.
To be sure who signed, a form can also ask for a code sent to the person's email,
WhatsApp or phone. Anyone who can't get one still finishes, and their waiver waits on
their profile for you to approve at the front desk.
## Sessions to the next belt
Set how many sessions each rank takes; members see how close they are, and smart tags show you who is eligible.
Martial arts schools can now set how many sessions each rank takes. Open a
progression track in Settings and, next to each belt or level, enter the
sessions a member needs at the rank below - for example 20 sessions at Orange
before Blue. The count starts again at every new rank.
1Club counts every session a member attends after earning their current rank,
the same way it counts their week streak: a check-in, or a completed booking.
Members see "13 / 20 sessions to Blue Belt" with a progress bar on their
account overview and profile, next to their streak, and "Eligible for
promotion" once they get there. Staff see the same counter on the member's
page.
Eligible is a signal, not a promotion - the coach still decides. Two new
smart-tag categories help you act on it: Progression tags mark who is eligible,
close to promotion, stalled at their rank or recently promoted, and Assessment
tags mark who has not been assessed lately, your top performers and who needs
attention. Filter the member list by them, message them, or start an
automation.
[Learn more](/settings/progression)
## Time-of-day promotions on the booking calendar
Run offers like "2 hours for the price of 1, weekdays 8:00 – 16:00" and members see the deal right on your booking calendar.
Promotions can now target the time of day a booking starts, the length of the
booking and the venues it applies to. A padel club can offer a second hour
free on daytime courts at one venue, or a cheaper early-morning hour, without
a promo code.
Members see it where they book. The calendar shows the offer and its hours
under the grid, strikes through the regular price when they pick a booking
that qualifies and names the promotion beside the new total. A member who
picks one hour is told the second one is free and can add it in one tap.
The name can be translated into every language your site supports.
On a court booked together with a coach, a court-only promotion discounts the
court and leaves the coach's fee untouched.
[Learn more](/sales/promotions)
## Training assessments for members and parents
Instructors score each training on your own criteria, and the member and their parents get the result right away.
Academies can now tell every member, and every parent, how each training
went. Create an assessment template in Settings - for example Technique,
Serve, Speed and Focus, scored from 1 to 5 - and set it as the default on a
class type.
After a class or a private lesson, the instructor opens the roster and taps
Assess next to a member: one row of buttons per criterion and an optional
note. Saving sends the assessment to the member and, for a child, to the
parents on their household by email or in the app, so a parent receives the card
without anyone copying scores into a message.
Members and parents find every assessment under My account, grouped by
child, with an average per criterion once there are a few. Staff see the full
history on the member's page, where they can also add one for a training the
system never recorded.
## Wallet deposits and cancellation charges
Protect a minimum wallet balance and collect what cancelled or missed bookings cost from it.
Set named deposit rules for individual clubs or all clubs in Billing settings.
Warn about a shortfall, follow the booking payment policy, or require a deposit
before booking. When the booking policy allows payment on arrival, members can
confirm their booking and pay the booking and deposit later. Staff can collect
both in one payment, while the deposit stays in the wallet for future bookings.
Members can top up their wallet, and money above the deposit remains available
for purchases. Staff can refund unspent paid funding from wallet history.
Your existing cancellation terms now decide the final cost of a cancelled class,
court or appointment the same way whether the member paid upfront or planned to
pay at the club. Anything paid beyond that cost is refunded, anything unpaid is
taken from the wallet, deposit included, and a missed booking costs the full
price. Members see the terms before booking and the exact charge before
cancelling. Fully covered Membership and Pass bookings cost nothing, and top-ups
settle unpaid charges before restoring the deposit. Deposits start disabled, so
you choose when to introduce them.
## Instructor profile pages
Every instructor now has their own page on your website, your member portal and 1club.me, built to be found in search.
Each instructor you publish gets a profile page: their photo, role and
specialties, their bio, experience and certifications, a photo gallery and the
classes they teach next. A card beside it sends visitors straight to that
instructor's booking calendar or, when they are not booked one-to-one online,
lets them message your club about training with them.
The pages are written for search engines as well as people. Each has its own
title and description in every language your site supports, a readable
address with the instructor's name in it, and the structured data search
engines use to recognise a person and where they work. Instructors with
something to say about themselves are added to your sitemap automatically,
while hidden and members-only instructors stay out of search.
The instructor lists on your website and the team on your 1club.me club page
now link to these profiles, so a visitor who likes the look of a coach can
read about them before booking.
## Run a tournament
Turn any event into a tournament - import the entry list from registrations, generate a knockout or round-robin draw, and record scores that count towards 1club Rating.
Your events can now run a tournament. Create the event as usual and let people
sign up for it; the tournament is built on top of those registrations.
Import the entry list straight from the event's registrations, or add players
yourself - including pairs, for a doubles sport like padel. Seed whoever you
want seeded and leave the rest to the draw. Then generate it: a seeded
knockout bracket with byes handed to the top seeds, or a round robin where
everyone plays everyone once and a standings table ranks them by matches won,
then set difference, then game difference.
Recording a score is one dialog. In a knockout the winner moves into the next
round on the spot, and a no-show is entered as a walkover. Every completed
match is also recorded as a real match in each player's history and moves
their 1club Rating - through the same rating system a manually recorded match
uses. A player without a 1club account can still compete: their matches are
recorded and the bracket runs, they simply are not rated, and the tournament
says so rather than leaving you to notice.
Members and visitors follow the draw on the event page, in the member portal
and on your website: the bracket or the standings table, updating as you
enter results. Before the draw is out they see who has entered and how many
places are left - the question anyone deciding whether to play actually has.
Registrations close the moment you generate the draw, so nobody takes a place
in a bracket that already exists. A Tournaments page lists what is coming up
and what has finished.
Everything competitive now sits in one place. A new Competitions section holds
the tournaments, every match that has been played, and a rating leaderboard per
sport - so a draw, the matches it produced and where they left everyone's
rating are all one click apart. Switch it on under Settings → Competitions.
## Announce something at the top of your website
A bar above your navigation carries one important line on every page - a sentence you write, or a post, class or event that keeps itself up to date.
Some things a visitor has to know before anything else. The hall is closed
this weekend. Renewals end on Sunday. The new lead wall opens on Friday.
Turn on the announcement bar under Features in the website editor and that
line sits above your navigation on every page.
You can write the sentence yourself. Or you can point the bar at something you
already have - a post, a class, or an event - and it takes the title, adds a
"Read more" link to that page, and stops on its own once the class or the
event is over. No going back to take it down on Monday morning.
Add your own words in front of a post, a class or an event if you want to:
"This Saturday: Autumn Boulder Jam".
There is nothing to style. The bar is your brand colour, so it changes with
the rest of your site and cannot end up unreadable.
Visitors can close it, and it stays closed until you change what it says - so
an announcement that matters is never hidden from the people who dismissed the
last one.
## Instructors see members by first name only
Coaches now see "Maria P." with no email, phone or address across the schedule, class rosters and check-ins - not just the Clients list.
The Clients list has always shown instructors a member's first name and surname
initial, with no contact details. The rest of the instructor panel did not: open
the same member from the schedule, a class roster or a check-in and the full
name, email address, phone number and home address were all there.
That is now consistent across the instructor panel. On the calendar, on a
booking, on a class roster, on a check-in and in the client picker when adding
a booking, they see the first name and the surname initial, and no contact
details. Their photo still shows, so the person in front of them is still easy
to recognise. Two members who would otherwise both read as "Maria P." are told
apart automatically.
The client picker also now offers only the members that instructor actually
works with, matching what their Clients list shows, instead of everyone in the
organization.
Nothing changes for admins, managers or front desk. If you want your coaches to
keep full member details, grant them Members → View in Settings → Roles and
permissions, and they will see everything again. You can also widen which
members an instructor sees - from the ones they teach to the whole
organization - without revealing contact details, using the Member directory
permission.
## Bill through more than one company
Run several companies from one organization, each with its own invoices, number series and payment accounts.
Some clubs are one company. Others are several - a separate company per venue, or
a separate one for the bar and the shop. Until now 1club assumed the first, so every
invoice carried the same legal name and every card payment landed in the same
merchant account.
Billing entities are where you describe the rest. Each one holds its own legal name,
tax and registration numbers, registered address, bank details and invoice number
series. Invoices it issues carry its details and run in its own numbering, so each
company's books stand on their own.
Point a club at a company and everything sold there bills through it. Point a revenue
account at one instead and everything posted to that account bills through it wherever
it was sold, which is how a bar or a shop owned by a separate company gets its own
invoices without a venue of its own. Anything you leave alone keeps billing through
your default company, so nothing changes until you say so.
Payment accounts follow the same split. Each company connects its own Stripe, myPOS or
DatecsPay account, and money for its sales settles there. A card reader charges through
the account of the company that bills for the club it sits at.
Reports, the invoice list and the accounting export can each be narrowed to one
company, so your bookkeeper gets the file for the company they are filing for.
Two things stay deliberately whole. A basket holding items from two companies cannot be
paid in one go - the till names both and you sell them separately, because one payment
settles to one company. Wallet credit belongs to your default company and is spent on
its sales.
[Learn more](/settings/billing-entities)
## Set up your club over the API
The platform API and connected AI assistants can now build out what your club sells and books - repeating classes, new courts and studios, coaches, membership plans with the category and account their sales belong to, and what each coach is paid.
The platform API could already read almost everything about a club and sell
from what was there. It can now set that up in the first place.
A repeating class is one request. Ask for Tuesdays and Thursdays at 19:00 until
the end of the season and the whole series is created at once, linked as one
class so it can be rescheduled or cancelled together later. Anything with an
end date is written in full there and then, so the season is on the schedule
the moment you ask for it rather than filling in eight weeks at a time; a class
with no end keeps extending itself.
New courts, studios and lanes can be added to a club and given their price,
their opening hours and how many bookings they take at once. Taking one out of
service for refurbishment is the same call, and it leaves availability
immediately - including for the partners and channels that read it.
Coaches can be added too, from a person the club already knows, with the sports
they teach, what a member pays for an hour with them, and when they are
available. Creating a coach this way makes them bookable and nothing more: it
never hands anyone a login to your admin portal, which stays a decision a
person makes there. What the club pays its coaches is now configurable
separately - a flat amount per session, per class or per attendee, or a share
of what the session took, scoped to particular coaches, particular kinds of
class, or particular ways a member got in.
Membership plans gained the two fields that decide where a sale belongs: the
category it is grouped under on your price list, and the revenue account its
income posts to. Categories can be created and renamed over the API as well,
and your chart of accounts is readable so the right account can be picked
rather than guessed.
Connected AI assistants get all of it through new MCP tools, so you can ask
Claude or ChatGPT to put a new court on the schedule, add the coach who runs
it, and set what they earn - without opening the dashboard.
## SEO panel for your website
Your website now grades its own SEO, and one button fills in every description it is missing.
The website editor has a new SEO panel, next to Edit website and CSS. It
opens on an analysis rather than a form.
At the top, Google measures your published site and reports its SEO, speed,
accessibility and best-practices scores, with the exact issues it found. The
same reading powers the AI visibility report.
Below it, 1club checks the draft you are editing and lists what is missing or
wrong before you publish: pages with no search title or description, two
pages claiming the same one, a description too long to fit a search result,
a page accidentally hidden from search. Each finding links straight to the
page it is about.
"Fix my SEO" writes the rest. It fills in the site description and the search
title and description of every page that has none, using your club details
and the content already on each page. Anything you wrote yourself is left
alone, and everything lands in the draft, so you review it before it goes
live.
## Design your website, not just fill it in
A Design panel with named looks, a full colour and typography palette, corner, shadow and spacing controls, and sections that animate in as visitors scroll.
Your website has always taken your brand colour and your logo. Now it has a
Design panel of its own - so two clubs using 1club no longer have to look like
the same website in different colours.
Start from a look. Editorial, Brutalist, Soft studio, Night, Warm boutique,
Sport, Minimal or Clean - each one a complete, coordinated set of choices
rather than a single setting. Pick one and adjust from there, or ignore them
and set what you want yourself.
What you can now control: seven colours instead of three - the brand colour,
an accent, the page background, body text, cards, secondary text and the lines
between things. A body font and a heading font, paired. How heavy and how
tightly spaced headings are. How round corners are, separately for cards,
images and form fields if you want the contrast. How much shadow surfaces
carry, how heavy the lines are, and how much room the page breathes.
Everything is optional. Leave a setting alone and it follows your brand
settings, exactly as before - so the panel reads as "our brand, with these
exceptions" rather than a form to fill in. Clearing one hands it straight back
to your brand.
Sections can animate into place as visitors scroll, if you want them to.
Movement is off unless you turn it on - your site keeps behaving exactly as
it does today until you say otherwise, and picking one of the looks above
brings whatever movement suits it. Choose how much (subtle or lively) and how:
fade, rise, zoom or slide. Any single section can do something different, or
nothing at all.
Cards and photos can respond to the pointer too: lift, zoom the photo, or glow
in your brand colour. And a Stats section can count its numbers up as visitors
reach it. Visitors who have asked their phone or computer for less on-screen
movement always get none of it, whatever you pick.
All of it is a draft until you publish, which is the real change underneath:
changing your website's colours used to take effect on the live site the
moment you saved it. Now you can see it in the preview, publish it when it is
right, and roll it back if it is not.
And you can just ask. The website assistant, and any AI assistant you have
connected to 1club, can now read and change the design directly - "make this
feel more premium", "use a serif for headings", "square everything off" - and
it knows to leave your brand colour alone unless you say otherwise.
## Promotion notices
Members are told when your club moves them up a progression track.
When you promote a member to a new belt, grade or level, they now hear about
it - in the app and by email, in their own language, without anyone building
an automation.
The message names the track, the level they reached and the one they came
from, and links to their progress in their account. You control the wording:
"Promotion" is an editable template in Settings, so you can add whatever your
club says at a grading - when to bow in, what comes next.
Only real promotions send anything. Recording the rank a member already holds
when you first set them up, awarding a stripe, correcting a mistake or
re-saving a note stays silent, so entering your existing ranks does not email
the whole roster. Every promotion also carries a tick box in case you already
told them in person.
Members can switch promotion notices off under Progression in their
notification settings, and you can turn them off for everyone from your club's
notification policy.
## Card payments in the member app
Members can now pay by card in the mobile app at clubs on myPOS or DatecsPay, not just Stripe.
The member app now takes card payments through myPOS and DatecsPay, so clubs
on those providers can be paid from a phone the same way Stripe clubs already
could. It covers everything members buy: class, court and coach bookings,
event registrations, memberships, and settling an outstanding balance on a
booking they already have.
Paying works as it does on the web. The member taps Pay, the club's secure
hosted card page opens inside the app, and they are returned to the app with
the result - no leaving for a browser and no copying links. If the confirmation
from the provider takes a moment longer than usual, the app says so plainly
rather than reporting a failure, so nobody is nudged into paying twice.
[Learn more](/settings/integrations/mypos)
## Sell a whole season of court time as a season pass
Define a season, hold recurring weekly court slots for a member (with a coach on them if the hour is a lesson), and sell the whole run in one paid contract with volume discounts and makeup hours. Members can now pick their hours and buy a season pass themselves, online.
Racket clubs live on contracted court time: the same member, the same court,
the same hour, every week of the winter. Selling that used to mean a
spreadsheet next to the calendar. Now it is a product.
Define a season under Settings with its start and end dates, and the plan it
sells through carries a volume ladder, so holding more hours a week earns a
bigger discount. On the schedule, click any free hour to start a season sale:
pick up to four weekly slots, see every week priced by your pricing rules
with conflicts flagged, and sell the whole run as one paid contract. The sale
books every week of the season at once, so the courts come off the market the
moment the member commits.
A weekly hour can carry a coach as well as a court, which is how a term of
lessons is sold: pick the coach on the slot and every week of the season
reserves them too, so nobody can book them over the lesson. Their diary and
roster are checked across the whole season before you sell. Weeks they cannot
make (time off, a class they teach, an hour already booked) are shown on the
quote and charged at the court-only rate. The member keeps their court either
way, so one coach holiday in February no longer stands between you and
selling the winter.
Members can buy a season pass themselves, too. Publish a season and it
appears beside your other plans in the member portal, on your website and in
the mobile app. The member picks their weekly hours the way your desk does,
sees which courts are free for every week of the season, switches to another
court or hour when one is taken, and pays online. Their hours are held while
they pay and booked for the whole season the moment the payment lands; an
abandoned checkout gives the courts back on its own. Keep a season unpublished
while your desk signs up last season's members, then publish it to everyone.
A season pass is paid up front and never auto-renews; the next season is a
fresh sale at the new season's prices. When a holder cancels a week far
enough ahead, they earn a makeup hour to replay on another date inside the
season, and cancelling the whole pass releases every remaining week in one
step, with a refund figure computed for you. Season holders count as members
everywhere: their own extra bookings get the member rate you author in
Pricing rules.
## Invite and sign in members by phone
Invite members over WhatsApp or SMS, and let them join and sign in with a phone number instead of an email.
Members no longer need an email address. Invite anyone you have a phone number
for and the invitation goes out over WhatsApp - from your own WhatsApp Business
number if you have connected one, otherwise from ours - or by text. Your member
taps the link, confirms a code, and they are in: no password to choose, no
inbox to check.
From then on they sign in with the same number. Switch it on in
Settings → Contacts, where you can also make email optional so your signup form
stops asking for something your members may not have.
Members who have no email now get their booking confirmations by text, so a
phone-only member hears from you the same way everyone else does.
## Browser notifications
Get alerts on your desktop the moment something happens, even when 1club isn't the tab you're looking at.
Notifications that were already set to reach you in the app can now also raise
an alert from your browser - so a member booking, a failed payment or a new
message reaches you while you are working in something else.
Turn it on per browser under Settings → Notifications. Members can do the same
from their notification settings in the portal. Nothing else changes: the same
categories you have chosen still decide what you hear about, and the
notification list keeps everything either way.
Your browser asks for permission the first time you switch it on, and the
setting applies to that browser only - so a work laptop can alert you while a
shared front-desk machine stays quiet.
## Family and corporate plans, shared with the whole account
Turn on sharing for a plan and everyone on the buyer's household or company account can use it.
A plan can now be shared with the account that bought it. Switch on "Shared
with account" on the plan and everyone on the holder's household - or their
company - books with it, drawing on the same allowance. Set it per plan, and
override it on an individual membership when one family wants something
different from the next.
Sharing works the same way for a company as for a family, so an employer can
buy one plan and have their team book on it.
Members see it wherever they see the plan: on the plan card, on the plan
details page, in the signup form, and on the membership in their account. The
person the plan is shared with simply picks it as their way in and books -
no seat numbers to manage, and nobody quietly falls outside the family.
## Forms
Signup forms are now just Forms - build a questionnaire, a health declaration or a details update, choose exactly what it asks, and send it to a member with one click.
Signup forms have become **Forms**. The same builder still makes the signup
page that onboards a new member, but a form no longer has to sign anyone up.
Turn that off and you have a page you send to people you already have on
file: a medical questionnaire before a course, an emergency contact you never
collected, a consent form for photos, a details refresh at the start of the
season.
**Write your own questions.** A form asks the questions you write on it -
Yes/No, short or long text, a date, a number, a list to choose from - grouped
under headings, with help text where a question needs it, dragged into the
order you want them answered. A thirteen-item PAR-Q looks like the paper one
it replaces. You can also still set phone, date of birth, gender or last name
to required, optional or hidden per form, and a signup form always collects
the custom fields you marked as required at signup, so nothing you rely on
can be left out by accident.
**Every section is optional.** Plans, packages, add-on products, class types,
and the documents-and-signature step each appear only when you switch them
on. A form that just asks three questions looks like three questions. The
form dialog is grouped the same way - what the form does, what it asks, what
it sells - so there is one place to look for each decision.
**Send a form from an automation.** A new **Send form** step mails a member
their own single-use link, prefilled with what you already know about them,
so they confirm rather than retype. Put it after a plan is bought, a first
visit, or a tag - wherever the question actually needs asking.
**Answers stay answers.** Each form keeps a list of who was asked and what
they said, and opening a response shows it question by question. A
questionnaire's answers belong to that response and to its date - asking a
health declaration every season leaves you last season's as well as this
one's, instead of overwriting it. For the answers that really are part of
someone's record, like a shirt size, point the question at a profile field
and it updates the profile too. And a member's answers are on their own page
too, under **Documents** - so before a first session you can read what they
declared without going looking for the form they filled in.
## Club closed status
Retire a club - with an optional closing date - and it disappears from the schedule, switcher and every picker on its own.
A club can now be marked Closed, alongside the existing Active and Hidden
states. Closed is for a location that has stopped running for good: it drops
out of the schedule, the club switcher, and every club picker across the
admin, so staff are never asked to work in - or choose - a club that no
longer exists day to day. If closing leaves an organization with one club
left open, the switcher stops asking which club altogether.
Set a closing date instead of closing immediately, and the club stays fully
open - bookable, on the schedule, in the cash drawer - right up through that
date. Bookings, classes and events can't be scheduled for after it. The club
closes on its own the day after, with nothing further to do.
Closing a club never touches its history: existing bookings, memberships and
reports stay exactly as they were. A closed club's cash drawer stays visible
on the cash overview until its balance is reconciled to zero, so nothing is
silently written off.
## Style your website
Set your own page palette, fonts, corner radius, spacing and heading size for the whole site; give any section a dark band, a background photo, its own spacing or cropped images; and choose how the header and hero behave.
Until now a website section gave you two style choices: a background tone and
a full or contained width. Every section now takes six, and they behave
identically on all of them:
* **Vertical spacing** - none, tight, standard or generous. Set two related
sections to "none" to butt them together as one block.
* **Dark band** - a new background tone that flips the whole section to light
text on a dark surface, tinted towards your brand colour. The emphatic
section above the footer that used to need custom CSS.
* **Background photo** - a photo behind any section, with a slider for how
dark the scrim over it is. Previously only the hero could do this.
* **Narrow width** - a readable measure for text-heavy pages, where the
standard page width is too wide for prose.
* **Alignment** - centre a section's heading and its buttons, or keep them
left.
Sections that show photos - courts, instructors, galleries, call-to-action
blocks and locations - also gained **image framing**: keep each photo's own
proportions, or crop them all to square, landscape, wide or portrait so a
row of mismatched uploads still lines up.
**Call-to-action blocks get four layouts.** A *banner* is the centred band you
already had. *Text and photo* puts your copy beside an image. An *inline
nudge* is a single thin row - heading on the left, buttons on the right - that
slots between two sections without interrupting the page. An *offer card* is a
contained card for mid-page asks. Buttons can also be set to a larger size,
and gained a text-only style for the lesser of two options.
**Your palette, not ours.** Branding now sets the page background and body
text colour, separately for light and dark mode, alongside the brand colour
you already had. Every shaded section on the site - the subtle bands, the
brand tints, the dark band - is mixed from those, so one colour change
re-tones the whole site. The font picker also works now: seventeen families
to choose from for body and headings, and the one you pick is the one the
page loads. Before this, whichever font you chose, the site rendered in Inter.
**Four new site-wide choices.** *Corner radius* - sharp, soft or round - sets
every corner on the site at once: section bands, photo frames, cards, form
fields and buttons. *Spacing* - compact, standard or airy - sets the gap
between sections and the default padding inside one; a section's own Vertical
spacing still overrides it. *Heading size* - compact, standard or editorial -
scales every heading, including the hero's, against your body text. And
*buttons* now match across the whole site: a shape (pill, rounded or square)
and an uppercase option, applied to every button on every page rather than
chosen per section.
**The header can stay or scroll.** A header can now sit in the page and
scroll away instead of floating over everything - which is what you want when
the hero is a banner rather than a full-screen photo.
**The hero is no longer one fixed shape.** A photo hero can be full-screen,
tall, banner-height or short; its text can sit centred, bottom-left or
bottom-centre; and the dark scrim over the photo is now a slider rather than a
fixed 70%, so a photo worth showing can actually be seen. A new *Banner*
starting point sets all three at once, for the conventional club-site head:
a wide photo under the header rather than behind it.
Adding a section is also faster: heroes and call-to-action blocks offer named
starting points in the picker, so you can add "Inline nudge" or "Banner"
already set up instead of a blank section you then configure.
## Equipment rentals
Lend rackets, bibs and other gear out at the counter, and see what has not come back.
Any product can now be lent out instead of sold. Set a product's fulfilment to
**Rented out** and it leaves stock when it goes out and returns to stock when
it comes back, rather than being sold outright. The price becomes the rental
fee, and free gear works the same way.
* **Lend it like you sell anything else.** The till gains a **Rentals** tab,
kept apart from Products so a loan is never rung up as a sale. Ring gear up
alongside a court booking and it is due back when the court is; otherwise it
falls due at the end of the day.
* **See what is out.** A new Rentals page under Products shows what is out
right now, what is still to collect, and what is overdue, searchable by item
or by who has it.
* **Take it back at the counter.** The register gains a **Returns** lane that
opens on whoever is at the till. Two of three towels back is recorded as two
of three, and the loan stays open for the one still missing.
* **Be told at the moment it matters.** When someone checks in still holding
gear, the front desk sees it while they are standing there.
* **Close what is not coming back.** Write a rental off and it closes as lost,
with stock left reduced because the item genuinely is.
Items that are always handed back at the end of a session can return
themselves: switch on automatic return and the item closes when it falls due.
Those closures are marked as automatic, so a return someone actually saw stays
distinguishable from one the system assumed - which is the difference between
a rental log and a shrinkage report.
Gear belongs to the gym it is assigned to and can only go out from there, and
rental items cannot be sold online or bundled into a package, because both
need someone at a counter to hand the thing over.
Turn it on under **Settings > Operations > Rentals**.
[Learn more](/sales/rentals)
## Take bookings on your own website
Add the Book online section to any page and visitors can see live availability, pick a slot and pay without ever leaving your site.
Your website can now take the booking itself. Add the **Book online** section to
any page and visitors get the same booking experience your members use: courts
and rooms, private training with a coach, and group activities, all with live
availability and real prices.
It is the full experience, not a link out. Someone picks a court, sees what it
costs, chooses a duration and pays - by card, from their wallet or against their
membership - on your own domain, under your own branding. Guests can book
without an account if your booking policy allows it, and members see their own
existing bookings on the same calendar.
It fits the way clubs actually work. A multi-location club gets a location
picker above the calendar. A club with one court gets a whole week at a glance
instead of one day. On a phone the calendar becomes a swipeable list of times.
Group sessions appear as the day's timetable with a join button, waitlist
included.
You can also narrow it down. Point the section at a single sport, or use the
Calendar section to publish availability for just the court types or coaches you
choose - useful for a page about one facility or one coach.
Sections are easier to find while you are at it: the section picker in the
website editor now groups sections by what they do and has a search box, so
"reservations" or "timetable" finds the right one.
## Take card payments on a payment terminal at the front desk
Send a sale straight to a payment terminal on your counter - with Stripe or myPOS - and the customer taps while the payment records itself against the booking, membership or order.
Your front desk can now charge a card without anyone typing an amount twice.
Pick the payment terminal as the payment method, choose the device on your counter,
and the amount appears on its screen. The customer taps, inserts or swipes, and
the payment is recorded against whatever it was paying for - the booking is
confirmed, the membership starts, the order closes.
Both Stripe and myPOS terminals are supported, and you are not asked to choose
between them: a club can run a Stripe terminal at reception and a myPOS device at
the bar, each attached to the desk it stands on.
Add your terminals under Settings → Billing → Payment terminals, where you can see
which ones are online, name them by desk, and retire one without losing the
history of the payments it took. If a card is declined the terminal says why, and
the sale stays open so the customer can try another card or pay cash.
Nothing is lost if something goes wrong mid-sale. Close the tab, lose your wifi
or lock the tablet after the customer has tapped, and the payment still lands on
the right booking a few minutes later.
## Accept card payments with DatecsPay
Bulgarian clubs can now accept online card payments through DatecsPay (BORICA), alongside Stripe and myPOS.
Organizations can now connect DatecsPay to take online card payments. Once
connected in Settings → Integrations, DatecsPay appears as a payment option
everywhere members pay online - settling outstanding charges, booking paid
classes, buying memberships, and completing package or product orders.
Paying works the same familiar way as myPOS: the member is taken to the
secure BORICA hosted page to enter their card and complete 3-D Secure, then
returned to your site with the result. Successful payments confirm the
booking or membership and mark the charge paid automatically.
Connecting takes a terminal (TID), merchant (MID), the merchant's signing key,
and BORICA's certificate - entered once per environment. DatecsPay and myPOS
cover the same "pay by card" checkout, so only one of the two can be active at
a time. Shareable payment links remain myPOS-only for now.
[Learn more](/settings/integrations/datecspay)
## Resend your calendar invitation
Instructors can put a class or personal training session back in their own calendar without asking an admin to re-save it.
When a class or a personal training session is scheduled, the instructor gets
an email invitation they accept to put it in their own calendar. Until now
that email only ever went out once. If the invitation was declined by mistake,
the email got lost or the event was deleted, the instructor had no convenient
way to import it into their calendar.
Instructors can now send it to themselves. Open the class or booking, choose
"Resend calendar invitation" from the "..." menu, and the invitation arrives
again at the address on your staff profile - which the confirmation names, so
you can spot an out-of-date one.
## Bookable packages
Sell a birthday party or a team building day from a signup form, and let the customer pick the date themselves.
A package can now be sold against a time you actually run it, instead of the
club phoning the customer back to agree a date.
Turn on "Bookable" on any package, choose the days of the week it runs, and
list the times you start it - 10:00 and 14:00, say. Everything else about the
package stays as it was: the plans and products it includes, the headcount,
the price per extra child.
On the signup form the customer sees the free times next to the package,
picks one while they are choosing what to buy, and confirms the date on the
review screen before paying. Only times you actually offer appear, and days
the package does not run are greyed out. A time already sold disappears, so
two customers cannot book the same Saturday afternoon - the second one is
told the slot has gone rather than finding out on the day.
Staff see the date wherever the order is: on the order line in the admin, and
in the confirmation email the customer keeps. Cancelling the order gives the
date back, so it goes straight back on sale.
Built for parties, team building and private hire - anything where one
customer takes the whole thing rather than buying a ticket alongside others.
## Cancelling a paid order refunds it
Cancelling an order that has been paid now returns the money instead of just voiding the sale.
Cancelling a paid order used to void its transactions. The sale left the
books, but nothing gave the money back - the club kept it and there was no
record that it was owed.
Cancelling now asks where the refund should go and returns the money through
the same path as a refund from the transaction screen. The dialog shows the
amount before you commit, and the destination it can actually reach: back to
the original card, or as account credit. Payments taken through a provider
that cannot be reversed automatically say so, and offer account credit
instead of quietly turning a card payment into credit without telling you.
An order with nothing settled on it still cancels in one step, exactly as
before. An order that has been paid can no longer be cancelled without
deciding what happens to the money.
## Invoice or pro-forma straight from a sale
Issue an invoice or a pro-forma for an order in one click - including a quote for a team-building booking before anything is paid.
Documents used to be assembled the long way round: pick the customer, hunt
through their uninvoiced charges, and hope you caught all of them. And there
was no way at all to produce a document before a sale was fulfilled - exactly
what a company asks for when they want a quote for a team-building day.
Open any order and you can now issue an invoice or a pro-forma directly from
it. The document is built from the order's own lines, so it always matches
what was sold.
* Works on an unpaid draft too: build the booking, issue a pro-forma, and send
it for approval before anything is charged.
* A package appears as the package the customer bought - "Birthday package,
10 × Day pass" - instead of ten separate lines nobody recognises.
* VAT is now calculated on every document, including hand-written lines, which
previously showed no tax at all.
* Issued documents are listed on the order, and each order can carry one
invoice and one pro-forma.
Reporting gained the other half of the picture: revenue by item now shows what
a package listed for next to what it actually charged, plus the total discount
your bundles gave away over the period.
## Find a club in the app and join it in one tap
Members browse every published club on a map in the mobile app, open a full club page, and tap Book now to join that club and start booking.
The mobile app now opens on Explore: a map and list of every club published on
the 1club network, with filters, search by city and clubs near you. It works
before anyone has an account, so a first-time visitor sees real clubs instead
of a sign-in wall.
Tapping a club opens its own page in the app - cover photos, sports, amenities,
opening hours and location - and a single Book now button. Signing in is one
email field: someone who already has a 1club account carries straight on, and
a first-time visitor is moved into your club's own signup without having to
pick which of the two they are. That signup is yours - your branding, your
signup documents, your required and custom fields. Either way they arrive as a
real member, with a contact record and everything your automations and member
lists expect, and land on your club's home screen in the app.
Clubs receive marketplace members only while they are published on the
network - unpublishing stops it.
## Members can buy products from the portal
A cart in the member portal - members add products, adjust quantities, and pay in one order with the club's existing card provider.
The member portal now has a shop. Members browse your products, add what
they want to a cart, adjust quantities, and pay for everything in one order.
The cart sits at the bottom of the screen while they browse and remembers
what they picked between visits. Checkout goes straight to your card
provider - Stripe, myPOS or DatecsPay - and everything bought arrives as a
normal order in Billing, so sales come off club stock exactly as they would
at the front desk.
Enable the Products page under Settings → Member Portal → Portal Pages.
## Meta integration for Facebook and Instagram ads
Connect your Meta Business account in one click and see the bookings and revenue your ads actually produce.
Connect Meta from Settings → Integrations and 1club reports your bookings and
sales straight back to Facebook and Instagram, so your ads are optimized on
real revenue instead of clicks.
Connecting takes one click: sign in with Meta, pick your Business, your
dataset and your ad account, and you are done. There is no pixel ID to find
and no access token to copy out of Events Manager - and if you have never
advertised before, 1club can create the dataset for you.
Reporting is split in two. The Meta pixel runs on your member portal and
website - page views and reaching checkout - and only after a visitor accepts
marketing cookies. Bookings and payments are reported from 1club's own servers
instead, with the amount that was actually charged. That means your
conversions are still measured when a visitor blocks the pixel, and even for
charges that never touch a browser, such as a card-on-file renewal.
Sales taken at the front desk are reported as in-person conversions, not as
website ones, so your online ad results stay honest.
You can switch the browser pixel and the server-side reporting on or off
independently at any time.
[Learn more](/settings/integrations/meta)
## SEO controls for your website
Set how your site appears in search - site-wide descriptions, per-page control, search-console verification - and get every page you publish into your sitemap.
Your website's search presence is now something you control rather than
something that happens to you.
Under the website's settings there is a site-wide SEO section: a description
used wherever a page has none of its own, a default social-share image for
links posted to Facebook, LinkedIn or WhatsApp, and fields for your Google
Search Console and Bing Webmaster Tools verification codes - so you can claim
your site and see what people search for to find you.
Each page keeps its own SEO title, description and social image, and can now
be hidden from search engines with a single switch. That is what you want for
thank-you pages and campaign landing pages: they stay reachable to anyone with
the link, but stop competing with the pages you actually want ranking. On a
multi-language site, the SEO title and description can be written per
language.
Behind the scenes, more of your site now reaches search engines. Your sitemap
previously listed only the pages you authored by hand; it now includes every
club, plan, court, coach, news post, class and event page your site serves, so
a news post you publish gets found instead of sitting unlisted. News articles
and events also describe themselves to Google in a form it understands, which
is what lets an event show its date, place and price directly in search
results.
All of it is available over the platform API and to AI assistants, so an
assistant can write a page and its SEO in one pass - and check what the
published page will actually say before you make it live.
## Book deliveries by scanning the supplier invoice
Upload a supplier invoice, let 1club read the line items, review them, and post once - stock goes up and the expense is recorded with the invoice attached.
Receiving goods is now a single document instead of a line-at-a-time chore.
Open Products > Deliveries, attach the supplier invoice (photo or PDF), and
1club reads the supplier, invoice number, date and every line item off it.
* Everything it reads lands in an editable grid, so you review and correct
rather than retype. Adding a delivery by hand uses the same grid - scanning
just fills it in for you.
* Items are matched to your catalogue by barcode, article number or name, and
anything you match by hand is remembered for that supplier: the next invoice
from them comes in already matched.
* A product you have never stocked can be created straight from the invoice
line, with its cost price already filled in.
* Case quantities are handled - tell 1club how many items are in a pack and it
books the individual units, not the boxes.
* Post once and both sides are done: stock rises at the club, and an expense is
recorded in the cash registry with the invoice attached to it.
A posted delivery is a permanent record. If something was wrong, void it -
1club writes reversing stock entries and reverses the expense, leaving the
original intact for the audit trail.
## Export to Excel
Every export and report now downloads as a real Excel workbook - dates, amounts and phone numbers arrive as themselves, not as text.
Exports used to come out as CSV only. That opens in Excel, but not always the
way you want: in languages where Excel expects a semicolon between values, a
comma-separated file lands entirely in the first column, and everything in it
arrives as text - so amounts will not add up, dates will not sort, and a phone
number can lose its leading "+".
Every export now offers Excel (.xlsx) alongside CSV. Pick the format in the
export wizard, or from the Export button on any report or dashboard. Excel is
the default; CSV is one click away whenever another system needs it.
In an Excel export the cells carry their real types. Amounts are numbers you
can sum and format as currency, dates are dates you can sort and filter by,
and identifiers that only look like numbers - phone numbers, postal codes,
invoice references - stay exactly as they were entered. The header row is
bold and stays visible while you scroll, and columns are sized to their
contents, so the file is ready to read the moment it opens.
The accounting export follows the same choice. As CSV it stays the ZIP of
separate files your accountant's import tool expects; as Excel it becomes one
workbook with a sheet per document type - customers, sales receipts, invoices,
credit memos - plus the read-me explaining what to import and in what order.
Very large exports stay on CSV: an Excel workbook is assembled in one piece,
so above 50,000 rows the export asks you to narrow the date range or take the
CSV instead.
## Manage your shop catalogue over the API
The platform API and the MCP servers can now read and manage your products and their categories, and report how many of each are left to sell - so an AI assistant or your own tooling can reprice an item or add a category.
Until now the platform API could see what you sell as memberships, but not what
you sell over the counter. It can now work with your shop catalogue.
Search products by name, SKU, or barcode, create and reprice them, group them
into categories, and take off the till anything you no longer sell. Everything
an integration writes goes through the same rules the admin dashboard uses, so
a barcode already on another product, package, pass, or access card is refused
rather than quietly shadowing it at the till.
Each product also reports how many can still be sold, per club - including for
products assembled from a recipe, where the answer comes from the scarcest
ingredient. Stock itself stays where it is managed today: booking deliveries
in, correcting counts, and removing a product outright remain in the admin
portal.
Connected AI assistants get the same reach through new MCP tools, so you can
ask Claude or ChatGPT what is running low across your clubs, or have it fix a
price.
## Staying signed in through brief service interruptions
Short-lived server or connection problems no longer end your session - you stay signed in and the page keeps working.
Some staff were being returned to the sign-in screen while they were still
working, sometimes repeatedly through the day, with the message that their
session had expired.
Sessions renew quietly in the background every few minutes. If that renewal
couldn't complete - a brief database or connectivity problem, or a routine
deployment - it was treated the same as a session that had genuinely ended, and
you were signed out. Now a renewal that simply didn't finish is retried, your
session is kept, and only a session that has really ended sends you back to
sign-in.
Two related causes are fixed alongside it: a browser that had an out-of-date
copy of its session could get stuck in the same loop after every sign-in, and
staff working across several tabs no longer lose their session when the tabs
renew at the same moment.
## Build and edit your website with Claude
Connect Claude or another AI assistant to 1club and have it build your website - pages, sections, navigation, images and styling - with nothing going live until you publish.
Your website is now something you can ask for rather than build by hand. Connect
an AI assistant to 1club and describe what you want: "add a personal training
page with pricing and a booking button", "rewrite the homepage hero for our new
location", "put the class timetable in the footer navigation". The assistant
reads your real site, makes the changes, and shows you the result.
**Nothing goes live until you say so.** Every edit lands on a draft. The
assistant hands you a preview link to review the work, and the live site keeps
serving your last published version until you ask to publish. If something does
go out that you'd rather undo, you can roll the site back to any earlier
published version.
You decide how far it can go. The API token you give the assistant carries
separate permissions for reading the site, editing it, and publishing it, so you
can let an assistant rebuild pages all week while keeping the publish button
yours. It also works with your real content: the assistant picks images from
your own media library rather than inventing stock photos, and it can write and
update your posts, FAQs and reusable content blocks.
This runs on the same documented API the assistant uses, so anything an
assistant can do you can also script yourself - a bulk page migration, a
seasonal timetable refresh, or a nightly sync from your own systems.
[Learn more](/mcp/overview)
## Cancellation policies for events
Set refund terms for your events, and let members cancel their own tickets within them.
Booking policies can now be scoped to events. Pick "Events" when you create a
policy, add your refund steps, and every event follows those terms: full refund
up to a week out, half up to two days, nothing after that. Leave the event list
empty for a policy that covers all your events, or name specific events to give
a sold-out gala its own non-refundable terms.
Until you set terms for an event, its tickets stay in your hands: members are
told to contact you rather than being offered a refund the club never agreed
to. Once a policy is in place, members see exactly what cancelling returns and
can release their tickets themselves. Either way, cancelling a multi-ticket
purchase releases every ticket in it, so the seats go back on sale together.
Event policies carry cancellation terms only. Booking and check-in settings
stay with your other policies, since event tickets do not use them.
## Area blocks
Close a court, or a whole club, for a set period - and have every calendar and booking channel respect it.
Courts can now be closed for a specific window instead of being switched to
maintenance indefinitely or blocked out with a placeholder booking.
Pick a court and a period, add a reason ("Resurfacing", "Camp", "Public
holiday"), and the block appears on the schedule as a greyed-out block on
that court's lane. The times default to that day's opening and closing hours,
so closing for a day takes two clicks. Closing the whole club is a single
entry rather than one per court, and courts added later are covered by it
automatically.
A block stops new bookings everywhere the slot is offered - the admin
schedule, the member portal and app, the marketplace, and the API - and it
keeps working with your existing opening hours rather than replacing them.
Bookings already on the books are not cancelled, so you stay in control of
who to move. Staff with the right permission can still record a booking
inside a closed period when they need to, for example when entering a session
that already happened.
Partners and integrations can read blocks over the API to stop selling a
closed court, and write them when migrating a schedule across from another
system.
## Scan barcodes at the point of sale
Plug a barcode scanner into the till to add items to an order, or scan a member card to see at once whether the member is valid.
Any keyboard-style USB or Bluetooth scanner gun works at the point of sale.
There is nothing to install, nothing to pair inside 1club, and no scan mode to
switch on. The till listens for a scanner the whole time it is open, wherever
the cashier's hands are.
Scan a product and it drops into the order, ready to charge. Membership plans,
passes, and packages can carry a barcode as well, so a stickered day pass rings
up exactly the way a drink does. A code means one thing inside your
organization: if you try to save a barcode that is already on another item or on
a member card, the save is refused and you are told what already holds it.
Scan a member's card and the till answers the question the desk actually asks:
may this person be let in right now. You get one of three verdicts, each with its
reason. Valid means they hold a membership the desk can admit them on. Needs
attention means they are a current member but nothing they hold is
straightforwardly usable, for example the sessions are spent or money is owed.
Not valid means the card resolves to nobody usable. From the same panel you can
add that member to the order in one click, or open their profile.
Scanning a card is a lookup, not a check-in. Nothing is written against the
member, no session is spent, and no attendance is recorded, so admitting someone
stays a separate and deliberate step.
[Learn more](/pos/point-of-sale)
## Connect ChatGPT and Claude with one secure sign-in
Staff can connect supported AI assistants to 1club, choose an organization, and decide exactly what the assistant may change - without copying API tokens.
1club's MCP connection now supports the standard OAuth sign-in flow used by
ChatGPT, Claude, and compatible assistants. Sign in with your 1club account,
select the organization to connect, review the requested access, and approve.
You control what the assistant can do. Approving a connection grants read
access only. Anything that changes your data - creating classes, editing
plans, cancelling bookings, recording attendance - is a separate tick box on
the approval screen, off by default, so an assistant only gets the permissions
you deliberately hand it.
Connections stay organization-scoped, and you can revoke one at any time from
Settings > Artificial Intelligence > AI assistants (MCP).
## Multiple websites per organization
Run more than one website from a single account, each on its own domain or subdomain.
An organization is no longer limited to one website. Build a second site for a
shop, a brand, a location or a language, and point it at its own domain
(`shop.example.com`) or an entirely separate one - each site gets its own pages,
design, SEO settings and publish history.
Everything operational stays shared: the same members, plans, classes,
instructors and media library sit behind every site, so a visitor who books on
one is the same member you already have. Only the pages and the design are
separate.
Switch between sites from the website editor's header, duplicate an existing
site to start a variant from a working page tree, and choose which site each
custom domain opens on the Domains page. One site is your default - it is where
email links and your `1club.me` address point.
## Connect AI assistants to your gym with MCP
Claude, ChatGPT, and other AI assistants can now connect to 1club and work with your real schedule, availability, bookings, and revenue.
1club now runs Model Context Protocol servers, so an AI assistant can call
1club directly instead of guessing. Connect Claude or ChatGPT once, and you can
ask things like "which classes next week still have space?" or "book court 2 on
Thursday at 18:00" and get an answer from your live data.
There are two servers, each with its own address. The admin server works with
one organization's own data. Add its endpoint as a custom connector in your
assistant, sign in to 1club when prompted, choose your organization and
authorize the connection - there is no API token to create or copy. The
discovery server searches the public marketplace and needs nothing at all. An
assistant connected to one never sees the other's tools.
What an assistant can reach today: your classes, your bookable areas and when
they are free, your gyms, your published website content, and your billing
transactions. It can also create and cancel bookings when you grant it write
access.
Classes come with their rosters, so you can ask who is in a session and get
names back - including when two groups run at the same hour, which an assistant
can now tell apart.
You stay in control of who has access and how much it can do. Approving a
connection grants read access only; anything that changes your data is a
separate tick box on the approval screen. Settings > Artificial Intelligence >
AI assistants (MCP) lists every connected assistant, who authorized it, what it
is allowed to do, and when it was last used. Revoking one there cuts its access
off immediately.
[Learn more](/mcp/overview)
## Import your product catalog from a spreadsheet
You can now bulk-import products from a CSV, including price, VAT rate, barcode, category, cost, and opening stock for a chosen location.
The data import tool now supports products. Export your existing product list
to CSV and upload it the same way you import members or memberships - the
importer detects the columns (including Bulgarian headers like "Име артикул",
"Продажна цена", "ДДС %" and "Количество") and maps them for you.
Each imported product can carry its own VAT rate. That rate is created once and
then used at the point of sale, so a product is always taxed at the correct rate
regardless of its revenue account. Products also gained a cost price (for margin
reporting) and a product code (SKU) alongside the existing barcode.
If your file has a quantity column, choose a location during the import and those
quantities are recorded as opening stock for that location. Products without a
quantity are imported without stock. Duplicate products (matched by barcode, then
name) are skipped so you can safely re-run an import.
## Cash registry & expense management
Track the cash in your drawer, record expenses, and see where the money goes - cash payments are captured automatically.
You can now manage cash and expenses right inside 1club. Turn on the new
Cash module in Settings and each club gets a cash drawer that always knows
how much cash it should hold.
* Cash payments you take for classes, memberships, and products are added to
the drawer automatically - no double entry.
* Record expenses (rent, instructor pay, supplies, water delivery, and more)
against editable categories, with an optional receipt photo. Non-cash
expenses like card or bank transfer are tracked for reporting without
touching the drawer.
* Add or remove cash for anything else (floats, deposits, pay-outs).
* Optionally count the drawer at any time - 1club compares your count to the
expected balance and records any over/short so the books match reality.
* An overview dashboard shows cash on hand per club, cash in vs out over time,
and expenses by category.
Every entry is kept as a permanent record; corrections are made with a
reversing entry rather than editing history, so the trail is always auditable.
## Social booking links
Generate tagged booking links for your social channels and see which platforms drive bookings.
Turn your social posts into trackable booking links. Pick a destination and a
platform such as Instagram, Facebook, TikTok or YouTube, choose whether the post
is organic or paid, and optionally name a campaign. 1club builds a ready-to-share
link with the right UTM tags attached, and you can add your own custom sources too.
When someone books after following one of these links, the booking is attributed
to that source, so your bookings analytics can show which channels and campaigns
actually bring members in.
[Learn more](/settings/integrations/social-booking)
## Buy packages and products through the signup form
Signup/purchase forms can now sell packages and add-on products end to end - the buyer checks out and pays through the standard flow and gets an order confirmation email.
Purchase forms that include packages or add-on products now complete the
sale: after signing up, the buyer is taken through the standard order
checkout and pays the same way as anywhere else on the platform (card or
myPOS), and free/zero-cost orders complete instantly. When the order is
paid, they receive an itemized order confirmation email. Previously a
package/product selection created an order that was never charged and sent
no confirmation. Plan purchases are unchanged.
## WhatsApp as a messaging & notification channel
Send and receive on WhatsApp across the inbox, automations, and system notifications like booking confirmations.
WhatsApp is now a first-class channel everywhere you message members. Reply to
and start conversations from the inbox, add WhatsApp alongside email, SMS and
in-app in your automations, and let members receive system notifications - such
as booking confirmations and cancellations - on WhatsApp.
Connect your WhatsApp Business number, then map each notification and automation
to one of your Meta-approved templates so messages still reach members outside
WhatsApp's 24-hour reply window. Members are messaged on WhatsApp only when they
are reachable and have opted in, and staff can turn WhatsApp on for their own
alerts.
## Real-time availability and webhooks for channel partners
Partner platforms can now read a whole club's open slots in one call and receive webhooks the moment a court's availability changes, so their calendars stay live.
Partners selling your courts through the channel integration can now keep a
truly live calendar.
The availability API returns ready-made open slots - with remaining capacity
and the club's timezone - for a single court or a whole club in one request,
so partners no longer have to stitch together operating hours and busy
intervals themselves.
New availability webhooks push a signed event within seconds whenever a court
is booked, freed, or blocked on any channel - your own tools, another partner,
a class, or an event. Partners register an endpoint under Settings → APIs and
MCP, verify the signature, and re-pull the affected slots. A reconciliation
feed lets them catch up exactly after any missed delivery, so availability
never drifts and double-bookings are still impossible.
## Self-service Check-in App
Members can now check themselves into classes or the gym from a dedicated self-service screen at the club.
A new touch-first Check-in App runs on a tablet or screen at your club, next to
the existing kiosk. Members find themselves by name (or with their QR code or
PIN, when enabled) and are checked in instantly - a green screen confirms the
check-in, an amber screen means they are in but a payment is still due, and a
red screen sends them to the front desk.
* Members can hold the QR code from their 1club app up to the tablet's camera
and are checked in on the spot, without touching the screen.
* Members with an upcoming booking are checked in the moment they identify
themselves.
* Without a booking, members pick from the classes starting in the next hour;
the club's booking policy decides whether their membership covers it or an
unpaid payment is recorded for the front desk.
* Choose the journey per club: identify first, or pick a class first and tap
your name on the class list.
* Open Gym mode checks members in on their general membership with no class
list at all.
Configure everything under Settings, Member Experience, Check-in App: enable it
per club, copy the device link, toggle QR and PIN identification and Open Gym
mode, and drag to reorder the user journey.
## Events and ticketing
Create ticketed events, sell multiple ticket types, and check attendees in.
You can now run events end to end. Set up an event with one or more ticket types,
choose whether attendees pay online or on arrival, and let people buy several
tickets in a single order across the portal, website, marketplace and mobile app.
Live events reserve their area and instructor on the schedule so they never
double book, and staff can check attendees in and collect payment from the
attendee list. There is even an AI assistant to help draft an event.
[Learn more](/events/events)
## Instructor pay rates and shifts
Set how each instructor is paid and plan their shifts, with earnings visible to both of you.
Managing instructors now covers both pay and time. Define how each instructor is
paid, as a fixed amount or a percentage, and choose whether they are paid only for
paid bookings and whether the percentage is on gross or net revenue. Instructors
get an earnings report with a monthly breakdown and a compensation card on their
dashboard. Scheduling is built around shifts too, with rotations and roster merged
into one view and coverage shown right on the scheduler calendar.
[Learn more](/staff/instructors)
## Member mobile app
Members can now book, pay and manage their membership from a native iOS and Android app.
The 1club member app is here for iOS and Android. Members can browse the schedule
and book classes and courts, buy plans and pay with Apple Pay, manage memberships,
and chat with your club from a built-in inbox. Push notifications keep them up to
date on bookings and messages, sessions stay signed in securely between visits,
and the whole app follows the member's language and your club's time zone.
[Learn more](/members/overview)
## myPOS payments
Take payments through myPOS, including shareable payment links for memberships and orders.
1club now integrates with myPOS as a payment provider. Clubs can connect myPOS,
switch between test and live mode, and collect payment for memberships and orders
through a hosted checkout. You can also send a shareable payment link so a member
can pay from anywhere, with the checkout shown in the member's own language.
[Learn more](/settings/integrations/mypos)
## Automations
Set up automated member messages from ready-made blueprints, with suggestions as you work.
Automations replace campaigns with a simpler, always-on way to reach members.
Start from suggested blueprints tailored to the page you are on, insert member
and booking variables into your message with a picker, and send across your
chosen channels. Built-in automations, such as a review request and a membership
expiration reminder, come ready to use, and you can adjust the timing, channels
and wording of each or switch it off.
[Learn more](/marketing/automations)
# Events
Source: https://docs.1club.ai/events/events
Create one-off ticketed events, draft them with AI, sell tickets online or at the front desk, take pay-on-arrival reservations, and promote events to members in one click.
An event is a one-off happening your members can discover and book - a workshop, seminar, tournament, open day, or party. Gym owners and managers create and promote events, the front desk sells tickets and checks people in on the day, and members book from the app, portal, or website.
## When to use it
Events turn a special occasion into something members can find, pay for, and show up to, without you juggling sign-up sheets, bank transfers, and a separate email blast. Each event gets its own public page, its own ticket types, and an attendee list you check in from.
Typical uses:
* **A tournament with tiers** - a padel tournament with capped **Player** entries paid up front and cheaper, unlimited **Spectator** tickets, where one member can buy seats for friends in a single order.
* **A day where people pay at the door** - a martial-arts grading day that students reserve now, with the front desk collecting payment as they check in.
* **A workshop you want filled** - a Saturday yoga workshop drafted by the AI assistant, announced to members with a prefilled message, and featured on your app, portal, and website.
See [Worked examples](#worked-examples) for how to set up each of these.
## Events vs classes
Use a [class](/schedule/classes) for recurring group activities with a single per-seat price and a waitlist. Use an event for one-off occasions that need their own page, multiple ticket tiers, or promotion:
| | Event | Class |
| - | - | - |
| Recurrence | One-off | One-off or recurring |
| Pricing | Per ticket type (e.g. General admission, VIP) | Single price per seat |
| Capacity | Per ticket type | Per class |
| Group purchase | Buyer + guest seats, mixed ticket types | Party booking, capped by max party size |
| Waitlist | Not available | Available when full |
| Promotion | One-click **Promote** to a prefilled message | **Notify participants** (booked members only) |
## The list page
The list page is the **Events** tab of the **Events** section in the left rail (`/schedule/events`). The detail page is where you publish, promote, watch ticket sales, and run check-in on the day.
The **Events** section is on by default. If it's missing from the rail, check that **Show Events** is on under **Settings** > **Events** - see [Events section](/events/overview).
The events list has five tabs with live counts: **All**, **Draft**, **Live**, **Past**, and **Cancelled**. Search filters by title or location. Each row shows the cover thumbnail, date and time, booked count, and status.
Row actions depend on status: **Publish** (draft), **Unpublish** (live, only while nothing is booked), **Promote** (live), **View attendees**, **Copy link** (the event's public page URL), **Edit**, **Cancel event**, and **Delete**.
## Creating an event
Click **Add Event** on the list page, or choose **Add** > **Event** on the [Schedule](/schedule/calendar) calendar. The dialog has four sections:
* **Details** - **Title**, **Summary** (a short teaser shown on event cards and in notifications), **Description** (rich text shown on the event page - what to expect, who it's for, what to bring), and **Cover image**.
* **When & where** - start and end time (entered in the gym's timezone; multi-day events are supported), a free-text **Location** line, and the gym. Leave the gym empty for an organization-wide or off-site event.
* **People & sports** - **Instructors**, **Areas**, and **Sports**. These are labels for the event page and the schedule, not reservations: assigning an area does not block that area from being booked separately at the same time, and instructors are not marked busy.
* **Booking** - the **Members can book a spot** toggle, [tickets](#tickets), [payment collection](#payment-collection-pay-online-or-reserve), and the **Featured** toggle.
Events also have a visibility setting: **Public** (anyone can see it), **Member only** (only signed-in members), or **Private** (admins only).
New events start as drafts. Members never see a draft - publish when you're ready.
## Drafting events with AI
The admin AI assistant can plan and draft an event for you - ask it something like "plan a Saturday morning yoga workshop". The draft is grounded in your real data, not invented: your gyms, sports, instructors, areas, recent event titles (to avoid near-duplicates), and a suggested slot on the next free weekend morning.
The cover image comes from one of three sources, in order of preference:
1. **Your media library** - real photos of your gym are always preferred.
2. **Stock photo search** - the assistant searches a stock photo catalog (powered by Pexels) for a concept-matched cover. The photo you approve is imported into your own media storage, so the event never depends on a third-party link.
3. **A curated stock catalog** - a built-in fallback set covering common concepts (yoga, HIIT, running, dance, community events, and more).
AI drafts are deliberately conservative: they are created as private drafts with a free ticket, they always target a future date, and the assistant keeps at most one AI draft at a time - if one exists, it offers to refine it instead of creating another. You review, edit, and publish; the assistant never publishes for you.
When your organization has no upcoming events, the admin home and schedule pages show a suggestion card that starts this flow.
## Tickets
Tickets set the booking price - members pick one when they book. A bookable event needs at least one active ticket. Each ticket has:
* **Ticket name** - for example "General admission", "Session + brunch", "VIP".
* **Price** - in your organization's currency. Set `0` for a free ticket; free tickets skip payment entirely.
* **Capacity** - how many of this ticket can be sold. Leave empty for unlimited spots.
* **Description** - optional, shown in the ticket picker.
* **Active** - deactivate a ticket to stop new sales without touching existing bookings.
Add several tickets to offer price tiers. Members see each ticket with its price and remaining spots; a ticket at capacity shows **Sold out** and can't be selected. Cards and listings show the price as "from X" (the cheapest active ticket).
A few rules to know:
* The price a member pays is captured at purchase - editing a ticket's price later does not change existing orders.
* A ticket that has bookings can't be deleted; deactivate it instead.
* Each ticket can book revenue to its own revenue account for tax and reporting.
* Ticket names and descriptions (and the event title and summary) can be translated per locale.
### Group orders and guest seats
A member can buy several tickets in one order - including a mix of types, like two General admission and one VIP - with up to 10 seats per ticket type. The buyer gets one named seat; every additional seat becomes an anonymous guest registration tied to the buyer's order. Guests appear on the [attendee list](#attendees-and-check-in) as "Anonymous (buyer name)" so you always know who brought them. A member can hold only one named seat per event.
## Payment collection: pay online or reserve
For paid tickets, the **How is payment collected?** setting controls when money changes hands:
* **Pay online** - members pay by card when they book.
* **Pay on arrival** - members reserve their spot and pay in person at the event. The booking is created with an outstanding balance that staff collect at check-in.
* **Let members choose** - members pick between paying online now or paying on arrival at checkout.
Pay-on-arrival balances are settled with the **Check in & collect** action on the attendee list, which records the payment and checks the attendee in at the same time.
## Publishing, unpublishing, and cancelling
* **Publish** moves a draft to **Live**. Live events appear on member surfaces and become bookable. The detail page of a draft event has a **Publish** button in the header.
* **Unpublish** moves a live event back to draft, but only while it has no active bookings. Once seats are booked, cancel instead.
* **Cancel event** cancels the event and all of its active bookings through the standard booking cancellation flow. Cancelled events move to the **Cancelled** tab and disappear from member surfaces.
* **Delete** is only available for events that never had bookings.
## Promoting an event
On a live event, click **Promote**. This opens [Compose](/inbox/compose) with a ready-to-send message: the cover image, the event title, a formatted when-and-where line in the gym's timezone, the summary, and a **View event** button that links to the event's public page.
You choose the audience (all members, a filtered segment, or individual contacts) and press **Send** yourself - nothing goes out automatically. In the mobile app, the **View event** button opens the event screen directly in the app.
## Featured events
Turn on **Featured** to spotlight an event across member surfaces:
* The mobile app home screen shows a **Featured events** row; tapping **See all** opens the full events list.
* The member portal highlights featured events at the top of the events page.
* The website's **Events** section defaults to a featured-events rail (see [Website](/marketing/website)).
Featured placements are self-hiding: when no live, upcoming, featured events exist, the rails don't render. Feature your next big event, and unfeature it after it happens.
## Selling tickets at the front desk (POS)
The **Events** tab in Point of Sale lists your live, bookable events sorted by start time, each card showing the cover, date, and number of ticket tiers. Tap an event to pick a ticket tier, set the quantity, and add it to the order alongside any products. The selected contact gets the named seat; additional seats become guest registrations, exactly like online orders.
Complete the order with any POS payment method - card, cash, or terminal. Collecting an existing pay-on-arrival reservation is done with **Check in & collect** on the event's attendee list, not by ringing up a new POS sale.
## Attendees and check-in
The event detail page shows per-ticket sales ("N sold") next to each ticket and a booked-versus-capacity summary, above the **Attendees** grid. Attendees are sorted newest-first, and clicking a row opens the underlying [booking](/schedule/bookings).
Each row shows the attendee, the ticket they hold, booking date, payment status, and check-in status. Guest seats read **Anonymous (buyer name)** with the buyer's email underneath, and the buyer's own row carries a **+N guests** chip so a party reads as one order.
Row actions:
* **Check in** - marks a paid (or free) attendee as present.
* **Check in & collect** - for unpaid pay-on-arrival attendees: opens the payment method picker, settles the balance, and checks them in, in one step.
* **Assign** - attaches a real contact to an anonymous guest seat, turning it into a named attendee. Useful when a guest walks up and you want their visit on their profile. A contact who already has a seat on the event can't be assigned a second one.
## Running a tournament on an event
When competitions are switched on under **Settings** > **Events**, the event detail page has a **Tournament** line. Use **Set up a tournament** to attach a knockout or round-robin draw to the event, or **Manage the draw** to open one that already exists. Registrations stay on the event; the entry list, draw, and scores live on the tournament page. See [Tournaments](/events/tournaments).
## Events on the schedule
Live events appear on the [Schedule](/schedule/calendar) calendar as read-only cards. A single-day event placed at a gym renders in its areas' lanes in day view (or under its instructors when it has no areas). Multi-day and organization-wide events render as chips in a banner above the grid. Click a card to open the event detail page.
Remember that area and instructor assignments are visual placement only - an event "in Court 1" does not reserve Court 1. If you need the space held, keep an eye on bookings for that area during the event window.
## Where members see events
* **Member portal** - the events page lists upcoming live events with cover, date, location, and price; each event has its own detail page with the description, instructors, ticket picker, and booking flow. The event's **Copy link** URL points here.
* **Mobile app** - the home screen shows the featured events row, the events list shows everything upcoming, and an upcoming event booking appears on the Today card with the event cover, title, and ticket count. Event links in messages open in the app.
* **Website** - the **Events** section (a featured rail or a full list) can be added to any page of your site, and new template sites include it on the home page. See [Website](/marketing/website).
* **Visibility** - **Member only** events require a signed-in member; **Private** events never appear on member surfaces.
## Current limits
* Events don't recur - create each occurrence separately, or ask the AI assistant to draft the next one.
* There is no event waitlist. When every ticket is sold out, booking closes.
* Event tickets are always paid directly - membership plans and external programs don't cover them.
* The kiosk doesn't sell event tickets; use the [POS](#selling-tickets-at-the-front-desk-pos) for on-site sales.
* The event description is single-language; only the title, summary, and ticket names/descriptions are translatable.
## Worked examples
### Padel tournament with tiers
Create the tournament event, add two tickets - **Player** (capped at your draw size) and **Spectator** (unlimited, lower price) - and set payment to **Pay online** so entries are confirmed and paid up front. Promote it to your padel players and watch the per-ticket "N sold" counts on the detail page.
### AI-drafted yoga workshop
From the suggestion card (or by asking the assistant), draft a "Saturday morning yoga workshop". It arrives as a private draft with a free ticket, a future date, and a cover image from your media library. Review the description and time, add a paid ticket if you want, and publish.
### Martial-arts grading day, pay on arrival
Set the grading event's ticket to **Pay on arrival** so students reserve now and pay at the door. On the day, use **Check in & collect** on the attendee list to take payment and mark them present in one step.
### Club open day you want everyone to see
Make the event **Public** and turn on **Featured** so it surfaces on the app home rail, the portal, and the website. Use **Promote** to email members with a prefilled message, then unfeature it once the day has passed.
## Tips & best practices
* **Reach for an event, not a class, when you need tiers, a landing page, or promotion.** Use a class for recurring, single-price group activities with a waitlist.
* **Free tickets skip payment entirely** - ideal for community sessions and RSVPs.
* **Feature your next headline event, then unfeature it.** Featured rails self-hide when nothing qualifies, so keeping this tidy keeps member surfaces clean.
* **Assign guest seats at the door.** Turning an anonymous guest into a named contact puts the visit on their profile and helps future marketing.
* **Remember area assignment is visual only** - an event shown "in Court 1" does not hold Court 1; block it with a booking if you need the space reserved.
## Troubleshooting
**Issue**: The **Unpublish** action is missing.
**Solution**: You can only unpublish a live event while it has no active bookings. Once seats are booked, use **Cancel event** instead.
**Issue**: I can't delete a ticket.
**Solution**: A ticket with bookings can't be deleted. Set it to inactive to stop new sales while preserving existing orders.
**Issue**: Members can't see my event.
**Solution**: Check it's **Live**, not a draft, and that visibility isn't **Private** (or **Member only** for logged-out visitors).
**Issue**: A member's plan didn't cover the ticket.
**Solution**: By design - event tickets are always paid directly; memberships and external programs don't cover them.
## Related
* [Schedule](/schedule/calendar) - Where live events render on the calendar.
* [Bookings](/schedule/bookings) - Every ticket is a booking row; the detail page shows the event and ticket.
* [Classes](/schedule/classes) - For recurring group activities with waitlists.
* [Compose](/inbox/compose) - The messaging page the **Promote** button prefills.
* [Website](/marketing/website) - The Events section on your public site.
* [Tournaments](/events/tournaments) - Draws, entrants, and scores for a tournament held at an event.
# Matches and ratings
Source: https://docs.1club.ai/events/matches
Record competitive match results, track per-sport player skill with the 1club Rating (Glicko-2) system, and read the ratings leaderboard under Events.
**Matches** let you record the outcome of competitive games between members and turn those results into a per-sport skill rating for each player. Every recorded match is scored with the Glicko-2 algorithm, so a player's rating reflects not just wins and losses but the strength of their opponents and how confident the system is in the estimate. Staff log the games and review the rating history that comes out of them.
## When to use it
Ratings give your racket-sports players a per-sport measure of skill, and a ranking you don't have to keep in a spreadsheet. Matches tied to a real booking count in full, casual results count at half weight, and friendly games are kept for the record without touching anyone's rating.
Typical uses:
* **Ranked singles on a booked court** - two tennis members play a best-of-three on a reserved court, and both ratings update the moment you save the score.
* **Padel doubles with mixed experience** - a beginner paired with a regular sees their own rating move more than their partner's, because the change is shared by rating uncertainty.
* **A gym ladder** - you record every game of an informal pickleball ladder as a ranked singles match, and sorting members by their pickleball rating gives you the ladder.
* **A casual result reported at the desk** - a friendly tennis game with no booking is recorded as ranked at half weight, or as friendly so ratings stay put.
See [Worked examples](#worked-examples) for each of these step by step.
Matches and ratings are separate from **progression levels** (belts, tiers, skill grades). Recording a match never changes a member's progression assignment, and vice versa. See [Member profile](/members/member-profiles) for progression.
## How ratings work
Ratings use **Glicko-2** (Glickman, 2013). Each player has one rating per sport, so a strong tennis player who is new to padel carries two independent numbers. Three values describe each rating:
* **Rating** - The skill estimate. Everyone starts at **1000**.
* **Rating deviation (RD)** - How uncertain the system is about the rating. New players start at **200**. A high RD means the rating can still move a lot; as a player logs more matches, RD shrinks and the rating settles.
* **Volatility** - How erratic a player's recent results have been. Starts at **0.06**.
A player's rating row is created automatically the first time they appear in a match, seeded with those defaults. Every rated match also increments their **games played** count and records the date of their last match.
### What moves a rating
* Only **ranked** matches change ratings. Friendly matches never do.
* **Beating a stronger opponent** raises your rating more than beating a weaker one; the reverse is true for losses.
* **New players move fast.** Because their RD starts high, early matches produce large swings (roughly plus or minus 80 points on the first ranked match). After a handful of matches, RD drops and results move the rating in smaller increments.
* **Verified matches** (linked to a booking) apply the full change; **unverified** matches apply half.
* A **win** counts as 1, a **loss** as 0, and a **draw** as 0.5 from each player's perspective. The set-based dialog always resolves to a winner, so recording a true draw is a rating-math capability rather than something the standard dialog produces.
### Singles vs doubles
* **Singles** (tennis, pickleball) run standard head-to-head Glicko-2 between the two players.
* **Doubles / teams** (padel) are computed by first averaging each side into a single team rating, running Glicko-2 team-versus-team, then distributing the team's change back to its members. The share each player absorbs is weighted **inversely by their RD**: a less-established player (high RD) moves more, while a seasoned partner (low RD) barely budges. This keeps a strong veteran from swinging wildly because they happened to partner with a beginner.
## Turn on competitions
Matches live at **Events** > **Matches**, next to **Tournaments** and **Ratings**. All three appear once you turn on competition features.
Matches, tournaments, and all rating behavior are gated behind one organization switch. Until it is on, there are no **Tournaments**, **Matches**, or **Ratings** tabs, no **Matches** tab on member profiles, and the match API returns a "not enabled" error.
To enable it:
1. Go to **Settings** > **Events**.
2. Make sure **Show Events** is on. Competitions run on events, so the competitions switch is disabled while Events is off.
3. In the **Competitions** card, turn on **Enable competition features**.
Turning it on adds the **Tournaments**, **Matches**, and **Ratings** tabs to the **Events** section, adds a **Matches** tab to member profiles, and starts computing ratings as ranked matches are recorded. Changing the switch requires the **settings.manage.organization** permission.
## Supported sports
Match recording and rating are available for the sports that have a scoring schema:
| Sport | Format | Scoring |
| - | - | - |
| Tennis | Singles (1 player per side) | Best-of sets |
| Padel | Doubles (2 players per side) | Best-of sets |
| Pickleball | Singles (1 player per side) | Games modelled as sets |
The number of players per side comes from the sport configuration and is enforced when a match is saved. Other racket sports such as squash, badminton, and table tennis exist elsewhere in 1Club (for example as area types), but they are **not** currently available for match recording or rating - only the three sports above have a result schema. If you need one of them scored, treat that as a future request rather than something you can log today.
## Record a match
You can start a match from the **Add match** button on the Matches page, or from the **Matches** tab of a member's profile. Recording a match requires the **members.create.organization** permission; editing or disqualifying one requires **members.update.organization**.
In the **Add match** dialog:
1. **Sport** - Pick the sport. The dialog restricts this to the supported sports and uses the sport's team size to decide how many player slots each side gets.
2. **Type** - Choose **Ranked** or **Friendly**:
* **Ranked** matches update player ratings.
* **Friendly** matches are recorded but leave every rating untouched.
3. **Played at** - Set the date and time the match was played. Defaults to now.
4. **Club** - Choose the club the match belongs to. This is required and anchors the match to your organization.
5. **Team 1** and **Team 2** - Select the players on each side. Each side must be filled to the sport's team size (one player for singles, two for padel doubles), and no player may appear on both sides.
6. **Set scores** - Enter the score for each set (up to five). The two scores in a set cannot be tied. Use **Add set** for additional sets.
7. The dialog shows the derived winner ("Winner: Team 1/2") below the scores. The winner is computed automatically from how many sets each side took - you never pick it manually.
8. Save the match.
Only contacts that are linked to a registered user account can be added to a match, because a rating attaches to a user, not to a bare contact. The player picker only lists contacts that have a linked account. If a member is missing from the picker, link their account first (see [Member profile](/members/member-profiles)).
### Verified vs unverified matches
A match can be tied to a booking. When it is, the match is treated as **verified** and its rating change is applied at full weight. A match recorded without a booking still moves ratings, but at half weight, so casual, self-reported results do not carry the same confidence as a match that maps to a real court reservation. You do not need to link a booking to record a match; it just changes how much the result counts toward ratings.
### Edit a match
Open a match from the list to view its detail page, then use **Edit**. Editing reverses the match's original rating impact, then recomputes and re-applies the rating from the corrected result. A disqualified match cannot be edited.
### Disqualify a match
To void a match, use **Disqualify** from the row actions or the detail page. You must enter a reason. Disqualifying:
* Marks the match **Disqualified**.
* Reverses the rating change it applied to every participant (rating, deviation, volatility, and the games-played count).
* Records who disqualified it, when, and why.
Reverting a rating (on edit or disqualify) subtracts the delta that this specific match applied. If the same players have played later matches in the same sport, those later results are not re-derived end to end, so the correction is close but approximate. This is a deliberate trade-off - moderation events are rare.
### Matches from tournaments
Every completed fixture in a [tournament](/events/tournaments) is recorded as a **Ranked** match automatically, so tournament results move ratings and appear in the Matches list like any other match. A fixture stays unrated when a player has no linked user account, when it was a walkover or a bye, or when the sport has no scoring schema. Clearing a tournament result or the whole draw disqualifies the match it created, which reverses its rating change.
## Ratings leaderboard
**Events** > **Ratings** is the leaderboard for one sport. Pick the sport in the filters drawer; the page opens on the first rated sport your organization plays. Columns are **#** (rank), **Player**, **Rating**, **Deviation**, **Matches**, and **Last played**, ordered by rating, highest first. Click a row to open the player's member profile.
A rating belongs to the player, not to your organization: one person carries one rating per sport wherever they play on 1Club. The leaderboard shows your active members against those ratings, and a member with no rated match in that sport yet has no row. Viewing it requires the **members.read.organization** permission.
## Rating settings
The rating system is intentionally simple to configure. The only control is the **Enable competition features** switch at **Settings** > **Events** described above. The Glicko-2 constants (starting rating 1000, starting RD 200, starting volatility 0.06) are fixed platform defaults and are not per-organization settings, so ratings stay comparable wherever a player plays.
## Where ratings show up
* **Events** > **Matches** - The full match list for your organization, filterable by sport, type, and status, with a detail page per match.
* **Events** > **Ratings** - The per-sport leaderboard described above.
* **Member profile** > **Matches tab** - When 1club Rating is on, each member's profile gains a **Matches** tab listing the matches that member has played. It only appears for members whose contact is linked to a user account.
* **Member list** - Rows can surface a member's rating per sport (rating, RD, and games played) where their contact is linked to a user.
## Worked examples
### Tennis singles ranked match
Two members play a tennis match on a booked court. In **Add match** you pick **Tennis**, set the type to **Ranked**, choose the **Played at** time and the **Club**, put one player on each side, and enter the sets: `6-4`, `3-6`, `7-5`. Team 1 took two sets to one, so the dialog shows "Winner: Team 1". On save, both players' tennis ratings update head-to-head. If the winner was the lower-rated player, expect a larger jump for them and a larger drop for the favourite.
### Padel doubles ranked match
Four members play padel. You pick **Padel**, which gives each side two player slots. Assign both pairs, enter `6-2`, `4-6`, `6-3`, and save. The system averages each pair into a team rating, runs the team-versus-team calculation, then splits the result back to the four players. A beginner paired with a gym regular will see their own rating move noticeably more than their partner's, because the change is distributed by rating uncertainty.
### Pickleball gym ladder
A gym runs an informal pickleball ladder. Staff record each singles game as a **Ranked** pickleball match with the game scores entered as sets. Over a few weeks, players who consistently beat higher-rated opponents climb, and the per-sport rating on each member profile becomes a de facto ladder ranking. To publish a ladder, sort members by their pickleball rating.
### Casual game with no booking
Two members report a friendly tennis result at the front desk with no court reservation on file. If you record it as **Ranked** without linking a booking, it still updates ratings but at half weight, so the informal result nudges the numbers without dominating them. If you would rather it not affect ratings at all, record it as **Friendly**.
### Fixing a mistyped score
You recorded `6-4, 6-4` but the second set was actually `4-6, plus a third set`. Open the match, choose **Edit**, correct the sets, and save. The original rating change is reversed and the corrected result is applied in its place.
## Tips & best practices
* **Link a booking when you can.** Verified matches carry full rating weight and tie the result to a real session, which keeps ratings trustworthy.
* **Use Friendly for exhibitions and warm-ups.** Anything you do not want to affect standings should be recorded as Friendly, not Ranked.
* **Link user accounts first.** A contact without a linked account cannot be added to a match. Sort out account linking before a tournament so recording is not blocked mid-event.
* **Let new players settle.** Expect big swings in a new player's first few ranked matches. That is the system converging, not an error - RD tightens quickly.
* **Prefer Disqualify over deletion for cheating or disputes.** It reverses the rating impact and keeps an auditable reason on record.
* **Keep sports honest.** Record tennis as tennis and padel as padel; ratings are per sport, and mixing them produces a meaningless number.
## Troubleshooting
| Symptom | Likely cause | Fix |
| - | - | - |
| No **Matches** tab under **Events**, or no profile tab | Competition features are off | Turn on **Enable competition features** at **Settings** > **Events** (with **Show Events** on). |
| A member is missing from the player picker | Their contact is not linked to a user account | Link the account on the [Member profile](/members/member-profiles); only linked contacts can carry a rating. |
| "Add match" button is not visible | You lack the **members.create.organization** permission | Ask an admin to grant it; read-only staff can view but not record. |
| Cannot save - "Set scores don't determine a winner" | The sets are tied (equal sets each) or a set is tied | Add a deciding set, or fix a set so the two scores differ. |
| Cannot save - team size error | Wrong number of players for the sport | Fill exactly one player per side for singles, two per side for padel. |
| A player's rating did not change after a match | The match was **Friendly**, not **Ranked** | Only ranked matches move ratings; edit the type if it should count. |
| A ranked result moved ratings less than expected | The match was unverified (no booking) so it applied at half weight | Link the match to a booking for full weight. |
| The sport you want is not in the list | Only tennis, padel, and pickleball have a scoring schema | Other sports are not currently supported for match recording. |
| Cannot edit a match | The match is disqualified | Disqualified matches are locked; record a new match instead. |
## Related
* [Members overview](/members/overview) - how contacts, members, and accounts fit together.
* [Member profile](/members/member-profiles) - where a member's Matches tab and account linking live.
* [Member list](/members/member-list) - the contacts grid where per-sport ratings can surface.
* [Tournaments](/events/tournaments) - knockout and round-robin draws whose results become ranked matches.
# Events
Source: https://docs.1club.ai/events/overview
The Events section: bookable events, plus tournaments, matches, and ratings when competitions are on. Both are switched on and off under Settings > Events.
The **Events** section in the left rail holds one-off occasions your gym runs - an open day, a workshop, a social - and competitive play: tournaments, the matches they produce, and the ratings those matches move.
## What's in the section
| Tab | What it's for | Shown when |
| - | - | - |
| [Events](/events/events) | One-off ticketed events members book, with their own page, ticket types, promotion, and check-in. | **Show Events** is on (the default). |
| [Tournaments](/events/tournaments) | Knockout and round-robin draws run on an event, with entrants, seeds, and scores. | Competitions are on. |
| [Matches](/events/matches) | Every recorded match result, ranked or friendly, including matches from tournaments. | Competitions are on. |
| [Ratings](/events/matches#ratings-leaderboard) | The per-sport 1club Rating leaderboard for your members. | Competitions are on. |
When only the **Events** tab is available, the section opens straight on the events list, with no column of tabs beside it.
## Turn the section on or off
Both switches live on one page, **Settings** > **Events** (`/settings/events`):
* **Show Events** (in the **Events** card) - on by default. Turn it off to hide the whole **Events** section from the admin menu, competition tabs included.
* **Enable competition features** (in the **Competitions** card) - off by default. Turns on tournaments, matches, and 1club Rating. It needs **Show Events** on, because a tournament is drawn on an event, so it's disabled while Events is off.
Changing either switch requires the **settings.manage.organization** permission.
## Who sees what
The **Events** and **Tournaments** tabs follow the operations permissions: instructors see them at their own scope, and creating or managing events and tournaments needs **operations.manage.club**. **Matches** and **Ratings** follow the members permissions (**members.read.organization** to view), because a match and a rating belong to a member.
## Related
* [Schedule](/schedule/overview) - Live events also appear on the calendar as read-only cards.
* [Bookings](/schedule/bookings) - Every event ticket is a booking.
# Tournaments
Source: https://docs.1club.ai/events/tournaments
Run a knockout or round-robin draw on an event: build the entry list from registrations, seed players, generate the draw, and enter scores that feed 1club Rating.
A tournament is a draw that runs on an [event](/events/events): the event handles sign-ups, tickets, and payment, and the tournament holds the entry list, the bracket or table, and the scores. Owners and managers set tournaments up, and whoever runs the desk on the day enters results as matches finish.
## When to use it
A tournament saves you the bracket sheet and the spreadsheet of scores. Registrations flow straight into the entry list, the draw is generated for you with seeds kept apart, winners advance on their own, and every completed match is recorded as a ranked match that moves the players' [ratings](/events/matches).
Typical uses:
* **A Saturday tennis knockout** - 16 members register for the event, they're imported as entrants when you set up the tournament, you seed the top four, and the bracket fills in round by round as you enter scores.
* **A padel doubles round robin** - six pairs play everyone once over an evening, and the standings table ranks them by matches won, then set and game difference.
* **A pickleball ladder night entered after the fact** - you set up the tournament on yesterday's event and enter the results from the paper sheet.
## How it works
* **One tournament per event.** The event is the occasion members sign up to; the tournament is the competition held at it. Only events that aren't cancelled and don't already have a tournament can host one.
* **Supported sports.** Tennis, padel, and pickleball - the sports 1club Rating can score. Padel draws are of pairs; tennis and pickleball draws are singles. The team size comes from the sport, not from a setting.
* **Formats.** **Single elimination** is a knockout bracket. When the field isn't a power of two, the top seeds get byes in the first round. **Round robin** pairs every entrant with every other entrant once; with an odd number of entrants, one sits out each round.
* **Status.** A tournament starts as **Draft**, becomes **Published** when you generate the draw, **In Progress** once a result is in, and **Completed** when no fixture is left to play and the event has started. **Cancelled** takes it off member surfaces until you reinstate it.
* **Ratings.** Each completed fixture is recorded as a **Ranked** match. Walkovers, byes, and fixtures with a player who has no linked user account are not rated.
## Turn on tournaments
Tournaments are part of competitions, which are off by default:
1. Go to **Settings** > **Events**.
2. Make sure **Show Events** is on.
3. In the **Competitions** card, turn on **Enable competition features**.
The **Tournaments** tab then appears in the **Events** section, and event detail pages gain a **Tournament** line. See [Events section](/events/overview).
## The tournaments list
**Events** > **Tournaments** lists every draw in your organization, across all events. Each row shows the event and gym, **Date**, **Sport**, **Format**, **Entrants**, **Played** (results entered out of total fixtures, or **No draw yet**), and status. Filter by sport or status from the filters drawer. Click a row to open the tournament.
## Set up a tournament
You can start from either side:
* On **Events** > **Tournaments**, click **Add tournament** and pick the **Event**. The picker lists events that can still host a tournament, upcoming ones first. Choose **Add Event** at the bottom to create the event without leaving the dialog.
* On an event's detail page, use **Set up a tournament** on the **Tournament** line.
The dialog asks for:
* **Club** - where the draw is played, and the gym its rated matches are recorded against. It defaults to the event's gym and is required, even for an organization-wide event.
* **Sport** - tennis, padel, or pickleball.
* **Format** - **Single elimination** or **Round robin**.
* **Match length** - **Best of 1**, **Best of 3** (the default), or **Best of 5** sets.
Saving opens the new tournament. For a singles draw, everyone registered on the event with a named attendee is imported as an entrant straight away.
You need the **operations.manage.club** permission to create or manage a tournament.
## Build the entry list
The tournament page starts with the entry list. Each row is an entrant (one player, or a pair for padel) with an optional **Seed**. A warning icon marks a player with no user account, whose matches can't be rated.
* **Import registrations** - for a singles draw, an **Import N registrations** button appears when people have registered on the event since the tournament was set up. Doubles registrations can't be paired automatically, so add each pair yourself.
* **Add entrant** - pick the player, or **Player 1** and **Player 2** for a pair. A contact can only be in the draw once.
* **Seed** - open an entrant to set their seed. Seeded entrants are kept apart in the draw, so seeds 1 and 2 can only meet in a knockout final. Leave it empty for an unseeded place; unseeded entrants are placed at random. Giving a seed somebody already holds swaps the two.
* **Remove from the draw** - takes an entrant out before the draw is generated.
## Generate the draw
When you have at least two entrants, click **Generate the draw**. A knockout shows as a bracket, one column per round (**Round of N**, **Quarter-finals**, **Semi-finals**, **Final**). A round robin shows the standings table (**#**, **Entrant**, **P**, **W**, **L**, **Sets**, **Games**) above the fixtures grouped by round.
Once the draw exists:
* The entry list is locked. To record that someone pulled out, use **Withdraw** (and **Reinstate** to undo it). A withdrawn entrant's played matches still count. A no-show for a single match is entered as a walkover.
* **Sport** and **Format** can't change. Use **Edit** to change the **Club** or **Match length**.
* **Clear the draw** removes every fixture and score, unlocks the entry list, and takes back any rating changes already applied.
## Enter results
Click a fixture to open **Record the result**. Results can only be entered once the event has started, and only when both sides are known.
* Enter the **Set scores**, one row per set, using **Add set** up to the match length. A set can't be level, and the dialog shows the winner as you type. A best-of-3 is won at two sets, so a third set after a 2-0 is refused.
* For a match that wasn't played, turn on **Won by walkover** and pick the **Winner**.
* **Clear this result** takes a score back off. In a knockout, it also removes the winner from the next round.
In a knockout, the winner moves into the next round automatically. If the winner has already played their next match, you can't change or clear the earlier result until you clear the later one first.
In a round robin, the standings rank entrants by matches won, then set difference, then game difference. If exactly two entrants are still level, their head-to-head result separates them; otherwise they share the rank.
## Other actions
The tournament page header has:
* **Edit** - the tournament settings described above.
* **Edit event** - opens the event, where dates, description, tickets, and registrations live.
* **Cancel tournament** (in the overflow menu) - takes it off the member portal and the website but keeps the draw and entry list. **Reinstate** brings it back.
* **Delete** (in the overflow menu) - deletes the entry list and the draw. Matches already recorded stay in each player's history as disqualified, so their rating changes are taken back out.
## Where members see tournaments
Members see the tournament, including the entry list and, once generated, the draw and scores, on the member portal. A tournament is visible exactly when its event is: the event must be live, and a **Member only** event shows the tournament only to signed-in members of your organization. Use **Member only** if you don't want entrants' names visible to the public. Cancelled tournaments, and tournaments of organizations with competitions switched off, are hidden.
## Troubleshooting
**Issue**: **Generate the draw** is disabled.
**Solution**: A draw needs at least two active entrants. Import registrations or add entrants first.
**Issue**: Saving a result fails with "This event has not started yet".
**Solution**: Results can only be entered from the event's start time. Check the event's date on its detail page.
**Issue**: A completed match didn't change anyone's rating.
**Solution**: Walkovers and byes are never rated, and neither is a fixture with a player who has no user account (the warning icon in the entry list). Link or invite the player before the tournament so their matches count.
**Issue**: I can't change a first-round score.
**Solution**: The winner has already played the next round. Clear that later result first, then correct the earlier one.
**Issue**: The event I want isn't in the **Event** picker.
**Solution**: The picker hides cancelled events and events that already have a tournament. Each event can host one tournament.
## Related
* [Events](/events/events) - The event a tournament runs on, with its tickets and registrations.
* [Matches and ratings](/events/matches) - How completed fixtures become ranked matches and move ratings.
* [Events section](/events/overview) - The switches that show Events and competitions.
# Business audit
Source: https://docs.1club.ai/home/ai-visibility-report
Generate an AI visibility report that scores your gym's online presence - competitors, Google profile, reviews, AI discoverability, and website performance - with actionable recommendations.
The business audit is an AI visibility report that scores how your gym shows up online. It combines public information about your business (your Google listing, reviews, and nearby competitors) with live checks of your website into a single scored summary, so you can see at a glance where to focus your marketing. Owners and managers use it to decide what to fix first.
## When to use it
Prospective members find you through Google search, your reviews, your website, and AI assistants. The audit shows which of those is holding you back, gives prioritized recommendations, and lets you re-run it later to check whether your changes worked.
Typical uses:
* **See where you stand locally** - a padel gym checks its rank against nearby racket-sport venues, based on star rating and review volume.
* **Find the gaps in your Google profile** - a yoga studio with strong reviews learns its profile lacks a description and opening hours, fills them in on Google, and re-runs the audit.
* **Make the case for a website** - a jiu-jitsu gym with only a Google listing sees the score it could gain by adding a site.
* **Measure a website revamp** - a CrossFit box re-runs the audit after relaunching its site and compares the PageSpeed sub-scores.
See [Worked examples](#worked-examples) for more on each of these.
## How it works
The audit runs five analyses - competitor analysis, profile content and optimization, customer reviews and sentiment, AI discoverability, and website performance. Each returns its own 0-100 score, and the overall visibility score out of 100 is the average of the categories that could be measured. Every section ends with concrete, prioritized recommendations. See [What the report scores](#what-the-report-scores) for how each category is measured.
## Where to find it
The audit lives on the admin **Home** page, under **Grow**, as the **AI visibility report** row of the **Online presence** card (in the **How members find you** group). There is no Marketing tab or separate menu item for it. The full, shareable report opens in a dedicated report app in a new browser tab.
A report is often generated automatically for you shortly after your organization finishes onboarding, so the row may already show a score the first time you open **Home**. If it is still generating, the row shows "Generating your AI visibility report..." and refreshes on its own when the report is ready.
## Generating a report
When no report exists yet, the row reads "Free report on how search and AI assistants see your club" and shows a **Check** button.
1. Open **Home** and find the **AI visibility report** row under **Grow**.
2. Click **Check**. A **Find your club name** search box opens.
3. As you type, pick your business from the Google suggestions so the audit locks onto the correct public listing. Selecting a suggestion attaches the exact Google place, which produces the most accurate report.
4. Click **Analyze**. A new tab opens in the report app and the report builds there step by step (competitors, profile, reviews, AI discoverability, website performance).
5. When it finishes, the report is saved and the row shows your score and when it was checked. Open the report again any time with **View report**.
Because you are signed in as an admin, your email is pre-verified for the report, so you skip the email gate that public visitors see.
## What the report scores
Each report produces an overall visibility score plus the five category scores below. On **Home**, the row shows the overall score out of 100.
### Competitor analysis
Finds up to 10 nearby businesses in your category and ranks you by a composite of star rating and review volume. Your score is your percentile position in that set - rank first and you score 100, last and you score 0. In the full report you can add or remove competitors to fine-tune the comparison set.
### Profile content and optimization
Grades your public Google profile field by field: business name, description, editorial summary, website, phone, address, opening hours, categories, price level, and attributes. It flags missing or weak fields and suggests improvements (and can even draft rewritten profile copy).
### Customer reviews and sentiment
Reads your public reviews and returns overall sentiment (positive / neutral / negative), recurring topics, key issues with severity, and standout strengths. Businesses with no reviews score 0 here, which is itself a signal to start collecting them.
### AI discoverability
Fetches your website and checks how well AI assistants and crawlers can understand it - specifically whether an `llm.txt` file is present at the site root and whether `schema.org` structured data (JSON-LD, microdata, or RDFa) is in place. It returns technical factors and prioritized fixes. If your business has no website on file, this category scores 0.
### Website performance
Runs Google PageSpeed Insights against your site and averages four sub-scores: **Performance**, **Accessibility**, **Best practices**, and **SEO**. A site that cannot be measured, or a business with no website, scores 0 here.
The score on **Home** is color-coded: green at 75 and above, amber from 50 to 74, and red below 50. The overall score also maps to a band label (Poor, Fair, Good, Excellent).
## Reading the full report
Click **View report** on the **AI visibility report** row to open the full report. Beyond the five category sections, the report includes:
* **Estimated revenue loss** - An indicative euro figure for the business you may be leaving on the table, based on how far your overall score sits below 100 and how many sections score below 70. It is a directional estimate, not an exact accounting figure.
* **Competitor ranking** - A visual ranking against the competitor set, where you can search for and add competitors or remove ones that are not relevant.
* **A website prompt** - If no website was found, the report shows the score you could gain by adding one and lets you paste a URL to analyze on the spot.
## Re-running the audit
Once a report exists, the row shows a **Re-run report** action (the refresh icon). It reuses the business name and Google place from your most recent report, so you do not have to search again. The new report generates in the background: the row switches to the generating state and updates itself when the fresh report lands. Re-running is the right way to track progress after you have acted on recommendations.
## Worked examples
### Padel club checking its competitive position
A padel club in a busy city wants to know where it stands. The competitor analysis ranks it against nearby racket-sport venues; a low percentile plus a strong reviews score usually means the profile and website need work to convert searchers who are already impressed by the reviews.
### Yoga studio with great classes but a thin Google profile
A yoga studio scores high on reviews and sentiment but low on profile content. The profile analysis flags a missing description, no opening hours, and few attributes. Filling those in on Google, then re-running the audit, lifts both the profile and AI discoverability scores.
### Jiu-jitsu gym with no website
A martial-arts gym has a Google listing but no website. AI discoverability and website performance both score 0, and the report surfaces the estimated score boost from adding a site. The gym can publish a site with the [website builder](/marketing/website), then re-run the audit to capture the lift.
### CrossFit box comparing before and after a website revamp
A CrossFit box rebuilds its site for speed. Re-running the audit after launch shows the website performance sub-scores (performance, accessibility, best practices, SEO) moving, which quantifies the impact of the revamp.
## Tips & best practices
* **Pick your business from the Google suggestions** - Selecting the exact listing (not just typing a name) attaches the correct Google place and produces a far more accurate report.
* **Match your public name** - Use the same business name your members see on Google so the audit pulls the right profile and reviews.
* **Re-run each quarter or after a change** - Re-run after a website update, a profile cleanup, or a push to collect reviews, and use the score movement to measure progress.
* **Action the recommendations** - Each section ends with prioritized actions; hand them to whoever owns your marketing, website, or Google profile.
* **Fix the zeros first** - A category scoring 0 (no website, no reviews) is usually the fastest, highest-impact thing to change.
## Troubleshooting
**Issue**: The report shows the wrong business or pulls the wrong reviews.
**Solution**: The search matched a different Google listing. Re-run from the empty state, type your name, and pick your business from the Google suggestions so the exact place is attached.
**Issue**: Website performance or AI discoverability scores 0.
**Solution**: Either no website is on file, or your site could not be fetched or measured. Add or correct your website (on your Google profile, or via the in-report prompt), then re-run.
**Issue**: The row is stuck on "Generating your AI visibility report...".
**Solution**: Generation runs in the background and the row checks for completion. Give it a minute or two. If a run fails, the row falls back to the empty state so you can try again with **Check** and **Analyze**.
**Issue**: The overall score looks lower than expected.
**Solution**: The overall score is an average of the categories that could be measured. A single 0 (no website, no reviews) pulls the average down hard. Open the full report to see which category is dragging the score and act on its recommendations.
## Related
* [Reviews](/marketing/reviews) - Manage and moderate the reviews the audit analyzes.
* [Website builder](/marketing/website) - Publish or improve the site the audit measures.
* [Integrations](/settings/integrations) - Connect Google Business Profile and other marketing services.
# Home metrics
Source: https://docs.1club.ai/home/metrics
The six overview charts on Home - revenue, today's performance, payments, bookings, customer growth, and website visitors - what each one counts and which report it opens.
The overview metrics are a panel of six small charts on [Home](/home/overview), each with one headline figure. Owners and managers use them for a quick daily read on the gym's money, activity, and growth before opening a full report.
## When to use it
Typical uses:
* **Morning check** - a climbing gym owner sees the week's bookings and payments and how today is going in one glance, without opening Analytics.
* **Spot a collection problem** - a Pilates studio sees the red **Unpaid** share in **Payments (last 7 days)** growing and chases open invoices before the gap widens.
* **See where bookings come from** - a padel gym reads the source split in **Bookings (last 7 days)** to tell whether a busy week came from the member app, the website, or a partner platform.
## How it works
The panel is shown to staff who can read gym analytics, once your organization has any revenue or members. Before that, Home leaves the panel out rather than showing empty charts.
Every chart works the same way:
* **The figure** at the top is the headline number for the period.
* **The chart** below it has no axes. The labels under it give the first and last day, hour, or month it covers.
* **Totals** are printed above each bar or point, and hovering shows each series with a **Total**. **Today's performance** and **Unique visitors** show their values on hover only.
* **The legend** names each series, with its total for the period where it has one.
* **The title** links to the full report.
Money is shown in your organization's currency. If a chart has nothing to show for its period, it says so instead of drawing an empty chart.
## The six charts
| Chart | Headline figure | What it draws | Opens |
| - | - | - | - |
| **Revenue overview** | Revenue for the months shown | Monthly bars stacked by revenue account | Revenue report |
| **Today's performance** | Today's net revenue | Hour by hour: revenue bars, bookings and check-ins lines | Today's performance |
| **Payments (last 7 days)** | Amount billed in the last 7 days | Daily bars, paid and unpaid | Payments report |
| **Bookings (last 7 days)** | Bookings for sessions in the last 7 days, today included | Daily bars stacked by booking source | Bookings dashboard |
| **Customer growth** | Billable customers now | Weekly members and drop-ins over the last 30 days | Members dashboard |
| **Unique visitors (last 7 days)** | Unique visitors in the last 7 days | Daily visitors line | Web traffic |
### Revenue overview
The revenue for the period, with one bar per month stacked by revenue account. The legend lists each account and its total. When you have many accounts, the smaller ones are grouped into **Other (N)** so the chart stays readable.
The chart shows the current month and the two before it by default. To change that, click the settings icon in the chart's header to open **Configure Revenue Widget**, set **Number of months** (1 to 24), and click **Update**. The setting is saved in this browser only, so it doesn't follow you to another device or apply to your colleagues.
The title opens the [Revenue report](/analytics/billing#revenue-report). Switch to its **By account** tab to see the same breakdown as the chart.
### Today's performance
Today's net revenue so far, and the day hour by hour: revenue as bars, with bookings created and check-ins as lines. A dashed line marks the current hour, and the hours still ahead are faded. This chart refreshes itself every five minutes.
The title opens [Today's performance](/analytics/clubs#todays-performance) in Analytics.
### Payments (last 7 days)
The amount billed over the last seven days, with one bar per day split into **Paid** (green) and **Unpaid** (red). The legend shows the total paid and unpaid for the week.
The title opens the [Payments report](/analytics/billing#payments-report).
### Bookings (last 7 days)
The number of bookings for sessions in the last seven days, today included, with one bar for each day, quiet days included. Bookings count on the day the session takes place, not the day they were made, so today's bar includes sessions later today. Each bar is stacked by booking source, in the colors your organization uses for its sources, and the legend shows each source's count for the week. Smaller sources are grouped into **Other (N)**. Cancelled bookings are not counted.
The title opens the [Bookings dashboard](/analytics/schedule#bookings-dashboard). For how sources are defined, see [Bookings by source](/analytics/schedule#bookings-by-source).
### Customer growth
How many billable customers you have now: members plus drop-ins. The chart shows the same count week by week over the last 30 days, stacked as **Members** and **Drop-ins**.
The title opens the [Members dashboard](/analytics/members#members-dashboard).
### Unique visitors (last 7 days)
How many unique visitors your website and member portal had over the last seven days, counted together, with one point per day. Days with no visitors show as zero.
The title opens **Web Traffic** under **Analytics** > **Website and Apps**. See [Analytics overview](/analytics/overview).
## Troubleshooting
**The panel doesn't appear.** You need permission to read gym analytics, and your organization needs at least some revenue or members. See [Roles and permissions](/reference/roles-permissions).
**A chart shows a dash instead of a number.** The data for that chart failed to load. Reload the page.
**A figure differs from the report it opens.** Most reports open on their own default date range. Set the report to the same period as the chart (for example the last seven days) to compare.
**The numbers don't change during the day.** **Today's performance** refreshes every five minutes. The other charts update when you return to the tab after a while, or when you reload the page.
## Related
* [Home](/home/overview) - The rest of the page: Today, Monthly earnings, and Grow.
* [Analytics overview](/analytics/overview) - The full reports behind each chart.
# Home
Source: https://docs.1club.ai/home/overview
The first page you see in the admin - today's classes and to-dos, six overview metrics, your own earnings if you teach, and a checklist for growing your gym online.
**Home** is the page you land on after signing in to the admin. It shows what needs attention today, how the gym is doing at a glance, and what is still left to set up to grow it. Owners, managers, front desk staff, and instructors all use the same page, and each person sees the sections their permissions allow.
## When to use it
Typical uses:
* **Run the front desk** - at a CrossFit box, the front desk opens **Today** before the 7:00 class, opens its roster, and checks members in as they arrive, adding a walk-in without leaving the page.
* **Catch problems early** - a padel gym manager sees two memberships ending next week and an overdue invoice in the same **Today** list, and follows up before the members lapse.
* **Read the week at a glance** - a yoga studio owner checks revenue, payments, bookings, and website visitors in the overview metrics, then clicks through to the full report behind any number that looks off.
* **Finish setting up** - a new jiu-jitsu gym uses the **Grow** checklist to publish its website, list itself on the Marketplace, and turn on review requests.
## What's on the page
Sections appear top to bottom in this order. A section you can't see is left out entirely, so the page never shows empty placeholders.
| Section | What it shows | Who sees it |
| - | - | - |
| Greeting | Your first name and a one-sentence AI summary of the day | Everyone (the summary needs access to **Today** or the overview metrics) |
| **Today** | Today's classes, standalone bookings, expiring memberships, and overdue invoices | Staff who can read classes, bookings, memberships, or invoices, and instructors |
| Overview metrics | Six small charts: revenue, today's performance, payments, bookings, customer growth, and website visitors | Staff who can read gym analytics, once the gym has revenue or members |
| **Monthly earnings** | What you earned teaching this month | Instructors who can see their own pay |
| **Grow** | The **Online presence** and **Marketing** cards | Staff who can read organization settings, members, or marketing |
The overview metrics sit three across on a wide screen, then two, then one as the window narrows. The **Grow** cards sit side by side on wide screens and stack on narrow ones. For what each overview chart measures, see [Home metrics](/home/metrics).
### Greeting and day summary
The line under **Hello** is a one-sentence AI summary of the most relevant thing about today, such as the next class, how busy the day is, or an overdue invoice worth a look. It is scoped to you, so an instructor gets a summary of their own day, and it only mentions money and membership figures you have permission to read.
The line stays blank while the summary loads, and stays blank if AI isn't available for your organization. For the other AI features in the admin, see [AI assistants](/reference/ai-assistants).
## Today
**Today** lists what is happening at the gym today. Classes come first, then standalone bookings, then expiring memberships and overdue invoices, each group in time order. The section header shows how many of today's standalone bookings haven't ended yet, and the details link next to it opens the [Schedule](/schedule/calendar).
Each row starts with an icon and a time, then a name and a short detail. Click a row to open the record behind it.
| Row | What it shows | Click opens |
| - | - | - |
| **Class** | Start time, class name, and spots taken out of capacity (for example `8/12`). A **View** button opens the roster. | The class |
| **Booking** | Start time, the member, the area, and the amount charged | The booking |
| **N more bookings** | Shown when there are more than five standalone bookings today | The bookings list |
| **Expiring** | An active recurring membership that ends in the next 14 days, with the member, plan, and end date | Memberships |
| **Overdue** | An unpaid invoice past its due date, with the member and the amount in red | The invoice |
Classes and bookings drop off the list once they have ended, and cancelled bookings are not listed. When nothing is left, the list reads **Nothing scheduled for today**.
The list is built from what you are allowed to read. Front desk staff who can read classes but not invoices, for example, see the timetable and bookings without the **Overdue** rows.
### Club and My classes
If you teach classes and also have access to the whole gym's day, a **Club** / **My classes** switch appears in the **Today** header.
* **Club** shows the full list described above.
* **My classes** shows only the classes you teach and the bookings assigned to you, including the ones already finished. Booking rows show a status such as **Starts in 1h 30m**, **In progress**, or **Completed**, and class rows show **View** to open the roster. Each row opens your own performance page for that class or booking. This list refreshes every minute.
Your choice is remembered in this browser. Instructors who can only see their own schedule get the **My classes** list without a switch.
### Class roster
Click **View** on a class row to open its roster in a side panel. The header keeps the class name and time in view and counts arrivals, for example **6 of 12 checked in**. The roster lists everyone in the class: members who booked, and walk-ins who were checked in without a booking. Members move to the bottom as they check in, so the people still to arrive stay at the top.
Each attendee shows their name, what covers their place (a membership or plan name, or **Drop In**), and an indicator when they owe money or still need to sign a required waiver. Row actions:
* **Check In** - records the member's arrival. Staff who can check members in can do it at any time from here. The one-hour-before-class window only applies when members check themselves in.
* **Cancel check-in** - click the checked-in mark to take a check-in back. If you recorded it yourself, you can undo it for a short time afterwards. Staff with permission to delete check-ins can take one back at any time, but a check-in that took a payment has to be refunded by a manager first.
* **Assess** - opens the member's score card for this session. See [Assessments](/members/assessments).
* **Send Message** - writes to the member.
* **Cancel** - cancels the member's booking. Cancelling a booking that is already checked in voids the visit, so it needs permission to delete check-ins. Bookings made through a partner platform can't be cancelled here.
Use **Search members…** to find someone on a long roster, and **Add attendee** to sell a place to a walk-in. **Add attendee** is unavailable when the class is full.
Which actions you see depends on your permissions. Instructors get the same roster for the classes they teach.
## Monthly earnings
Instructors who can see their own pay get a **Monthly earnings** section. It shows what you earned teaching this month and never the gym's revenue, so a manager who also teaches sees their own pay here and the gym's numbers in the overview metrics.
The card shows the month's total and one bar per day. The solid part of each bar is paid and the pale part on top is still pending, so a bar's height is the work you did and its color is the money collected so far. Today's bar is drawn wider. Between the total and the chart are four figures: **Classes**, **Standalone bookings**, **Days worked**, and **Avg / day**.
Use the arrows to move between months. Click a day's bar to open **Payment details**, which lists the classes and bookings behind that day with each one's amount and whether it is paid, pending, or partly paid.
## Grow
The **Grow** section holds two checklist cards. Each row is one channel or tool, with a status chip when it is live and one next step: open it when it is live, or set it up when it is not. Rows that are not live yet look muted.
### Online presence
**How members find you**
* **AI visibility report** - the score out of 100 from your latest [business audit](/home/ai-visibility-report), and when it was checked. Click **View report** to open it, or the re-run button to check again. If you have never run one, click **Check**, find your gym by name, and click **Analyze**.
* **Marketplace** - whether your gyms are listed on the [1club Marketplace](/marketing/marketplace). With several gyms, the row shows how many are listed; expand it to see each gym's status. Click **Publish** to list the rest.
**Where they book**
* **Website** - your [website](/marketing/website) address once it is published. This row needs access to marketing. Click **Publish** if you have a draft, or **Set up** if you have no website yet.
* **Member portal** - the [member portal](/settings/member-portal) address once it is on, or **Set up** to turn it on.
* **Mobile app** - your members book in the 1club app. The details link opens the app's settings.
If you can see the AI visibility report but not the other channels, the card is titled **AI visibility report** and shows only that row.
### Marketing
**Your reputation**
* **Reviews** - your average rating, the number of reviews, and how many are verified. The details link opens [Reviews](/marketing/reviews).
* **Review request** - whether members are asked for a review two hours after they check in. Click **Turn on** to switch it on: the review request automation is created if needed, turned on, and opened so you can see its steps. If your plan doesn't include custom automations, the button reads **Go to automations** instead. If no [booking policy](/settings/booking-policies) checks members in automatically, staff who can read organization settings see a note under the row reminding them to turn on auto check-in, since a request is only sent after a check-in.
**Bring members back**
* **Promotions** - opens [Promotions](/sales/promotions).
* **Automations** - how many of your own [automations](/marketing/automations) are running. Built-in system automations are not counted.
Staff who can read marketing but not manage it only see the **Review request** row once requests are already on.
## 1club AI on Home
On a wide screen, Home opens with the **1club AI** panel docked to its right, so you can ask a question or start a task next to your numbers. The panel suggests prompts that fit the page. If you close it, Home goes back to the floating AI bar for the rest of your session. On smaller screens the assistant opens as a drawer from the AI bar instead.
The panel is the same assistant you can open from any page. See [AI assistants](/reference/ai-assistants) for what it can do.
## Troubleshooting
**A section is missing.** Sections follow your permissions. The overview metrics need permission to read gym analytics, and only appear once the organization has revenue or members. **Monthly earnings** needs your user to be linked to an instructor and allowed to see their own pay. See [Roles and permissions](/reference/roles-permissions).
**Today looks shorter than expected.** In **Club**, classes and bookings that have already ended are removed from the list. Rows you aren't allowed to read, such as overdue invoices, are left out.
**Today starts or ends at the wrong time.** "Today" follows your organization's time zone, not your computer's.
**Numbers don't update.** **Today's performance** refreshes every five minutes and **My classes** every minute. **Today** reloads each time you come back to Home, and the rest updates when you return to the tab after a while. Reload the page to force fresh data.
## Related
* [Home metrics](/home/metrics) - What each of the six overview charts shows.
* [Schedule](/schedule/calendar) - The rest of the day and beyond.
* [Analytics overview](/analytics/overview) - The reports the overview metrics link to.
* [Roles and permissions](/reference/roles-permissions) - Who can see what.
# Compose
Source: https://docs.1club.ai/inbox/compose
Send email, SMS, or WhatsApp to one contact, a list, or everyone matching a contact-type and tag filter. Block editor, templates, variables, file attachments, AI-drafted replies, and drafts.
The composer is the single form behind every one-off message you write: new sends, drafts, and replies. You address one contact, a hand-picked group, or everyone matching a contact-type and tag filter. Owners and managers use it to send updates to a segment of members, and front desk staff and instructors use it to message and reply to individual members.
## When to use it
Use the composer when a message is a one-off: an announcement, a personal follow-up, or an answer to a member's question. The **Recipients** count shows how many contacts you are about to reach before you send, templates save you rewriting repeatable emails, and **Generate with AI** drafts a reply from the conversation and your gym's data. For messages that should go out on a trigger or a schedule, use an [automation](/marketing/automations) instead.
Typical uses:
* **Tell a group about something new** - a tennis gym opens extra Saturday courts and emails every member tagged "Weekend player", starting from a template and attaching the updated court grid.
* **Answer a member's question** - a boxing coach replies to a WhatsApp question about sparring gear, starting from an AI draft, and the reply goes back over WhatsApp.
* **Welcome a new lead** - a climbing gym opens the composer from a lead's profile and sends just that person a welcome email.
* **Win back lapsed members** - a yoga studio drafts a return offer to its lapsed contacts and saves it as a draft for a second look before sending.
See [Worked examples](#worked-examples) for more detail on each.
## Pick recipients
The composer lives inside the Inbox. Open it by clicking **Compose** in the Inbox header (route `/messages/new`), by selecting a draft from the conversation list, or by hitting **Reply** on an existing thread (route `/messages//reply`).
Some flows open the composer pre-filled. The **Promote** button on an [event](/events/events) arrives with the cover image, title, when-and-where line, and a "view event" button already in the body, addressed to all members. Pick or adjust the audience and send.
Above the editor, a **Recipients** header shows a running count of how many contacts the current selection resolves to. You build that audience two ways, which stack:
* **Contact type** - One or more of `member`, `lead`, `staff`, `contact`, `lapsed`, `drop_in`. Leave it empty for no type filter.
* **Filter by tag** - Any contact tags you have defined, picked from the tag input.
Below the filters, the **Select Contacts** picker lists matching contacts. Pick individuals to send to exactly those people, or leave the picker empty to send to *every* contact the filter resolves at send time. A message is capped at 5,000 recipients.
When you arrive from a contact's profile, the URL carries `contactId` (one recipient), `contactIds` (a comma-separated list), or `contactType` (pre-select a type filter), and the composer pre-fills the audience from those.
Instructor-only accounts compose to members at their club: the audience filters and template picker are hidden, and they select from their club's contacts only. Staff limited to the `.own` compose scope can message specific contacts they pick, but cannot target an audience segment.
## Channels
A new message starts as **email**. Switch to **SMS** once your gym has set up an [SMS sender](/settings/sms), or to **WhatsApp** once a WhatsApp number is connected; a channel that isn't ready isn't offered. The channel of a reply follows the conversation it belongs to: reply inside a WhatsApp thread and it goes out over WhatsApp. Each channel changes the editor and the payload.
* **Email** uses a block editor: paragraphs, headings, lists, images, embeds, code, and quotes. A **subject** field is required, and the body is stored as block JSON, not HTML. Email supports templates and file attachments.
* **SMS** uses a plain text area. No subject, no formatting, no attachments. A counter under the text shows how many SMS the message takes. It leaves from your gym's own sender name or toll-free number, and members in a country without a ready sender are skipped. Paid-plan only; see [SMS](/settings/sms).
* **WhatsApp** is also plain text and requires a connected WhatsApp Business number plus a paid plan.
Switching to a plain-text channel (SMS or WhatsApp) clears any applied template, since templates are an email construct.
## The email editor and attachments
The email body is a block editor. Write directly, or apply a template and edit it in place.
You can attach files to an email with **Attach files**. The limits: up to **5 files**, **10 MB** per file, and **25 MB** total. Attachments are not stored on our side; they ride along with the send, get encoded into the email, and are dropped. Because of that, **drafts cannot keep attachments** - saving a draft warns you and drops the files, so re-attach them when you reopen the draft to send. SMS and WhatsApp ignore attachments entirely.
## Templates
If you have access to custom templates (admin and manager roles), a template picker sits beside the subject on email composes. It lists templates of type `message` (your "Marketing & Updates" templates). Selecting one populates the subject and body in place; edit either afterward without changing the original template.
Templates carry `{{variable}}` placeholders. For message templates the available variables are `organizationName`, `organizationEmail`, `organizationPhone`, `contactName`, `contactFirstName`, `contactEmail`, `subject`, `senderName`, and `messageDate`. Legacy HTML templates predating the block editor are accepted, rendered, and converted to blocks on the next save.
These manual message templates are distinct from the system (transactional) templates that drive receipts and event emails. See [Templates](/settings/templates) for the full variable reference and the picker.
## Generate with AI
When you open the composer through **Reply**, a **Generate with AI** button appears above the editor. It calls `POST /v1/ai-messages/{messageId}/suggest`, which uses LangChain with pgvector retrieval to draft a response grounded in your organization and club details, the member's recent bookings and memberships, relevant FAQ/policy content, and the thread so far.
The suggestion replaces the body with paragraph blocks and, for email, sets the subject (defaulting to `Re: `). Edit anything before sending. The action is only enabled on replies, because it grounds the response in the original message, and it requires the AI assistant to be enabled for your organization.
## Send, save draft, or reply
The action row at the bottom of the form changes with the mode:
* **Send Message** posts the message immediately (`POST /v1/messages`). Success shows a toast and navigates to the new conversation; errors surface inline. A successful send invalidates the Inbox list and the billing quota.
* **Save Draft** posts the same payload with `isDraft: true`. Drafts show in the Inbox with a draft chip and reopen into the composer. (Drafts drop any attachments, as noted above.)
* **Reply to Message** sends with `replyToMessageId` set so the message threads under the original, with the subject pre-filled as `Re: `.
There is no scheduler in the composer. To schedule a future send, use an [automation](/marketing/automations) with a delay, or save a draft and send it manually when ready.
## Validation
The form blocks a send until:
* The subject is non-empty (email only).
* The body has at least one block (email) or non-whitespace text (SMS and WhatsApp).
* A reply resolves to at least one recipient.
Field errors display under the relevant input, and both **Send Message** and **Save Draft** are disabled while a request is in flight.
## Worked examples
**Racket sports - waitlist opened.** A tennis club opens extra Saturday courts. Compose an email to the `member` type filtered by the "Weekend player" tag, apply the "Court availability" template, adjust the times, attach the updated grid, and send. The counter confirms the audience size before you commit.
**Martial arts - single-student reply.** A boxing coach gets an inbound WhatsApp question about sparring gear. Open the thread, tap **Generate with AI** for a grounded first draft, edit it, and hit **Reply to Message** - the reply goes out over WhatsApp automatically.
**Climbing - lead onboarding.** A climbing gym arrives at the composer from a `lead` contact's profile (`contactId` pre-filled), writes a welcome email with a trial-pass button block, and sends to just that person.
**Pilates/Yoga - filtered win-back with a draft.** A yoga studio filters to `lapsed` contacts, drafts a return offer, and saves it as a draft to get a second set of eyes before sending. Because it targets a segment, this needs organization-level compose access.
## Tips & best practices
* Watch the **Recipients** count before sending a filtered blast; it is your last check that the audience is who you expect.
* Keep attachments under the total 25 MB cap and remember drafts drop them - attach right before you send, not when you draft.
* Apply a template for repeatable sends, then personalize the opening line so it does not read as a template.
* Use **Generate with AI** to clear a reply backlog fast, but always read the draft before sending; it grounds on your data but can still miss nuance.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| **Send Message** stays disabled | Missing subject (email), empty body, or a request already in flight | Fill the subject and body; wait for the current request to finish |
| Attachments disappear after saving a draft | Drafts do not persist files; only sends carry them | Re-attach files when you reopen the draft to send |
| **Generate with AI** button is missing | You are on a new compose, not a reply | The AI draft is reply-only; it needs an original message to ground on |
| AI generation errors out | The AI assistant is not enabled for the organization | Enable the AI assistant, or write the reply manually |
| Template picker is not shown | You are replying, on SMS/WhatsApp, or on an instructor (club-scoped) inbox | Templates apply to new email composes with template access |
| SMS or WhatsApp isn't offered, or the send is blocked | Paid-plan channel, no approved SMS sender, or (WhatsApp) no connected number | Upgrade the plan, set up a sender under [SMS](/settings/sms), or connect a WhatsApp number |
| "All recipients are unsubscribed or suppressed" | Every selected contact opted out or is suppressed | Adjust the recipient list and resend |
## Related
* [Inbox overview](/inbox/overview) - Channels, the message model, and AI replies.
* [Conversation list](/inbox/messages) - Where drafts, sent messages, and replies live afterward.
* [Templates](/settings/templates) - Templates the picker reads from, plus the variable reference.
* [Contacts](/members/overview) - Tag and segment the contacts you message.
* [Automations](/marketing/automations) - Trigger-based and scheduled sends, instead of one-off compose.
# Inbox
Source: https://docs.1club.ai/inbox/messages
The 1Club messaging inbox - one conversation-style list of every outbound and inbound message, with thread roll-ups, draft chips, channel signals, and a detail pane that opens the thread or compose form.
The inbox is one list of every conversation between your gym and your members, outbound and inbound, with drafts alongside. Each row is a conversation - a piece of human-authored, replyable correspondence between your gym and a member - not an individual delivery. Admins and managers work the organization-wide inbox, and instructors see the conversations for members at the clubs they teach.
## When to use it
Open the inbox to see what members have written back, pick up a conversation where it left off, and finish messages you started. Each row rolls up a whole thread, new replies appear without a refresh, and a **Draft** chip marks anything not yet sent.
Typical uses:
* **Catch a member's reply** - a boulderer answers your route-setting update, and its conversation shows a higher thread counter and a fresh timestamp without a refresh, ready to reply inline.
* **Finish a draft** - a message to all jiu-jitsu members about a belt-grading date is still marked **Draft**, and clicking it reopens the compose pane.
* **Follow a group conversation** - a message to four padel members about a doubles ladder shows as one row, with every reply threaded beneath.
See [Worked examples](#worked-examples) for more on each.
## How the list is built
The conversation list is the main view of **Inbox** in the left rail (route `/messages`). The left pane lists conversations; the right pane shows the selected thread or the compose form. On mobile the right pane slides up as a sheet over the list.
The list queries `/v1/messages` filtered to top-level rows (those with no `replyToMessageId`). Each row is therefore a conversation head: outbound sends with their replies, inbound submissions with your responses, and drafts not yet sent.
Rows are sorted by `createdAt` descending and paginated 25 at a time. **Load more** at the bottom extends the page in 25-row steps.
## What a row shows
Each row is built around the counterparty of the conversation:
* A **single contact** shows that contact's avatar and full name.
* A **pair of contacts** shows two stacked avatars and "Firstname, Firstname".
* A **group of 3+** shows the first contact's avatar with a `+N` badge and "Firstname + N others".
* An **inbound message with no stored recipients** falls back to the **sender's** avatar and name - this is how a member-initiated conversation renders.
To the right of the name you may see:
* A **WhatsApp** badge next to a single contact whose WhatsApp number is verified.
* A **Draft** status chip when the message has not been sent yet.
* The relative time of the latest message (`sentAt` if sent, `createdAt` if still a draft).
* A **thread counter** badge (the small number bubble) showing how many messages are in the conversation, including the original.
Below the name is a snippet: the subject if there is one, otherwise up to the first 80 characters of the body with HTML stripped.
## Channels and direction
A message carries a **channel** and a **direction**, both stored on the record.
* **Channel** (`type`) is `email`, `sms`, or `whatsapp`. The same channel-neutral content is projected to whichever channel a message was sent on.
* **Direction** marks transport provenance: `outbound` = sent from the platform, `inbound` = received from outside (for example a member's email into the club's inbox). Direction reflects provenance, not authorship - a member composing in the portal or app produces an **outbound** message addressed to staff, because it originated on the platform.
The underlying `/v1/messages` endpoint accepts filters for `contactId`, `status`, channel `type`, `inboxId`, and `direction`, which is how the same list powers scoped views (see below).
When a message is sent **on behalf of** a specific person - today, a class instructor via the "send from the instructor" option - the inbox shows that person's name and avatar as the sender, matching the email the member received. The record's underlying `senderId` still points at the staff user accountable for the send.
## Selecting a row
Clicking a row updates the URL to `/messages/` and renders the right pane:
* If the message is a **draft**, the compose pane opens so you can finish writing.
* Otherwise, the **thread view** renders: the original message at the top, replies underneath in order, and an inline reply box at the bottom.
When you click **Reply** the URL becomes `/messages//reply` and the compose pane takes over the right side with the original sender pre-selected as the recipient and the subject pre-filled with `Re: `.
On desktop, opening the inbox with nothing selected auto-opens the first conversation so the detail pane is not blank. On mobile the list stays put until you pick a row.
## Real-time updates
The inbox subscribes to the notification WebSocket. Every time a `message`-category event for the current organization arrives, the list and the open thread refetch, so new inbound replies appear without a manual refresh.
Members' email replies to messages you send arrive in the conversation automatically: each emailed message carries a personal reply address alongside your gym's own address. See [Replies to your messages](/settings/email-sending#replies-to-your-messages).
## Status and engagement signals
The message-level `status` is simple: `draft` or `sent`. The inbox uses it to decide whether selecting a row opens compose or the thread view.
Per-recipient delivery detail is tracked on the record but not surfaced directly in the list. Each recipient has a `deliveryStatus` that can be `queued`, `sent`, `processed`, `delivered`, `deferred`, `bounced`, `dropped`, `failed`, `spam`, `unsubscribed`, or `skipped`, plus an `engagementStatus` of `open`, `click`, `unsubscribe`, or `spamreport`, with timestamps for each transition. Webhook handlers from SendGrid (email) and Twilio (SMS) update these fields as events arrive.
## Audience scope: admin vs instructor view
* **Admins and managers** see the organization-wide inbox: every message attached to the org. The right-pane **Compose** button is enabled, and the audience filter and template picker are available when composing.
* **Instructors** see a club-scoped slice: conversations involving members at the clubs they teach at. Their compose form hides the audience filter and template picker; replying to existing inbound messages still works.
## Permissions
* Reading the inbox requires `messages.read.own` (broader `messages.read.club` / `messages.read.organization` holders see more).
* Composing or replying requires a `messages.create` permission.
* The audience filter and organization-wide templates require manager or admin scope.
## Worked examples
### Spotting a member reply (climbing gym)
A boulderer replied to your route-setting update. Because the inbox refetches on the WebSocket event, the conversation jumps with an incremented thread counter and a fresh timestamp - no refresh needed. Click it to open the thread and reply inline.
### Finishing a draft blast (martial arts)
You started a message to all jiu-jitsu members about a belt-grading date but did not send it. It sits in the list with a **Draft** chip. Clicking the row opens the compose pane instead of a thread, so you can finish and send it.
### Reading a group thread (racket sports)
You messaged four padel members about a doubles ladder. The row shows the first member's avatar with a `+3` badge and "Firstname + 3 others". Opening it shows the single outbound message and any replies threaded beneath.
## Tips & best practices
* **Watch the thread counter.** A rising number bubble means new replies have landed in that conversation.
* **Let the auto-select help on desktop.** The first conversation opens on load, so you can triage without an extra click.
* **Use Reply, not a fresh compose, to keep threads intact.** Replying preserves the conversation and pre-fills the recipient and `Re:` subject.
* **Load more, then scroll.** The list starts at 25 rows; extend it before hunting for an older conversation.
* **Verify WhatsApp before relying on it.** The WhatsApp badge tells you a contact's number is verified and reachable on that channel.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| An expected conversation is missing from the list | It is older than the loaded page | Click **Load more** to extend the list, or open it directly by URL |
| Instructor cannot see a member's message | Instructors are scoped to their own clubs' members | Confirm the member belongs to a club the instructor teaches at; an admin sees the full org inbox |
| A new reply did not appear | The WebSocket event was missed or the tab was backgrounded | Reload the inbox; the list refetches on load |
| Compose or reply is unavailable | Missing a `messages.create` permission | Grant the relevant messaging permission via roles and permissions |
| A row shows the sender instead of a recipient | It is an inbound, member-initiated conversation with no stored recipients | Expected - the inbox falls back to the sender's identity for inbound threads |
| Delivery looks stuck at "sent" | Per-recipient delivery detail is not shown in the list | Delivery and engagement status are tracked on the record via SendGrid/Twilio webhooks, not surfaced in the row |
## Related
* [Compose](/inbox/compose) - The send and reply flow the right pane drives.
* [Templates](/settings/templates) - Templates the compose pane reads from.
* [Inbox overview](/inbox/overview) - How the inbox fits with automations and AI replies.
* [Automations](/marketing/automations) - Automated, trigger-based sends that also land in the inbox.
# Inbox overview
Source: https://docs.1club.ai/inbox/overview
A unified inbox for outbound and inbound conversations with members, leads, and staff. Email, SMS, and WhatsApp messages live alongside replies in one threaded view, with AI-drafted replies grounded in your gym's data.
The **Inbox** (left rail, route `/messages`) is a unified conversation surface: every email, SMS, and WhatsApp message you send, plus every reply or inbound message that lands in 1Club, shows up here in one chronological list. Open a conversation to read the full thread, draft a reply, or generate one with AI.
A **message** is human-authored correspondence between a gym and a member, addressed and replyable. That is what distinguishes it from a **notification** ("your booking is confirmed"), which is system-generated, one-directional, and not replyable. The Inbox holds only messages. System events live in the notifications surface.
## Overview
* Read outbound and inbound messages as threaded conversations in one list.
* Compose to a single contact, a hand-picked group, or everyone matching a contact-type and tag filter.
* Send over three channels: **email**, **SMS**, and **WhatsApp**.
* Reply inside a thread, with an optional **Generate with AI** draft grounded in your organization's data.
* Save drafts, attach files to emails, and reuse custom message templates.
* Scope access by role: organization-wide for admins and managers, club-scoped for instructors.
## Channels
Every message carries a `type` that fixes its channel. The Inbox lists all three side by side.
* **Email** - The default for new manual sends. Uses a block editor (subject plus body), supports templates, variables, and file attachments. Delivered through SendGrid.
* **SMS** - Plain text, no subject, no formatting. Sent from your gym's own sender name, or its own toll-free number in the US and Canada, once it is set up under [SMS](/settings/sms). This is a paid-plan channel because it carries a direct carrier cost.
* **WhatsApp** - Sent through a per-organization WhatsApp Business connection (WhatsApp Cloud API). Requires a connected WhatsApp number and a paid plan. A `+` badge in the conversation row marks contacts whose WhatsApp number is verified.
A new manual message goes out as **email** by default; pick **SMS** or **WhatsApp** in the composer once that channel is set up. WhatsApp also arrives in the Inbox as inbound conversations: when you reply inside a WhatsApp thread, the reply stays on WhatsApp. SMS is outbound only. Automations can also send on any of the three channels.
App notifications, which cover the notification center and alerts on a member's devices, exist in the platform too, but they are emitted by transactional triggers (booking confirmed, waitlist promotion), not composed from the Inbox.
## How a message becomes a conversation
Sending creates one **message** record per outbound action and one recipient record per recipient. The Inbox lists only top-level messages: replies (anything with a `replyToMessageId`) roll up into the thread of their original.
A message's content is **canonical and channel-neutral**: it is stored as structured blocks (paragraph, heading, button, image), not as rendered HTML. Each channel renders its own projection of that content. The Inbox renders the blocks natively; email renders them through the transactional layout. So the delivered email is a projection of the message, not the message itself.
Inbound messages, including replies to your outbound sends, arrive as new top-level messages tagged `direction: inbound`. Selecting a draft opens the compose pane; selecting any other conversation opens the thread view with an inline reply box.
## Display sender, display recipient, and send on behalf of
The raw record only shows the staff `senderId` and the resolved recipient contacts, which is not always who a message should *appear* to be from or to. Three concepts fix that:
* **Display sender** - Who an outbound message appears to be *from* to the member. By default there is none, so member surfaces fall back to the club and admin surfaces fall back to the sending staff user. When a message is sent *on behalf of* a specific person, that person's name and avatar are persisted as the display sender and drive both the member inbox and the delivered email's `from` name. Today the one send-on-behalf-of path is the "send from the instructor" option on the review-request automation. `senderId` is unchanged: it stays the accountable staff user for audit and billing.
* **Message target** - Who a member-composed message is addressed to, as the member chose it: the club (the default) or a specific instructor they have a real relationship with. A member never addresses arbitrary contacts.
* **Display recipient** - The mirror of display sender for member-initiated threads. It fixes who the conversation appears to be *with* (the club's name and logo, or the instructor's name and avatar), so a club-targeted message never leaks which staff member happened to receive it.
## AI-assisted replies
Inside a thread, the reply composer has a **Generate with AI** action. It calls `POST /v1/ai-messages/{messageId}/suggest`, which uses LangChain plus pgvector retrieval over your organization's data to draft a response. The model grounds the reply in organization and club details, the member's recent bookings and memberships, relevant FAQ/policy content, and the conversation history so far.
The suggestion lands in the editor as paragraph blocks (and a subject for email), and you can edit anything before sending. The action is only available on replies, because it grounds the response in the original message. New sends do not expose it. Generating a suggestion requires the AI assistant to be enabled for your organization.
## Permissions and access
The Inbox respects two scopes:
* **Admins and managers** see the organization-wide inbox, compose new messages, use audience filters, and pick from custom templates.
* **Instructors** see a club-scoped view: only conversations involving members at their club, with no audience filters and no template picker.
The governing permissions are `messages.read.own` / `messages.read.club` for viewing and `messages.create.own` / `messages.create.club` for composing. A holder of only the `.own` scope can message specific contacts they select, but cannot broadcast to an audience segment and can only reply to messages they can already read.
## Plan limits
Each channel is gated differently at send time:
* **Email** requires a verified [sending domain](/settings/email-sending) during trial, and counts against your plan's email quota. Hitting the quota returns `PLAN_LIMIT_EXCEEDED` with the daily and monthly figures, surfaced as a dialog rather than a generic toast.
* **SMS** is available on paid plans only. On a trial or locked plan the send returns `PLAN_FEATURE_LOCKED` with an upgrade prompt. Without an approved sender the send returns `SMS_NOT_CONFIGURED`; members in a country your gym has no ready sender for are skipped, and if Twilio refuses every text the send returns `SMS_DELIVERY_FAILED`. See [SMS](/settings/sms).
* **WhatsApp** is also paid-only and additionally requires a connected WhatsApp Business number. A missing connection returns `WHATSAPP_NOT_CONNECTED`; a carrier delivery failure returns `WHATSAPP_DELIVERY_FAILED`.
## Worked examples
**Racket sports - court-closure blast.** A padel club needs to warn members that Court 3 is resurfaced this weekend. Compose an email to the `member` contact type, filtered to the "Padel league" tag, write the notice in the block editor, attach the revised weekend schedule as a PDF, and send. Replies from members land back in the Inbox as inbound messages threaded under nothing new, ready for a one-tap reply.
**Martial arts - belt-test reminder over WhatsApp.** A jiu-jitsu gym with a connected WhatsApp number gets an inbound question from a student about the upcoming grading. Open the thread, tap **Generate with AI** to draft a grounded reply (it pulls the student's recent attendance and the gym's grading policy), tweak the date, and send. The reply goes out on WhatsApp because the conversation is a WhatsApp thread.
**Pilates/Yoga - lapsed-client win-back.** A reformer studio filters to the `lapsed` contact type and sends a warm email offering a return class. Because the audience is a segment, this requires organization-level compose access, not an instructor's club-scoped inbox.
**Gym/Fitness - event promotion.** From a HIIT open-day event, the **Promote** button opens the composer prefilled with the event cover image, title, date and location, and a "view event" button, addressed to all members. Pick or adjust the audience and send.
## Tips & best practices
* Reserve SMS and WhatsApp for time-sensitive, high-value messages. They cost more and read as more urgent than email.
* Let AI draft the first pass on routine replies (hours, pricing, policies), then edit for tone. It is grounded in your gym's data, but you are accountable for what sends.
* Segment with tags rather than blasting your whole contact list. Filtered sends keep engagement high and unsubscribes low.
* Use the "send from the instructor" path for relationship messages so members see a familiar name, not a generic club sender.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| SMS send is blocked with an upgrade prompt | SMS is a paid-plan channel; the org is on trial or a locked plan | Upgrade to a paid plan, or send by email instead |
| WhatsApp send fails with "not connected" | No active WhatsApp Business number is connected for the org | Connect a WhatsApp number, then retry the send |
| Email send hits a limit dialog | The plan's email quota (daily or monthly) is exhausted | Wait for the quota to reset or upgrade the plan |
| "All recipients are unsubscribed or suppressed" | Every selected contact has opted out or is on the suppression list | Adjust the recipient list and resend |
| Instructor cannot see a conversation | Instructor inboxes are club-scoped to their members only | Have an admin or manager handle org-wide conversations |
| A member's reply appears on the wrong side of the thread | Reply arrived without sender org-membership context | Reload the thread; the Inbox realigns member vs. staff senders on refetch |
## Related
* [Compose a message](/inbox/compose) - The send flow, recipient filters, channels, and template usage.
* [Conversation list](/inbox/messages) - The Inbox view, conversation rows, and thread management.
* [Templates](/settings/templates) - Reusable manual-send message templates and the variable picker.
* [Automations](/marketing/automations) - Trigger-based and scheduled sends that run automatically, instead of manual messaging.
* [Contacts](/members/overview) - Tag and segment the contacts you message.
# Welcome to 1club
Source: https://docs.1club.ai/index
Set up your account, tour the interface, and learn the main sections of the 1Club admin
## What is 1Club Admin?
1Club is an AI-native gym management platform. Use the admin to run members, bookings, sales, marketing, and staff for every gym in your organization from one place.
## Key areas
The left rail lists the admin sections in this order. **Settings** sits at the bottom with your account menu.
Today's classes and to-dos, key metrics, and what's left to grow your gym online.
Member list, profiles, leads pipeline, accounts, access cards, and document signatures.
The calendar, bookings, classes, attendance, and check-ins.
Email, SMS, and WhatsApp conversations with members and leads.
Sell memberships, passes, products, and bookings at the front desk.
Plans, products, promotions, memberships, orders, finance, and cash.
Ticketed events, tournaments, matches, and ratings.
Website, automations, content, media, forms, reviews, and marketplace.
Team, instructors, pay rates, time off, and shifts.
Dashboards and reports for clubs, members, bookings, revenue, and marketing.
Organization, clubs, bookings, billing, member apps, integrations, and all configuration.
Some sections and tabs only appear when the matching module is turned on (for example **Events**, competitions, or cash) or when your role has permission for them. See [Roles and permissions](/reference/roles-permissions).
## Quick start guide
### Day 1: Basic setup
1. Complete your profile with accurate information.
2. Explore **Home**: today's classes and bookings, your key numbers, and 1club AI.
3. Review member data and understand current status.
4. Check billing settings and payment methods under **Settings** > **Billing**.
### Week 1: Core operations
1. Set up areas for bookings under **Settings** > **Clubs** > **Areas**.
2. Create membership plans and pricing under **Sales** > **Plans**.
3. Configure class schedules and instructor assignments.
4. Test the booking system with sample data.
### Month 1: Full system
1. Implement automated billing and payment processing.
2. Set up message templates under **Settings** > **Communication** > **Templates**.
3. Review your numbers in **Analytics**.
4. Train staff on system usage and best practices.
## Quick start
New to 1Club? Follow the [Quickstart](/quickstart) to complete your profile, add a gym, create a plan, and run your first booking in a few steps.
## Getting help
* **Browse the help sections** for detailed guides.
* **Use search** to find specific topics.
* **Check [Troubleshooting](/reference/troubleshooting)** for common issues.
* **Contact your administrator** for account issues.
## Reference
* [Roles and permissions](/reference/roles-permissions) - Who can access what in the admin.
* [Organization and clubs](/reference/organization-clubs) - How multi-tenancy works.
* [Troubleshooting](/reference/troubleshooting) - Common issues and fixes.
# Automations
Source: https://docs.1club.ai/marketing/automations
Build event-driven workflows that message members, manage tags, and notify staff automatically - with built-in reminders and one-click blueprints.
Automations are event-driven workflows that run in the background. A trigger fires (a booking is created, a membership expires, a payment fails), the contact is checked against the automation's entry conditions, and a sequence of steps then runs for that contact: send a message, wait, branch on a condition, add a tag, notify staff. Owners and managers build them, members receive the messages, and staff get notified when a person needs to follow up.
## When to use it
Automations take over the follow-ups you would otherwise have to remember by hand. Each one sends at the right moment for each member, without anyone on your team watching the calendar.
Typical uses:
* **Remind members before they train** - turn on the built-in booking reminder so every booking gets an email and app notification 24 hours before it starts.
* **Win back a member who cancels** - a jiu-jitsu gym tags the member **At risk** when they cancel, waits three days, then sends a reason to come back.
* **Collect reviews without nagging regulars** - a padel gym asks players for a review two hours after a checked-in match, paced by sessions played, and invites everyone who leaves one to post it on Google too.
* **Chase a failed payment** - notify your admins and managers, then ask the member to update their payment details.
See [Worked examples](#worked-examples) for step-by-step versions of these, and [Blueprints](#blueprints-prebuilt-automations) for the ready-made workflows behind them.
## How an automation runs
1. A **trigger** event happens in the system (for example, a booking is created).
2. The contact is matched against the automation's **entry conditions** (membership type, tags). If they don't match, nothing happens.
3. A **run** is created and the first step is scheduled.
4. The background worker picks up scheduled steps and executes them in order.
5. Each step records its outcome. Failed steps are retried up to three times before being marked failed.
You can see every run and step in the automation's history view, including timestamps and error messages for failed steps.
## The automations list
Use **Marketing** > **Automations** to view, edit, and create automations. The list is split into two sections:
* **Built-in automations** - Seeded by 1club into every gym (the booking reminder and the membership-expiration reminder). You can adjust timing, channels, and entry conditions, but the step structure is fixed.
* **Your automations** - Custom workflows you build from scratch, plus any suggested blueprints you have accepted. Available on the Grow plan.
Each row shows the trigger, step count, total runs, failed runs, and the last time it fired. Toggle the switch on a row to activate or pause it.
Above the list, a strip of **suggested blueprints** offers ready-made workflows (onboarding drip, review request, win-back, and more) that match your gym's setup. Accepting one drops a fully built, paused automation into your list for review. See [Blueprints: prebuilt automations](#blueprints-prebuilt-automations).
## Triggers
Choose one trigger per automation. Triggers are grouped by domain:
### Bookings
* **Booking created** - A member books a class or area.
* **Booking cancelled** - A booking is cancelled (by the member or staff).
* **Booking checked in** - A member checks in for their booking.
* **Booking no-show** - A booking is marked as no-show.
* **Upcoming booking** - Fires before the booking starts. Configure how far in advance (minutes, hours, or days). Used for reminders.
### Memberships
* **Membership created** - A new membership is activated for a contact.
* **Membership cancelled** - A membership is cancelled.
* **Membership expiring** - Fires before a membership expires. Configure the advance window.
### Payments
* **Payment succeeded** - A payment is successfully captured.
* **Payment failed** - A payment fails (card declined, insufficient funds, etc.).
### Messaging and reviews
* **Message received** - A contact sends an inbound message.
* **Review submitted** - A member submits a review.
### Contact lifecycle
* **Contact created** - A new contact is added.
* **Contact birthday** - Fires on the contact's birthday.
* **Tag added** - A tag is added to a contact. Optionally restrict to a specific tag.
* **Tag removed** - A tag is removed from a contact. Optionally restrict to a specific tag.
## Entry conditions
Entry conditions filter who the automation runs for after the trigger fires. Both conditions are optional and combined with AND.
* **Membership types** - Only run for contacts whose active membership matches one of the selected plan types.
* **Tags** - Only run for contacts who have at least one of the selected tags.
If neither is set, the automation runs for every contact the trigger applies to.
## Steps
Drag steps from the palette onto the canvas to build the workflow. Steps run in order, top to bottom, except where a **Condition** step branches the flow.
### Send message
Send an App, email, SMS, or WhatsApp notification to the contact.
* **Channels** - Pick one or more delivery channels. **App** covers the notification center and an alert on the contact's devices. Email and WhatsApp require the corresponding integration (see [Integrations](/settings/integrations)); SMS requires a sender set up under [SMS](/settings/sms). A text that can't go out (for example no sender for the member's country) doesn't fail the step: its other channels are still sent.
* **Content** - Either write content inline (subject + body) or bind to a [message template](/settings/templates). Bound content is snapshotted when you save the automation, so later edits to the template do not change what the automation sends.
### Send AI message
Generate the message content with AI at send time, using a rule and tone you provide.
* **Mode** - **Auto** sends the AI-generated content directly. **Draft** saves it for staff review in the [Inbox](/inbox/overview) before sending.
* **Tone** - Set the voice of the generated message.
### Send review request
Send a post-booking review request with a one-tap link.
* **Instructor chat bubble** - Optionally include a personal message from the instructor in a casual, professional, or motivational tone.
* **Sessions between requests** - Ask again only after the member attends this many sessions, counted across every club in your organization since their last request. Set it to 1 to ask after every session. **Also ask after the member's first session** lets a brand new member hear from you right away instead of waiting out a full interval.
* **Frequency limit** - Cap how often a single contact receives review requests (for example, at most once every 30 days). This is a ceiling on top of the session interval, and a request goes out only when both allow it.
* **Enable Google review conversion** - After a member submits their review, invite them to share it on Google. Everyone who leaves a review is invited, whatever their rating. Only shown for gyms connected to a Google Business Profile.
The two throttles cover opposite cases. The session interval paces the ask by how much a member actually trains, which is what protects infrequent visitors: someone who comes once a month never fills a 7-day window, so a time cap alone would ask them after every single visit. The frequency limit caps the member who trains daily and clears the session interval faster than you want to be asking.
### Wait
Pause the run for a fixed duration.
* **Duration** - Minutes, hours, or days.
* **Anchor** - Measure from the trigger event, the booking start, or the booking end (where applicable).
### Wait until
Pause until a condition becomes true, with a timeout.
* **Condition** - The check that, when true, lets the run continue.
* **Timeout** - Maximum wait before the run gives up and ends.
### Condition
Branch the workflow. The condition is evaluated and the run follows the **Yes** path or the **No** path.
Supported checks:
* **Has tag** / **Does not have tag**
* **Has active membership**
* **Engagement above** / **Engagement below** - Compare the contact's email open or click rate to a threshold.
* **Booked in period** - Did the contact book a class in the last N days, hours, or minutes?
### Modify contact
Update the contact in place.
* **Add tag** or **Remove tag**
* **Update field** - Set a contact field to a new value.
### Notify staff
Send an App, email, or SMS notification to staff.
* **Recipients** - By role (admin, manager, instructor, staff) or to the booking's assigned instructor.
* **Content** - Subject and body, with variables for the contact and trigger event.
## Blueprints: prebuilt automations
A **blueprint** is a prebuilt definition of a complete automation (a trigger plus a full step graph) authored by 1club. A blueprint is never executed on its own - it becomes a real automation only when it is turned into an org-owned row. There are two ways a blueprint reaches your gym: it is either **auto-provisioned** (seeded and locked) or **suggested** (offered to you, and built only when you accept it).
### Auto-provisioned (built-in) automations
Two blueprints are seeded into every gym at creation. They appear under **Built-in automations** and are locked: you cannot change their step structure, and you cannot delete them.
| Automation | Trigger | Default timing | Channels | State |
| - | - | - | - | - |
| **Booking reminder** | Upcoming booking | 24 hours before start | Email, App | Paused |
| **Membership expiration** | Membership expiring | 3 days before expiry | Email, App | Paused |
Both ship **paused**. Sending unconfigured reminders the moment a gym is created would be surprising, so they stay off until you turn them on.
For a built-in automation you can change:
* The advance window (how long before the event it fires).
* The channels used by the message step.
* The message subject and content (edit the step directly).
* The entry conditions (membership types, tags).
* The on/off state.
The step structure itself is fixed. Editing the message content or timing on a built-in automation is allowed on any plan - that is the point of shipping them. To add steps or branches, build a custom automation alongside.
### Suggested blueprints
Suggested blueprints are never seeded automatically. They appear as a strip of one-click cards above the automation list. Accepting one **instantiates** it: 1club builds a fully wired, **paused, editable** custom automation, stamped with the blueprint's identity so the same suggestion won't be offered again.
A suggestion appears only when all three are true:
* Your gym is on the **Grow plan** (custom automations require it).
* You don't already own an automation from that blueprint (accepting is idempotent - one per gym).
* Every prerequisite the blueprint declares is satisfiable for your gym (see [Prerequisites and gating](#prerequisites-and-gating)).
The catalog of suggested blueprints:
| Blueprint | Trigger | What it does | Prerequisites |
| - | - | - | - |
| **New-member onboarding drip** | Contact created | A three-touch welcome series over the first week (welcome, wait 2 days, tips, wait 3 days, "book your first session"), then tags the member as onboarded. | Onboarded tag (created for you) |
| **Review request** | Booking checked in | Waits 2 hours after the session ends, then sends a review request (every 10 sessions, and never more than one a week). | Gym runs bookings |
| **No-show win-back** | Booking no-show | Branches on whether the member still has an active membership, then sends a tailored nudge to each path. | Gym runs bookings |
| **Membership win-back** | Membership cancelled | Tags the member as at-risk, waits 3 days, then sends a win-back message. | Membership plans exist, At risk tag (created for you) |
| **Birthday celebration** | Contact birthday | Generates a warm, personalized birthday message with AI (saved as a draft for staff to review, never auto-sent). | None |
| **Failed-payment recovery** | Payment failed | Notifies your admins and managers, then messages the member to update their payment details. | Stripe connected |
After you accept a blueprint it lands paused in **Your automations**. Open it in the builder, review and tweak the copy, timing, and channels, then turn it on. Because it is a normal custom automation, you can freely add, remove, or rewire steps.
### Prerequisites and gating
Each blueprint declares typed **prerequisites** - dependencies it needs before it makes sense. Prerequisites fall into two kinds:
* **Providable** - 1club can create the thing for you when you accept. Today this is tags: the tag is found-or-created (scoped to your gym) as part of instantiation, so a providable prerequisite never hides a suggestion. If instantiation fails, any tag it created is rolled back.
* **Gating** - 1club cannot create it, so the blueprint is simply not suggested until you have it.
| Prerequisite | Kind | Satisfied when |
| - | - | - |
| Tag (for example Onboarded, At risk) | Providable | Always - created on accept |
| Gym runs bookings | Gating | You have at least one class or area |
| Membership plans exist | Gating | You have at least one plan |
| Stripe connected | Gating | You have a Stripe integration configured |
So a brand-new gym with no classes, no plans, and no payments connected sees only the blueprints that don't depend on those things (for example the onboarding drip and birthday celebration); the win-back and payment-recovery blueprints appear as you set the rest of the product up.
### Template catalog
The **Send message** step's content picker draws from a template catalog so you don't have to write every message inline. The catalog combines:
* **System templates** - a curated set of 1club-authored message templates that are eligible for automations. If your gym has customized one of these templates, the picker shows your customized version.
* **Custom templates** - your own reusable [message templates](/settings/templates).
Content bound from a template is snapshotted into the step when you save the automation, so later edits to the template do not silently change what an existing automation sends.
## Building a custom automation
1. Go to **Marketing** > **Automations**.
2. Click **Add Automation**.
3. Give it a name and description.
4. Select a **trigger** and configure its options.
5. Set **entry conditions** if you want to limit who the automation runs for.
6. Add steps from the palette and configure each one in the right sidebar.
7. For each **Send message** step, choose channels and either write content inline or select a template.
8. Click **Save**.
The automation is saved paused by default. Review it, then turn it on when you're ready (see [Activating and pausing](#activating-and-pausing)).
## Activating and pausing
New custom automations and accepted blueprints always land **paused** so you can review them before they touch a real member. When you're ready, flip the toggle on the automation's row (or save it as active from the builder) to start it. Flip it back to pause without deleting.
Steps run for real once an automation is active - real emails, real SMS, real staff notifications. If you want to see how a workflow behaves before opening it to your whole audience, narrow the entry conditions to a tag that only a test contact carries, turn it on, trigger the event yourself, then widen the conditions once you're happy.
## Limits and behavior
* Steps are executed by a background worker that polls every minute. Expect up to a minute of delay between when a step is scheduled and when it runs.
* A step that errors is retried up to three times (with exponential backoff) before being marked failed. Failed steps stop that branch of the run, but other branches continue.
* Completed and cancelled runs are archived after about 90 days; processed trigger events after about 30 days. Failed runs are kept indefinitely so you can investigate.
* One trigger event creates at most one run per contact per automation.
## Worked examples
### Membership win-back for a jiu-jitsu gym
A member cancels their unlimited plan. You want a considered follow-up, not an instant plea.
1. Accept the **Membership win-back** blueprint (or build the same shape by hand). It fires on **Membership cancelled**.
2. Step 1 tags the contact **At risk** so you can also see churn on the member list.
3. Step 2 is a **Wait** of 3 days - enough space to feel human.
4. Step 3 sends an email and App notification with a reason to return (a free open-mat week, say).
5. Optionally add a **Condition** on **Has tag** before the message so members who already resubscribed drop out.
### Post-session review request for a padel gym
You want reviews from players who actually showed up, without nagging regulars.
1. Accept the **Review request** blueprint. It fires on **Booking checked in**.
2. The first step is a **Wait** anchored to **booking end** (2 hours after the match), so the ask arrives once they've left the court.
3. The **Send review request** step is set to ask every 10 sessions, with **Also ask after the member's first session** left on. A regular who plays four times a week hears from you about once a month; a player who books once a month is asked once after their first match and then not again for the better part of a year.
4. The step also carries a frequency limit of one request per contact every 7 days, which caps the handful of players who train daily and would otherwise clear the 10-session interval every fortnight.
5. Turn on **Enable Google review conversion** so everyone who leaves a review is invited to post it on Google. The invitation goes to every reviewer, whatever their rating, because asking only happy members breaks Google's review policy.
### Birthday message for a yoga studio
1. Accept the **Birthday celebration** blueprint. It fires on **Contact birthday**.
2. The single **Send AI message** step is set to **Draft** and a **warm** tone, so each greeting is personalized but a staff member approves it in the [inbox](/inbox/messages) before it sends.
3. Prefer fully automatic? Switch the step to **Auto** mode once you trust the output.
### No-show win-back for a spinning studio
1. Accept the **No-show win-back** blueprint. It fires on **Booking no-show**.
2. A **Condition** on **Has active membership** splits the run: members who missed a class get a light "we missed you, rebook anytime" nudge on the **Yes** path.
3. Contacts with no active membership take the **No** path and get a stronger win-back offer instead.
## Tips and best practices
* **Start from a blueprint.** Even if you plan to customize heavily, accepting a suggested blueprint gives you a correct trigger, timing, and step graph to edit rather than a blank canvas.
* **Keep automations paused until you've read every step.** Content is snapshotted from templates on save, so proofread inside the builder, not in the template.
* **Use entry conditions to avoid overlap.** If two automations react to the same trigger, scope each one with a membership type or tag so a contact doesn't get two messages at once.
* **Anchor waits deliberately.** A reminder should measure from the booking start; a review request should measure from the booking end. The wrong anchor sends at the wrong moment.
* **Prefer Draft mode for AI messages** until you've seen a few generated examples for your gym's voice.
* **Pace review requests by sessions, not just by time.** A time cap alone never fills up for an infrequent visitor, so they get asked after every single visit. Set **Sessions between requests** and keep the frequency limit as the ceiling.
* **Notify staff, don't just message members,** on high-stakes triggers like failed payments - the sooner a human follows up, the more you recover.
## Troubleshooting
| Symptom | Likely cause | Fix |
| - | - | - |
| A suggested blueprint never appears | Its gating prerequisite isn't met (no classes/areas, no plans, or Stripe not connected), or your gym isn't on the Grow plan | Set up the missing piece, or upgrade to Grow. Providable tags are created for you and never block a suggestion. |
| The **Add Automation** button or blueprint accept is blocked | Custom automations require an active Grow subscription | Upgrade the plan under **Settings** > **Organization** > **Subscription**, then retry. |
| You can't accept a blueprint a second time | Blueprints are one-per-gym - you already own an automation from it | Open the existing automation from **Your automations** and edit that instead. |
| An automation is active but nothing sends | The email channel has no integration configured, the SMS channel has no sender for the member's country, or entry conditions exclude everyone | Configure the provider under [Integrations](/settings/integrations) or the sender under [SMS](/settings/sms); loosen or clear the entry conditions. |
| Messages arrive a minute late | The step worker polls every minute | Expected - build in a small buffer rather than relying on second-level timing. |
| A built-in automation won't let you add steps | Built-in (auto-provisioned) automations have a fixed structure | Edit the timing, channels, and content in place; build a separate custom automation for extra steps. |
| A member got two messages from one event | Two automations share the trigger | Add non-overlapping entry conditions (membership type or tag) so only one applies. |
## Related
* [Marketing overview](/marketing/overview) - How automations fit alongside the website, forms, reviews, and the rest of the Marketing section.
* [Templates](/settings/templates) - Reusable content you can bind to Send message steps.
* [Inbox overview](/inbox/overview) - Conversations, AI drafts, and one-off sends.
* [Member list](/members/member-list) - Tags and segments used in entry conditions and Modify contact steps.
* [Analytics: marketing](/analytics/marketing) - Automation run history and cross-feature performance.
* [Integrations](/settings/integrations) - Configure email and WhatsApp before using those channels.
* [SMS](/settings/sms) - Set up your gym's SMS sender before using the SMS channel.
# Content
Source: https://docs.1club.ai/marketing/content
Write posts, how-to guides, FAQs, and reusable blocks under Marketing > Content, translate them, and surface them on your website and member portal.
**Content** is where you write the standalone pieces that sit alongside your website pages: news posts, how-to guides, FAQs, and reusable blocks of copy. Owners and managers write them, and visitors and members read them on your public website and member portal.
## When to use it
Content items let you publish and update copy without touching your website layout. You write an article or an answer once, translate it, and every place that shows it stays in sync.
Typical uses:
* **Post gym news** - a padel gym announces its winter league as a **Post**, and the website's **News** section lists it with its own page.
* **Answer the same questions once** - a yoga studio writes its parking, mat hire, and late-arrival answers as **FAQs**, so the website's **FAQ** section and the member portal show the same answers.
* **Teach members how to use the app** - a climbing gym writes **How-To Guides** for booking a slot and freezing a membership, and members find them in the portal.
* **Reuse a paragraph across pages** - a jiu-jitsu gym keeps its "About us" text as a **Block** and pulls it into several website pages, so one edit updates them all.
## How it works
Every content item has a type, a slug, and a language. The four types and where they show:
| Type | What it is for | Where it shows |
| - | - | - |
| **Post** | News and blog entries | The website's **News** section (each post gets its own page at `/news/`) and the member portal's posts page |
| **How-To Guide** | Instructional articles | The member portal's how-to guides page |
| **Frequently Asked Question** | One question and its answer | The website's **FAQ** section and the member portal's FAQs page |
| **Block** | A reusable chunk of copy | Wherever a website page uses a **Block** section that points at its slug |
Items that share a slug and type are language variants of the same piece. The list shows one row per slug, with the languages it already has.
Signable documents (waivers, terms, privacy policy) are content items too, but you manage them under [Settings > Bookings > Waivers](/settings/waivers), not here.
### Content items vs website pages
If you want a brand-new page on your public site, build it in the [website builder](/marketing/website). If you want a single article that a website section lists (**News**, **FAQ**) or the portal shows, create a content item. Copy you would otherwise retype on several pages, such as legal blurbs or an "About us" paragraph, belongs in a **Block**.
## Where to find it
Open **Marketing** > **Content**. The page groups your items by type (**Posts**, **How-To Guides**, **Frequently Asked Questions**, **Blocks**), with a tab for each type showing how many items it has. Each row shows the title, status, visibility, and languages.
Drag rows by the handle to reorder items within a type. The order you set here is the order they are listed in.
## Creating a content item
1. On **Marketing** > **Content**, click **Create Content** and pick a type. You can also use the **Add** button next to a type's heading (for example **Add Post**).
2. Write the body in the block editor. It supports headings, text, images, tables, and more. Images come from your [Media library](/marketing/media).
3. In the **Details** panel, fill in at least **Title**, **Slug**, and **Content Type**.
4. Set **Status** to **Published** when it should go live, then save.
### Details panel
| Field | What it does |
| - | - |
| **Title** | The item's heading. Required. |
| **Slug** | The URL-friendly version of the title, used in links and by **Block** sections. Required. |
| **Content Type** | **Post**, **How-To Guide**, **Frequently Asked Question**, or **Block**. Required. |
| **Status** | **Draft** or **Published**. Only published items appear on your site and portal. |
| **Visibility** | **Public** (anyone), **Admin & Member** (signed-in members), or **Admin only**. |
| **Language** | Which language this variant is written in. |
| **Featured** | Pins the item to the top of its listing. |
| **Author** | The contact credited as the author, or **No Author**. |
| **Excerpt** | A short description shown in listings. |
| **Images** | Images attached to the item. |
| **Tags** | Shown publicly with the content. Website sections such as **FAQ** can filter by tag. |
| **Summary (for AI)** and **Keywords (for AI)** | A plain description and keywords that AI assistants and search use to understand the item. |
| **Meta Title** and **Meta Description** | The title and description search engines show, under **SEO**. |
## Translating and copying
Each row's language menu offers **Translate to** each of your organization's languages that the item does not have yet. Translating copies the item into that language and translates it, then opens the new variant for review. From an item's own page you can also **Copy** it, or switch between its existing language variants.
Your organization's languages are set under **Settings** > **Organization** > **Details**.
## Related
* [Website builder](/marketing/website) - The **News**, **FAQ**, and **Block** sections that show your content items.
* [Media](/marketing/media) - Images you embed in content items.
* [Member portal](/settings/member-portal) - Where members read posts, how-to guides, and FAQs.
* [Waivers](/settings/waivers) - Signable documents, written in the same editor.
# Forms
Source: https://docs.1club.ai/marketing/forms
The form builder under Marketing > Forms. Build a public signup page that onboards a new member, or a questionnaire, health declaration or details update you send to people you already have on file.
Forms are 1Club's form builder. Owners and managers build them, and new and existing members fill them in from a link. One builder produces two rather different things, and which one you get is decided by a single switch:
A public page that takes a brand new person from a shared link to a fully onboarded contact - account, documents, and optionally a plan or a cart.
A fill-in form for someone you already have on file: a questionnaire, a health declaration, a consent form, a details refresh. It records answers and creates no account.
Everything else - the questions it asks, the documents it shows, the copy at the top - is configured the same way for both.
Publish as many forms as you want, one per gym, one per offer, one per audience. Each has its own public URL, its own set of plans and products, and its own copy.
## When to use a form
### With sign up on
Reach for this whenever you want to send someone a link that ends with them in your contact list, with the right contracts signed and the right plan or products attached. Common cases:
* A landing-page CTA for prospects who clicked through from an ad or referral.
* A QR code at the front desk so walk-ins can register before their first session.
* A link in your link-in-bio that funnels followers into a free trial plan.
* A parent-facing form that registers children under a guardian account (junior programs, kids' classes).
* An **order form** where members pick packages and add-on products and pay at checkout.
* A different form per gym or per audience (adults, juniors, corporate) so each cohort sees the right offerings and the right documents.
If you only need to attach an **existing** person to a plan, create a [membership](/sales/memberships) instead. A form with sign up on is for first-time onboarding of new contacts.
### With sign up off
Turn sign up off and you have a page you send to people you already know. No account is created, nothing is sold, and the answers are kept as a dated record:
* A pre-exercise health declaration or PAR-Q before a course or a first session.
* An emergency contact you never collected.
* A photo or media consent form.
* A details refresh at the start of the season.
* A post-course feedback questionnaire.
You can send it as a public link, or have an [automation](/marketing/automations) mail a personal single-use link to one member with its **Send form** step. A personal link identifies the person, so their answers are attributed and their existing details are pre-filled.
A form with sign up off can also be restricted with **Only authenticated**, so only a signed-in member can open it. Leave that off for a form anyone with the link should be able to complete.
## What members go through
Every form is the same wizard. The steps that appear, and their order, are decided by the form's configuration - steps a form doesn't need are skipped automatically. A form with sign up off skips the account, participant and selling steps entirely.
Name, email, password, and any contact fields you require at signup. When the form registers children, this step also collects the participants (see **Who is this form for?** under [Settings](#settings)).
Pick a plan, a class type, and/or add packages and products to a cart. This step only appears when the form has at least one of those attached. You can merge it into the Details step with **Combine plan & user details into one step**.
The form's own questions, grouped under their section headings. Appears whenever the form has any (see [Questions](#questions)). Sits after the details and before the agreements, so a member knows what they are declaring before signing anything about it.
Read and accept any documents that apply to the new contact. The wizard automatically picks up the [document rules](/settings/waivers) and [document signatures](/members/document-signatures) that target member contact types - you don't attach documents to the form directly.
Sign the accepted documents by drawing or typing. Skipped when the form's signature method is set to **No signature**.
Confirm the summary and submit. If a selected plan needs upfront payment, the wizard moves straight into a payment step; if a cart has packages or products, the buyer is routed to checkout.
Drafts are saved in the member's browser as they go, so a half-finished signup survives a page refresh. Passwords, drawn signatures, and answers to the form's own questions are never persisted in the draft - a questionnaire is filled in one sitting, and these pages are often completed on a shared front-desk tablet.
## Creating a form
1. Go to **Marketing** > **Forms**.
2. Click **Add form**.
3. Fill in the sections below.
4. Save. The form's public URL appears in the **Preview URL** column.
The builder is organized into three sections: **Basic information**, **What members can choose**, and **Settings**.
### Basic information
| Field | What it does |
| - | - |
| **Name** | Internal label, shown in the admin forms list only. Members never see it. Typing a name auto-fills the slug. |
| **Slug** | The last segment of the public URL. Lowercase letters, numbers, and dashes only. Must be unique in your organization - if it collides, the system appends a number (`yoga-trial-2`). |
| **Form title** | The heading shown at the top of the wizard. Leave blank to use 1Club's built-in default copy. |
| **Form subtitle** | The sub-heading under the title. Same fallback behavior as the title. |
| **Plan selection title** | The heading shown above the plan picker. |
| **Class type selection title** | The heading shown above the class-type picker. |
All four heading fields are optional. When left blank, the wizard falls back to sensible default copy rather than showing an empty heading.
### What members can choose
This section defines the offerings on the wizard's Selection step. Attach any combination; leave everything empty to create a plain "just capture the contact" form that skips selection entirely.
* **Plans** - the plans a member can pick from. Only **active** plans appear in the wizard, so deactivating a plan removes it from every form on the next page load without editing the form. Each plan keeps its own pricing, signup fee, payment requirement, and renewal rules - selecting one behaves exactly like a standard [membership](/sales/memberships) checkout.
* **Class types** - attach one or more class types to add a class-type interest step. When set, members pick a single class type they're interested in. Useful for routing new leads to the right program.
* **Packages** - attaching packages turns the form into an **order form** (see below), where members add or remove packages and check out.
* **Add-on products** - optional products members can add to their order as extras. Products are only meaningful alongside packages or as part of the order-form cart.
### Settings
The switch that decides which kind of form this is.
* **On** - the form creates an account, can collect documents and a signature, and can sell a plan or a cart. This is the signup wizard.
* **Off** - the form records answers and creates nothing. It gains a **Questions** section, and the selling options disappear, because a plan is bought by an account at checkout.
Only meaningful with sign up off. When on, the person must be signed in to their member portal before the form opens, and their answers are attributed to them. Leave it off for a form anyone with the link can complete, or for one you send as a personal link from an automation - a personal link already names the person.
Whether the form runs the agreements ceremony at all. Turn it off for a short questionnaire that needs no contract. When on, the documents shown are the ones your [document rules](/settings/waivers) match to the person, not a list you attach to the form.
Controls who the form registers and whether the wizard collects participants.
* **Individuals** - each person signs up for themselves. No participant list.
* **Children (registered by a parent)** - a parent or guardian creates their own account and registers one or more children under it. The wizard adds a participants step for the children.
* **Individuals & children** - the person can sign up for themselves, add family members, or both. The wizard shows a "who is this for?" toggle plus a participants list.
How the member signs the documents shown on the Documents step.
* **Draw signature** - the member draws their signature on a canvas.
* **Type name** - the member types their legal name to bind the acceptance.
* **No signature (documents only)** - there's no separate signature step. Members still accept documents via the checkboxes on the Documents step; this just removes the dedicated signing ceremony.
How surfaces that link to the form should present it.
* **Open as a page** - the link navigates to the full-page wizard at `/signup/`.
* **Open as a dialog** - the wizard opens inline as a modal overlay on top of the current page. Use this when embedding the form on a gym page or marketing site so members never leave the page they're on.
When on, the Selection and Details steps are merged into a single first step, so members see plan selection and their sign-up details together instead of on two separate screens. Shorter forms feel faster this way. Off by default. This option only has an effect when the form has something to select (a plan, class type, or cart).
### Translations
Use the **Translate** button in the Basic information header to provide per-locale overrides for the **Form title**, **Form subtitle**, **Plan selection title**, and **Class type selection title**. Members see the copy for their own language; anything you don't translate falls back to the values above.
## Questions
A form asks the questions you write on it. They belong to that form, they are asked in the order you arrange them, and their answers are kept with the response rather than on the member's profile.
Questions are available on a form with **sign up off**. A form that signs people up asks your central contact configuration instead (see [Contact fields on the Details step](#contact-fields-on-the-details-step)), so it has no questions of its own.
### Adding a question
1. Open the form and scroll to **Questions**.
2. Click **Add question**.
3. Type the question in the label box, then pick its type.
4. Expand the row for the rest of its settings.
5. Drag the handle to move it. The order you see is the order members are asked.
| Setting | What it does |
| - | - |
| **Label** | The question itself, as the member reads it. |
| **Type** | Which control the member gets. See the table below. |
| **Answer required** | On by default. A member cannot continue until the question is answered. |
| **Section heading** | Groups this question with the ones above it that share the same heading. Use it to reproduce a numbered paper form. |
| **Help text** | A line of explanation under the question. |
| **Placeholder** | Grey hint text inside the input. Not shown for Yes/No or checkbox questions. |
| **Choices** | For a multiple-choice question, one option per line. |
| **Also save to profile field** | Optional. Copies the answer onto the member's profile as well. See [Answers that belong on the profile](#answers-that-belong-on-the-profile). |
### Question types
| Type | What the member sees |
| - | - |
| **Yes / No** | Two buttons. Nothing is pre-selected, so "No" is a real answer and an unanswered question stays unanswered. |
| **Short text** | A single-line box. |
| **Long text** | A multi-line box, for a medical note or an explanation. |
| **Multiple choice** | A dropdown of the choices you listed. |
| **Checkbox** | A single tick box. A required checkbox has to be ticked to continue, the way a confirmation does. |
| **Date**, **Time**, **Number** | The matching input for each. |
| **Email**, **Phone**, **Link** | A text box with the right keyboard on mobile. Stored as text. |
Reach for **Yes / No** rather than **Checkbox** on anything you might have to prove later. A tick box cannot tell "answered no" apart from "skipped the question", and on a health declaration that is the difference between a record and a blank.
### Reproducing a paper form
Section headings are what make a long questionnaire readable. Give every question in a run the same heading and it renders once, above the group - so a thirteen-item PAR-Q with two numbered sections reads on screen the way it reads on paper.
```
1 - Do you currently, or have you ever, suffered from:
A heart condition Yes / No
Chest pain during physical activity Yes / No
Loss of balance or dizziness Yes / No
2 - Do you currently receive medical care for:
High or low blood pressure Yes / No
Diabetes Yes / No
```
### Answers that belong on the profile
Most answers are not profile data. A health declaration is a dated record: you ask it again next season and you want both answers, not the newer one written over the older.
A few answers really are part of someone's record - a shirt size, an emergency contact. For those, point the question at a profile field with **Also save to profile field**. The answer is then stored on the response *and* copied onto the member's profile, and the question pre-fills with whatever you already hold.
Two things to know when you map a question:
* The question's type must match the profile field's type, and the field's choices become the question's choices. Picking a field adopts both.
* Two questions on the same form cannot write to the same profile field. It holds one value, so only one question may fill it.
**Yes / No** questions cannot be mapped to a profile field. Profile fields store a tick box, which cannot carry the "unanswered" state that makes Yes/No worth using.
### Editing questions later
Rewording, reordering, or deleting a question never changes a response that has already come in. Each response keeps the wording each question carried when it was asked, in the order it was asked, so last season's answers still read correctly against last season's questions.
### Translating questions
Each question has its own **Translate** button, next to its profile-field setting. Translate the label, placeholder, help text, and section heading per locale; members see their own language and anything untranslated falls back to what you typed. Choices are deliberately not translated, because the choice text is also the stored answer.
## Responses
Every submission is recorded against the form, and against the person when we know who they are.
### Per form
On the forms list, open a form's row menu and choose **View responses**. You get everyone who was asked and what they said, including people an automation invited who have not answered yet. Click a row to read one response in full, question by question.
The **Responses** tab next to **Forms** lists every response to every form in one place, including invitations nobody has answered yet. Responses are member data (up to medical answers), so this tab needs permission to read members, not just to build forms.
### On the member
A member's own page has a **Form responses** section on the **Documents** tab, showing what that person has declared, most recent first. This is the view to use before a first session: you are asking about the person, not about the form. Only submitted responses appear here; an unanswered invitation stays on the form's own list.
## Order forms (packages and products)
Attaching **packages** switches the form into order-form mode. Instead of picking one plan, members build a cart:
* Packages are the sellable bundles you've configured for your organization. In the wizard, members can add or remove them and adjust quantities.
* **Add-on products** appear alongside the packages as optional extras.
* On submit, 1Club creates a **draft order** attached to the new contact and routes the buyer to checkout to pay. Packages are broken down into their component memberships and products automatically - the order isn't charged during signup, so the buyer completes payment on the checkout page.
Order-form mode and plan mode can coexist on the same form: if you attach both, a member can either select a plan or fill a cart. Either path is a valid submission.
## Contact fields on the Details step
A form that signs people up does not define its own account fields. What the Details step asks for - phone, date of birth, gender, and any custom fields - is driven by your contact-field configuration under **Settings** > **Members** > **Contacts**. For each field you control:
* **Signup visibility** - hidden, optional, or required when signing up.
* **Applies to** - whether the field is collected for the person signing up, for the children/dependents they add, or both.
Because this is central configuration, adding or changing a required signup field updates every form at once - you don't edit forms one by one.
## Sharing the form
Each form has a public URL of the shape:
```
https:///signup/ sign up on
https:///forms/ sign up off
```
The **Preview URL** column on the forms list links straight to it. Paste that link into emails, ads, signage, or your link-in-bio, or wire it to a button using the form's **Display mode**.
The portal subdomain is set under **Settings** > **Member Apps** > **Member portal** (see the [member portal](/settings/member-portal)). Until that's configured, the **Preview URL** column is empty because there's no public host to build the link against.
The wizard is served first-party from your member portal, so the account it creates is signed in against the portal the moment signup completes - there's no cross-site login hop.
## Documents and signature
The Documents and Signature steps are driven by your [document rules](/settings/waivers) and [document signatures](/members/document-signatures), not by the form. A new contact created through any form that signs people up is matched against every active rule that targets member contact types. If a rule matches, the wizard shows the document and the contact must accept it before continuing.
Two consequences:
* You don't attach documents to a form directly. Adding a new document rule automatically applies to every form that creates matching contacts.
* The Signature step appears only when the form's signature method is **Draw** or **Type**. Setting it to **No signature** removes the step; documents are still accepted via their checkboxes.
## Editing and deleting
* **Editing** a form changes the wizard for everyone who opens the link from that point on, including people mid-draft. Drafts are held in the browser but re-fetch the form's config on every step, so changes take effect immediately.
* **Changing the slug** breaks any previously shared links - the old URL returns a "form not found" page. Prefer leaving the slug alone once you've published it.
* **Deleting** a form makes its public URL return "form not found" immediately. It does not delete or modify the contacts, memberships, or orders already created through it.
## What gets created
With sign up off, a submission creates one **response** against the form, attributed to the member when we know who they are. Answers to questions you pointed at a profile field are also copied onto their profile. Nothing else is created: no account, no membership, no order.
With sign up on, every successful submission creates:
* A new **contact** of type "member", with the account and contact fields filled in from the Details step.
* Any **children or family members** added on the participants step, linked to the primary contact.
* Document **acceptances** (and a signature, unless the form uses **No signature**) for each presented document, tied to the new contact.
* A **membership** if a plan was selected, plus its first payment if the plan requires upfront payment.
* A **draft order** if the form is an order form and the cart has packages or products, ready for the buyer to check out.
From there the contact behaves like any other member. They appear in the [member list](/members/member-list), their plan shows up under [memberships](/sales/memberships), and any [smart tagging](/settings/smart-tagging) rules they match apply on the next sync.
## Troubleshooting
**The Preview URL column is empty**
The public URL needs a portal subdomain. Set it under **Settings** > **Member Apps** > **Member portal**; until then there's no host to build the link against.
**A plan I attached doesn't appear in the wizard**
Only **active** plans render. Reactivate the plan under [Plans](/sales/plans) - the form picks it up on the next page load without re-saving the form.
**The Selection step never shows**
The step only appears when the form has something to select. Attach at least one plan, class type, package, or add-on product under **What members can choose**.
**The Documents or Signature step is missing**
Documents come from your document rules and [document signatures](/members/document-signatures), not the form - a step appears only when an active rule matches the new contact's member contact type. The Signature step is also skipped when the form's signature method is **No signature**.
**A shared link returns "form not found"**
The slug changed or the form was deleted. Changing a slug breaks every previously shared link, so avoid editing it once published.
**The Questions section isn't there**
Questions belong to a form that records answers. Turn **Sign up** off and the section appears.
**A profile field doesn't show in "Also save to profile field"**
Only fields your members can see are offered, so a field whose visibility is **Private** is never listed. Check it under **Settings** > **Members** > **Contacts**.
**A question won't let me change its type**
The type is owned by the profile field it saves into. Clear **Also save to profile field** first, or change the field's own type.
**A member's answers aren't on their profile**
That is the default and usually what you want: answers stay with the response. To copy one onto the profile, point that question at a profile field. Answers given before you set that are not back-filled.
**A required field is missing from the Details step**
Contact fields are configured centrally under **Settings** > **Members** > **Contacts**, not per form. Set the field's **Signup visibility** to optional or required there, and it updates every form at once.
## Related
The catalog and checkout behavior behind the Selection step's plans.
Pricing, signup fees, and renewal rules the form inherits per plan.
Control which documents appear on which forms.
The portal that serves the form, on the subdomain set under **Settings** > **Member Apps** > **Member portal**.
Mail a personal single-use link to one member with the Send form step.
Where contact fields are configured - the profile fields a question can copy its answer onto.
# Marketplace
Source: https://docs.1club.ai/marketing/marketplace
List your gym on the 1club consumer marketplace at 1club.me so new members can discover, explore, and book it, and manage your network listing from Marketing > Marketplace.
The Marketplace is currently in **beta**. Listing is free, and every application is reviewed by the 1club team before it goes live.
The 1club marketplace at [1club.me](https://1club.me) is a public directory where people discover, explore, and book gyms. A published listing shows your gym's details, photos, amenities, and location to anyone browsing - no account required to look around. Gym owners submit and manage the listing, and prospective members browse it.
## When to use it
A listing puts your gym in front of people who are looking for somewhere to train but haven't found you yet. Listing is free during the beta, and you decide which of your gyms appear and when.
Typical uses:
* **Get found by people nearby** - a new reformer Pilates studio lists itself so people browsing 1club.me can see its classes and plans and book a trial.
* **List locations one at a time** - an organization with several gyms submits each gym separately, so a location whose profile isn't complete yet stays off until it is.
* **Step off the marketplace for a while** - a gym closing for renovation unpublishes its listing, which takes effect immediately, then resubmits it for review when it reopens.
See [Worked example: listing a Pilates studio](#worked-example-listing-a-pilates-studio) for the full walkthrough.
## How it works
There are two sides to the Marketplace:
* **The consumer marketplace** at 1club.me, where the public finds and explores gyms.
* **The admin side** under **Marketing** > **Marketplace**, where you submit your gym to the network and manage its listing.
## The consumer marketplace (1club.me)
Anyone can browse [1club.me](https://1club.me) without an account. Visitors:
* **Search and filter** gyms by sport, location, and availability.
* **Browse gym cards** with the logo, name, location, and photos, and save favorites.
* **Open a gym's profile** to see its description, photo gallery, amenities, sports, coaches, and the plans, classes, and appointments it offers.
* **Get in touch or book** - contact the gym or start a booking from the public profile.
Your published listing is what powers all of this, so the more complete your gym profile is, the better it presents.
## Get your gym listed
You manage your listing from **Marketing** > **Marketplace** in the admin dashboard. You submit a gym to the network through a guided wizard, and the 1club team reviews it before it goes live.
1. Open **Marketing** > **Marketplace** and click **Start now**. If your organization has more than one gym, pick which one to list from the menu.
2. The setup wizard opens in a new tab and walks you through the listing profile in these steps: **club type**, **sports**, **location** (address, photos, phone, and email), **description**, **amenities**, and a final **Confirm & submit** step. Everything already on file for the gym is prefilled.
3. Confirm the final step to submit your application. This sends the gym for review and the 1club team is notified.
Your application lands in an **In review** state. You will see the status on the Marketplace page, and the listing goes live once a reviewer approves it.
A brand-new sign-up that starts from 1club.me creates a free account and must verify its email before the wizard's presets load. If you are already signed in to the admin dashboard, you go straight into the wizard for the gym you picked.
## Profile requirements
A gym can only be submitted when its profile is complete. The required fields are:
* **Address**
* **Phone number**
* **Email**
* **Description** (at least some written content)
* **Location** - a city plus a resolved map pin (latitude and longitude)
Photos are optional but strongly recommended - a listing without them looks bare. If something is missing, the Marketplace page shows a warning listing exactly which fields to complete, and the submit step will not go through until they are filled. The wizard is the guided way to complete them.
## Listing statuses
| Status | Meaning |
| - | - |
| **In review** | Submitted and waiting for approval by the 1club team. |
| **Published** | Live on the marketplace and shown in search and browse. |
| **Published (unlisted)** | Reachable by direct link, but hidden from marketplace search and browsing. |
| **Unpublished** | Not visible on the marketplace. A gym that has never been listed is treated the same way, with no status chip. |
## Managing a listing
The Marketplace page lists each gym in your organization as a card with its current status. Which actions you see depend on where the gym is:
* **In review** - **Withdraw** pulls the application back. This takes effect immediately, no review needed; the gym returns to unlisted and you can resubmit later.
* **Published** or **Published (unlisted)** - **Unpublish** takes the listing off the marketplace immediately, and **View listing** opens the public page on 1club.me.
* **Not listed** (unpublished or never listed) - **Submit application** opens the wizard to complete the profile and send it for review.
To relist a gym after unpublishing, submit it again and it goes back through review. Resubmitting is treated as a fresh application - any earlier review decision is cleared.
Viewing the Marketplace page needs marketing read access, but submitting, withdrawing, and unpublishing require the organization-management permission (`settings.manage.organization`). Read-only staff see the listings and their status but not the action buttons.
## Worked example: listing a Pilates studio
A single-location reformer Pilates studio wants to appear on 1club.me:
1. The owner opens **Marketing** > **Marketplace**. Because there is only one gym, **Start now** launches the wizard directly.
2. In the wizard, the club type is Pilates/Yoga and the sports include pilates and reformer. Location is confirmed with a map pin, and the owner adds interior photos and the studio phone and email.
3. The description highlights small-group reformer classes and the amenities step lists showers, mat storage, and free parking.
4. On **Confirm & submit**, the studio moves to **In review**. The owner sees the "In review" status on the Marketplace card, with a **Withdraw** option if plans change.
5. Once the 1club team approves it, the card flips to **Published**, and **View listing** opens the studio's public page at 1club.me, where prospective clients can browse classes and plans and book a trial.
## Tips & best practices
* **Finish the profile before you submit.** Address, phone, email, a real description, and a located map pin are all required; the submit step blocks until they are present.
* **Add photos even though they are optional.** Listings with a gallery get far more attention than bare ones.
* **Write the description for prospects, not staff.** Say what makes the gym worth visiting, in plain language.
* **List accurate amenities and sports.** They power the marketplace filters visitors use to find you.
* **Manage each gym separately.** In a multi-gym organization, submit and publish gyms independently as each one's profile becomes ready.
* **Use unlisted for soft launches.** A published-unlisted listing is reachable by direct link but stays out of search until you are ready to be found.
## Troubleshooting
**Issue**: The **Submit application** step fails and says the profile is incomplete.
**Cause**: One or more required fields (address, phone, email, description, or location) is missing.
**Fix**: Complete the fields named in the message. The wizard walks through each one; the location step needs both a city and a resolved map pin.
**Issue**: I do not see **Start now**, **Submit**, or **Unpublish** buttons.
**Cause**: Your account has marketing read access but not the organization-management permission.
**Fix**: Ask an owner or admin to grant `settings.manage.organization`, or have them perform the listing action.
**Issue**: My gym was published but disappeared from the marketplace.
**Cause**: A profile edit may have left a required field empty. A listing that becomes incomplete is pulled from the public network automatically.
**Fix**: Reopen **Marketing** > **Marketplace**, complete the fields flagged on the gym's card, and resubmit for review.
**Issue**: I unpublished a gym and now want it back.
**Cause**: Unpublishing is immediate and final for that listing; relisting goes back through review.
**Fix**: Click **Submit application** on the gym's card to resubmit. It re-enters **In review** as a fresh application.
**Issue**: My organization has several gyms but only one submitted itself.
**Cause**: Each gym is listed independently.
**Fix**: On the Marketplace page, use **Submit application** on each additional gym's card to send it through the wizard and review.
## Related
* [Marketing overview](/marketing/overview) - The rest of the Marketing toolset.
* [Organization and clubs](/reference/organization-clubs) - How gyms (clubs) are structured inside an organization.
* [Settings overview](/settings/overview) - Where the underlying gym profile fields (address, contact, description) are edited.
# Media
Source: https://docs.1club.ai/marketing/media
Upload, organize, and reuse images and videos for your website, documents, and emails. Files are stored in Cloudinary and shared across the platform.
The Media library is your organization's shared store of images and videos. You upload a file once, give it a name and alt text, and then reuse it anywhere in the platform: website sections, content blocks, documents, and emails. Files live in Cloudinary (1Club's file storage), so the library holds a record of each asset, its metadata, and where it came from, while Cloudinary serves the actual file. The library is shared by every admin on your team who can access Marketing.
## When to use it
With every image in one place, you pick the same file from the library instead of re-uploading it for each page or email, and its alt text travels with it. Images you set on other screens land in the library too, so it stays the single inventory of what your gym uses.
Typical uses:
* **Build a website gallery** - a padel gym uploads its four best court photos with alt text and shows them as a tiled gallery hero on the home page.
* **Play a class clip behind your headline** - a yoga studio uploads a 40-second reformer class video and uses it as a hero background.
* **Keep one logo identical everywhere** - a martial-arts gym uploads its crest once and uses the same asset in an email header and a website content block.
* **Clean up after a rebrand** - select the old photos and delete them in bulk, which removes their Cloudinary files too.
See [Worked examples](#worked-examples) for the details of each.
## Where media lives in the admin
Open **Marketing** > **Media**. The list view shows one row per asset with a thumbnail, name, alt text, type, and the date it was added. Use the search and column sort to find things; select multiple rows for bulk actions.
## Uploading media
1. On **Marketing** > **Media**, click **Add**.
2. In the dialog, click **Upload** and pick an image or video from your computer. The file uploads to Cloudinary and a preview appears.
3. Fill in the metadata:
* **Name** (required) - a human-friendly label used to find the asset later. Defaults to the file name if you leave it blank after uploading.
* **Alt text** - a short description of the image. Used for accessibility and read by search engines; set it on anything that appears on your public website.
* **Caption** - optional longer text some sections can display beneath the image.
4. Save. The asset now appears in the library and is ready to use.
Uploads have size limits: **10 MB for images** and **100 MB for videos**. Only
image and video files are accepted. The type (image or video) is detected
automatically from the file.
## Editing an asset
Open the row actions and choose **Edit** to change an asset's **name**, **alt text**, or **caption**. The underlying file and its URL do not change when you edit metadata, so anything already using the image keeps working. To replace the actual file, upload a new asset and re-point the sections that used the old one.
Two other row actions help day to day:
* **Copy URL** puts the asset's direct Cloudinary URL on your clipboard. Use it when a field asks for an image URL rather than a library picker (for example, a custom script or an external tool).
* **Delete** removes the record and the file from Cloudinary. This is permanent.
## Organizing and finding media
* **Search** matches your query against both the **name** and the **alt text**, case-insensitive, with name matches ranked first. Naming files well ("padel-court-evening", "reformer-studio-wide") makes them findable months later.
* **Sort by date** to surface your most recent uploads; the list defaults to newest first.
* **Alt text doubles as a keyword.** Since search looks at alt text too, a descriptive alt text both improves accessibility and makes the asset easier to locate.
## Where media is used
The library feeds the rest of the platform:
* **Website sections.** Image fields in the [website builder](/marketing/website) (hero backgrounds, galleries, CTA images, club photos) open the media picker. Uploading from that picker also lands the file in this library.
* **Content and documents.** [Content items](/marketing/content) such as blocks and posts, and the waivers under [Settings > Bookings > Waivers](/settings/waivers), pull images from the library.
* **Emails.** Email templates can reference library assets so your messages stay on-brand.
* **Automatically registered images.** When you save an image URL elsewhere in 1Club - a club cover photo, a plan image, an area photo - a media record is created for that URL if one does not exist yet. This keeps the library as the single inventory of every image your organization uses, even ones set from other screens.
## Worked examples
**Padel club - hero and gallery.** You photograph your courts at golden hour. Upload the four best shots, name them `padel-court-01` through `04`, and write alt text like "Outdoor padel court at sunset". In the website builder, add a **gallery**-variant hero and pick these four; they render as a tiled gallery on the home page, and their alt text carries into the public site for SEO.
**Yoga studio - a short promo clip.** You have a 40-second reformer class video. Upload it (under the 100 MB video limit); the library tags it as a video automatically. Reference it as the sole background of a **background**-variant hero so the class motion plays behind your headline. Videos must be the only item in a hero background, so do not mix the clip with still images in that section.
**Martial arts gym - reusing one logo everywhere.** Upload your dojo crest once with a clear name ("bjj-crest-white"). Use **Copy URL** to drop it into an email header and pick the same asset from the media library for a website content block, so the mark stays identical across surfaces.
**Cleaning up after a rebrand.** After new photography arrives, select all the old shots in the list and use the bulk **Delete** action. The records and their Cloudinary files are removed together, so you are not paying to store images no page references anymore.
## Tips & best practices
* **Write alt text on anything public.** It is your accessibility baseline and it feeds both website SEO and the library's own search.
* **Name files for search, not for storage.** "reformer-studio-wide" beats "IMG\_4821". You will thank yourself when a section needs "that wide studio shot".
* **Compress before uploading.** Cloudinary can transform images, but starting from a reasonably sized file keeps you under the 10 MB limit and speeds up your pages.
* **Delete unused assets in bulk.** After a redesign, prune the old set in one pass rather than one file at a time.
* **Prefer the library picker over pasted URLs** in the website builder, so every image you use is tracked and reusable.
## Troubleshooting
| Symptom | Likely cause | Fix |
| - | - | - |
| Upload rejected before it starts | File is not an image or video, or exceeds the size limit (10 MB image / 100 MB video) | Convert or compress the file, or trim the video, then re-upload |
| File uploaded but the row has no thumbnail | The asset is a video, or the URL did not save | Videos preview with a player rather than a static thumbnail; re-open the asset to confirm the URL is present |
| An image still shows on the site after you deleted it | The section still references the old URL, or a published release predates the delete | Re-pick a new image in the website builder and publish a new release |
| Can't find an asset you know you uploaded | Search only matches name and alt text | Add or fix the alt text, or sort by date to scan recent uploads |
| Deleted a file but it lingers in Cloudinary | Cloudinary removal failed while the record was still deleted | The record is gone from 1Club; contact support if you need the orphaned Cloudinary file cleaned up |
## Related
* [Website](/marketing/website) - Website sections pull their images and videos from this library.
* [Content](/marketing/content) - Posts, guides, FAQs, and blocks that embed media.
* [Reviews](/marketing/reviews) - Another Marketing tab that shapes your public site.
* [Integrations](/settings/integrations) - Where file storage (Cloudinary) is configured.
# Marketing overview
Source: https://docs.1club.ai/marketing/overview
The Marketing section - website, automations, content, media, forms, reviews, and marketplace listing - and how the tools fit together.
The **Marketing** section is where you get found, turn visitors into contacts, and keep members coming back. It groups several standalone tools: your public website and the content and media behind it, the forms people sign up through, automations that follow up for you, your reviews, and your marketplace listing. Each tool works on its own, so you can adopt them one at a time.
Open **Marketing** from the left rail. It opens on **Website**, and the section column lists the other tabs.
## What's in here
The **Marketing** tabs, in order:
| Tab | What it is for |
| - | - |
| **[Website](/marketing/website)** | Build, translate, and publish your public website with the section-based builder and AI assistant. |
| **[Automations](/marketing/automations)** | Trigger-driven workflows that message members, manage tags, and notify staff - including reminders and review requests. |
| **[Content](/marketing/content)** | Posts, how-to guides, FAQs, and reusable blocks shown on your website and member portal. |
| **[Media](/marketing/media)** | The shared library of images and videos used across your website, content, and emails. |
| **[Forms](/marketing/forms)** | Public signup pages that onboard new members, and questionnaires or health declarations for people you already know. |
| **[Reviews](/marketing/reviews)** | Member and Google reviews, with your coaching, facilities, and response-rate scores. |
| **[Marketplace](/marketing/marketplace)** (beta) | List your gym on the public 1club.me marketplace so new members can discover it. |
You only see the tabs your role allows. **Forms** is also open to roles that can read settings but not Marketing, so those roles see Marketing with **Forms** as its only tab.
### Moved out of Marketing
* The sales pipeline and deals are now the **Leads** tab of **Members**. See [Leads](/members/leads).
* Vouchers and discounts are now under **Sales** > **Promotions**. See [Promotions](/sales/promotions).
* The **AI visibility report** is a row on **Home**, under **Grow**. See [Business audit](/home/ai-visibility-report).
* The old **Reputation** page is folded into **Reviews**.
## How the pieces fit together
**Website**, **Content**, and **Media** form your CMS. Media holds the images and videos, Content holds the posts, guides, FAQs, and blocks, and Website is where you assemble pages and publish them. Website sections pull live data too (plans, classes, instructors, reviews), so the site stays current without extra edits.
**Forms** and the website's contact form are how new people get onto your list. A signup form creates a contact, collects their documents, and can sell them a plan. A contact-form submission creates a lead you work on the [Leads](/members/leads) board.
**Automations** sit on top of the rest. An automation reacts to a trigger (a booking, a membership change, a failed payment, a birthday, a tag change) and sends a message, applies a tag, notifies staff, or mails a personal link to a form. The review-request automation feeds new reviews into **Reviews**.
**Reviews** and **Marketplace** shape how people who don't know you yet see your gym. Your 1club reviews show on your website and your marketplace listing; Google reviews show on your own website and portal only.
## Getting started
1. Build your website from a template on **Website** and publish your first release.
2. Create a signup form on **Forms** and link it from your website and social profiles.
3. Turn on the built-in booking reminder on **Automations**, and accept the review-request blueprint.
4. Write a few FAQs on **Content** for the questions your front desk answers every day.
5. Check **Reviews** monthly to see your scores and response rate.
## Cross-feature reporting
For metrics across marketing tools - lead-to-member conversion, promotion redemptions, automation run history - use the [Marketing analytics](/analytics/marketing) dashboard. Per-automation run and step history is available from the automation list.
## Related
* [Leads](/members/leads) - The pipeline board for prospects.
* [Promotions](/sales/promotions) - Vouchers and discounts with targeting and caps.
* [Templates](/settings/templates) - Reusable messages you can bind to automation steps.
* [Marketing analytics](/analytics/marketing) - Cross-feature performance reporting.
* [Members overview](/members/overview) - Tags and segmentation used to target automations.
# Reviews
Source: https://docs.1club.ai/marketing/reviews
See member and Google reviews in one list with your coaching, facilities, and response-rate scores, report reviews that break policy, and track review-request conversion to Google.
**Reviews** lists every review attached to your organization: those members submit through 1Club and those synced from your gyms' Google listings. It also shows how well your gym scores and how many members you actually ask. Owners and managers read the list, and members write, edit, and decide the visibility of their own reviews.
## When to use it
Having member and Google reviews in one list, with the scores above it, tells you how members rate the coaching and the facilities, and whether your review requests reach enough of them. When you run review requests, the same page shows how many members you sent on to Google.
Typical uses:
* **See which half of the visit lets you down** - a climbing gym sees a 4.8 **Coaching** score next to a 3.9 **Facilities** score and plans its next investment around the changing rooms, not the coaches.
* **Check that you are asking enough people** - a padel gym with a low **Response rate** adds a review request after check-in, then watches the rate and the **Google conversion** card climb.
* **Ask in the coach's voice** - a jiu-jitsu gym sends review requests with a motivational note from the coach, sent from the coach's email.
* **Flag an abusive review** - a yoga studio reports a review that names a member of staff, and 1club looks at it.
See [Worked examples](#worked-examples) for the details of each.
## Who controls a review
A review belongs to the member who wrote it. Your gym cannot edit its text or rating, delete it, or hide it: the member chooses whether it is public when they submit it, and can change that later along with the rest of their review. This keeps ratings honest, because a gym cannot quietly remove the unfavorable ones.
What you can do is [report a review](#reporting-a-review) that breaks policy. The review stays visible while 1club decides.
## Where reviews come from
### 1club reviews
Members submit these through the member portal or post-booking prompts. They are tied to a user account, so the **Author** column shows the member's name and email. A review is **Verified** when it is tied to a visit 1Club has a booking for.
### Google reviews
Google reviews sync in for any gym that has a Google place linked. Link it on the gym's page under **Settings** > **Clubs** with **Connect Google Place**. The sync keeps the aggregate Google rating and review count on the gym, and imports recent reviews with their original author name, photo, rating, and text.
Google reviews arrive public and unverified, and you cannot change them. They show on your own website and member portal, never on your [Marketplace](/marketing/marketplace) listing, and they never count toward your Marketplace rating. To respect Google's terms, 1Club refreshes them about weekly and never keeps them longer than 30 days. Disconnecting the Google place removes the stored rating and reviews.
1Club imports Google reviews; it does not post replies back to Google.
## What the page shows
In the admin, reviews live under **Marketing** > **Reviews** (route `/marketing/reviews`). The page opens with stat cards followed by a data grid. The old **Reputation** page was folded into this one, and its address now opens **Reviews**.
### Stat cards
Every card narrows with the grid's filters, so filtering to one instructor or one club gives you that instructor's or club's scores. A card shows a dash (`-`) when nobody has scored that part yet.
* **Average Rating** - The overall rating out of 5, with the number of reviews (and how many are verified, when not all of them are).
* **Coaching** - The coaching score members give alongside the overall rating.
* **Facilities** - The facilities score members give alongside the overall rating. Scored apart from the overall rating, so you can see which of the two carries the other.
* **Response rate** - 1club reviews against visits that actually happened in the last 12 months. A low rate means members are not being asked, not that they are unhappy.
* **Recent Reviews** - Reviews created in the last 30 days.
If you run review-request automations with Google conversion turned on, a sixth card appears:
* **Google conversion** - The share of members shown the Google prompt who clicked through, with the counts behind it. A click is all 1Club can see; whether they actually post depends on their Google account.
### Review grid columns
* **Author** - The member's name and email for 1club reviews, or the reviewer's public name and avatar for Google reviews.
* **Club** - Which gym the review is attached to; some reviews also show an area underneath.
* **Instructor** - The instructor the review is about, if any.
* **Content** - The review title with a two-line clamp of the comment body.
* **Rating** - 1 to 5 stars, with the numeric value alongside.
* **Source** - A **1club** or **Google** chip.
* **Verified** - A chip showing **Verified** or **Unverified**.
* **Public** - A chip showing **Public** or **Private**, as the member chose.
* **Published At** - The publish date, falling back to the creation date.
## Filtering and search
Open the filters drawer above the grid to narrow the list by:
* **Club** - A single club (defaults to all your accessible clubs).
* **Instructor** - Reviews about a specific instructor.
* **Rating** - Exactly 5, 4, 3, 2, or 1 stars.
* **Source** - **1club** or **Google**.
* **Contact** - Reviews tied to a specific member (via the contact picker).
The grid's search box matches on review content. Filters and search are written to the URL, so a filtered view can be bookmarked or shared.
## Reporting a review
Each row's action menu has one option, **Report to 1club**.
1. Open the row's action menu and choose **Report to 1club**.
2. In **Why are you reporting it?**, explain how the review breaks policy (for example abusive language, naming a member of staff, or describing a different venue).
3. Click **Send report**.
The review stays visible while 1club decides; reporting is not a way to take one down. While the report is open, the action reads **Reported** and is disabled. Once 1club has ruled, you can report the review again, for example if the member edits it.
## Review requests (automation)
The way you actively collect more reviews is through [Automations](/marketing/automations). A **review request** step sends a member a link to leave a review after a trigger - most commonly after they check in to a class (`booking.checked_in`). You configure:
* **Channels** - Email, SMS, or both.
* **Instructor voice** (optional) - Include an AI-personalized chat bubble written in a **casual**, **professional**, or **motivational** style, and optionally send it from the instructor's own verified email. The message adapts to how many classes the member has attended (first-timer, getting started, regular, or loyal veteran).
* **Sessions between requests** (optional) - Ask again only once the member has attended N sessions since their last request, counted across every club in your organization. This spaces the ask by engagement rather than by the calendar, so an infrequent visitor is not asked after every single visit. You can also choose whether a brand new member is asked after their very first session.
* **Frequency limit** (optional) - Skip the step if the member has already had N requests in a rolling window, so you never over-ask. This is a ceiling on top of the session interval, and both must allow a request before it goes out.
* **Enable Google review conversion** (optional) - After a member submits their review, invite them to share it on Google. The invitation goes to every member, whatever their rating, because asking only the happy ones breaks Google's review policy. It only shows for gyms connected to a Google Business Profile.
The Google invitations are what the **Google conversion** card on this page counts.
## Worked examples
### Climbing gym comparing coaching and facilities
A climbing gym's **Average Rating** is a healthy 4.5, but the cards beside it show **Coaching** at 4.8 and **Facilities** at 3.9. Filtering to one club shows the gap is all at the older site, so the owner budgets for that site's changing rooms first.
### Padel club lifting its response rate
A padel club sees a **Response rate** of 1% and realises nobody is being asked. It adds a review-request automation triggered on check-in, email plus SMS, with **Enable Google review conversion** on. Over a month the response rate climbs, and the **Google conversion** card shows how many of those reviewers clicked through to Google.
### Jiu-jitsu gym with a personal coach voice
A jiu-jitsu gym enables the instructor voice in **motivational** style and sends from the coach's email. A loyal veteran who just finished their 30th class gets a warm, personal ask that references their dedication, which lands better than a generic template.
### Yoga studio reporting an abusive review
A yoga studio finds a review that names one of its instructors in abusive terms. Staff choose **Report to 1club**, explain that it names a member of staff, and send the report. The review stays up while 1club looks at it, and the row shows **Reported** until there is a ruling.
## Tips & best practices
* **Read the scores filtered** - Filter by instructor or club to see where a strong or weak score really comes from.
* **Watch the response rate** - A low rate is a sign you are not asking, not a bad score. Fix it with a review-request automation.
* **Space requests by sessions, then cap them by time** - Set **Sessions between requests** so the ask tracks how much a member actually trains, and keep a frequency limit as the ceiling for your most frequent attendees. A time cap on its own leaves once-a-month visitors being asked after every visit.
* **Report, don't argue** - For a review that breaks policy, use **Report to 1club** with a clear reason. For one you simply disagree with, the better answer is more reviews from the members you do serve well.
## Troubleshooting
**Issue**: Google reviews are not appearing in the list.
**Solution**: The gym has no Google place linked, or it has not synced yet. Connect the gym to its Google listing on its page under **Settings** > **Clubs** so the sync can run.
**Issue**: A review is missing from the website but shows in the list.
**Solution**: The member set it to **Private**. Only the member can change that, from their own review.
**Issue**: I want to remove or edit a review.
**Solution**: Gyms cannot edit, hide, or delete reviews. If a review breaks policy, choose **Report to 1club** and explain why.
**Issue**: The **Google conversion** card is not showing.
**Solution**: It only appears once at least one member has been shown the Google prompt. Set up a review-request automation with **Enable Google review conversion** turned on and let it run.
**Issue**: A card shows a dash instead of a score.
**Solution**: Nobody has scored that part yet for the current filters. A dash means no data, not a score of zero.
**Issue**: I cannot report reviews, only view them.
**Solution**: Viewing the list and stats needs the `marketing.read.club` permission. Reporting a review needs `marketing.update.club`. Ask an admin to grant it.
## Related
* [Website builder](/marketing/website) - The **Reviews** section renders your public reviews on the site.
* [Automations](/marketing/automations) - Build the review-request step that collects reviews and drives Google conversions.
* [Business audit](/home/ai-visibility-report) - The AI visibility report analyzes your public review sentiment.
* [Marketplace](/marketing/marketplace) - Your network listing, rated on 1club reviews only.
# Website builder
Source: https://docs.1club.ai/marketing/website
Design, translate, and publish your gym's public site from inside the admin. Sections pull live data from the platform, edits live in a draft, releases are versioned.
The Website builder runs your gym's public site without leaving the admin. Each organization has one site made of pages, and each page is made of sections you can add, reorder, and configure. Owners and managers build and publish the site, and visitors and members browse, book, and get in touch through it.
The public website is a **plan add-on**. It's included on the Grow plan,
available as a paid add-on on the Run plan, and unavailable on the Free plan.
Manage it under **Settings** > **Organization** > **Subscription**. While the Website
add-on is inactive, your public site is replaced by a simple unbranded holding
page rather than returning an error.
## When to use it
The site stays accurate without extra work: many sections pull live data from the platform (your plans, classes, instructors, locations, reviews), so they change as your operations change. Edits live in a draft, you publish them as a release, and you can roll back to any previous release.
Typical uses:
* **Let visitors book from your site** - a padel gym adds the **Book** section so visitors filter by day, time of day, and slot length, then book a court in one step.
* **Keep your timetable and prices current** - a yoga studio uses the **Classes** and **Plans** sections instead of pasting its schedule, so the site always shows what is set up in 1Club.
* **Turn visitors into leads** - the contact form creates a lead contact in your CRM and an inbound message your team replies to from the inbox.
* **Redesign safely** - publish a snapshot before a big round of edits, then roll back to it if the new version doesn't work out.
## How the site is structured
A site has three layers:
* **Site** - one per organization. Holds your domain, branding, languages, SEO defaults, analytics, cookie policy, and the list of pages.
* **Page** - a route on your site (`/`, `/about`, `/classes`, etc.). Each page has its own SEO metadata and an ordered list of sections.
* **Section** - a building block on a page (hero, plans, instructors, contact form, gallery). Every section has a **type** that determines what it renders, plus configurable fields.
Every page also includes the site's shared **header** and **footer** sections, so navigation and footer links do not have to be set page-by-page.
## Pages
Pages are free-form. You decide what to add. You can start from the defaults (Home, About, Classes, Calendar, Instructors, Locations, Pricing, Blog, News, FAQ, Contact) or build your own.
Each page has:
* **Title** shown in browser tabs and (by default) as the SEO title.
* **Slug** URL segment. The page marked **Home** lives at `/`; everything else lives at `/`.
* **Home flag** exactly one page is the home page.
* **SEO** meta title, meta description, Open Graph image (per page, per language).
* **Sections** the content of the page, in order.
To add a page, open the website editor and use **Add page**. To remove one, delete it from the page list; links to it will need to be updated manually.
## Sections
Sections are the building blocks. Pick from the catalog when you click **Add section** on a page. Each one has its own properties panel.
### Layout
* **Header** - your site's top bar: site name, logo, primary navigation, optional Login/Sign-up buttons. Navigation supports flat items and nested dropdowns.
* **Footer** - bottom of every page: link groups, contact information, social icons, and legal links.
### Hero and calls to action
* **Hero** banner with a headline, subheadline, one or more CTA buttons, and optional imagery. Four layout variants: **background** (full-width image or video behind the headline), **imageRight** (copy on the left, image on the right), **centered** (copy centered, no media), **gallery** (Airbnb-listing style: headline left, CTA right, image gallery below). The hero accepts a list of background images: the **background** and **imageRight** variants crossfade through them as a slideshow; the **gallery** variant renders them as a tiled gallery.
* **CTAs** focused call-to-action section with title, supporting text, one or more buttons, and an optional image.
* **Quote** centred testimonial or pull quote attributed to a staff member or member.
* **Stats** row of headline numbers ("10 years", "500 members", "4.9 stars") for social proof.
### Live data sections
These pull from the platform automatically. There is nothing to sync; they always reflect what is currently set up in 1Club.
* **Plans** your published [membership plans](/sales/plans), rendered as pricing cards with a CTA to sign up.
* **Classes** class schedule, filterable by class type or limited to a window.
* **Events** your live [events](/events/events), as a featured-events rail (default) or a full list; configurable title, subtitle, and card limit. Self-hides when there is nothing to show. New template sites include it on the home page.
* **Calendar** unified booking calendar combining areas and instructors; visitors can click through to book.
* **Book** a filter-driven search-and-book section for areas, classes, and coaches. Visitors filter by sport/category, day, time of day, slot length, and instructor, then book a slot in one step. (Optional title and subtitle.)
* **Instructors** staff directory. Choose grid (short) or alternating long-form layout.
* **Instructor** spotlight a single instructor by ID or slug.
* **Areas** bookable spaces or facilities, each linking to its booking page.
* **Clubs** location cards with map for organizations with multiple gyms.
* **Club** spotlight a single gym with its address, hours, and map.
* **Location** alternative compact location card.
* **Products** items from your product catalog (day passes, merch).
* **Reviews** Google reviews (from the gym's Google connection, set on each club under **Settings** > **Clubs**) or 1Club [reviews](/marketing/reviews), displayed as cards or a carousel.
* **News** list of posts from your CMS (slug `/news/` for each).
* **FAQ** accordion of questions and answers. Filter by tag if you keep separate FAQ topics.
### Content sections
* **Gallery** grid of images or videos picked from your [Media library](/marketing/media).
* **Block** a reusable content block (rich text managed under [Marketing > Content](/marketing/content)) referenced by slug. Use this for legal text, "About us" paragraphs, or any chunk of copy you want to reuse on multiple pages.
* **Columns** multi-column layout that nests other sections side-by-side. Use it to put a hero next to a contact form, or stats next to a quote.
### Forms
* **Contact form** lead-capture form. You choose the fields to show (name and email are always on; phone, date of birth, gender, category, subject, message are optional). It also supports any custom fields you have defined under **Settings** > **Members** > **Contacts**. Each submission creates a **lead** contact in your CRM and an inbound message you can reply to from the inbox.
### Section anatomy
Under the hood every section is the same simple shape: a **type**, a **config**, and optional **translations**.
* **Type** decides what the section renders (`hero`, `plans`, `gallery`, and so on) and which fields it exposes.
* **Config** holds the section's settings - text, numbers, dropdowns, image references, and structured lists.
* **Translations** store per-locale overrides for the translatable text, keyed by field. Complex lists (nav items, CTA buttons, footer link groups) carry stable per-item IDs, so translations survive when you reorder items.
Two config fields appear on most content sections and are worth knowing:
* **Background tone** (`default`, `muted`, `elevated`, `primary`) sets the section's background. `primary` tints it with a subtle wash of your brand color; use it to make one section stand out without hardcoding colors.
* **Size** (`content` or `full`) controls width: `content` constrains to a centered max-width, `full` runs edge to edge.
Layout section types (`header`, `footer`) live once at the site level and appear on every page. Content section types live in each page's ordered list. The editor keeps these separate for you - you will not see a hero offered as a header, or a footer offered inside a page. This same config schema drives both the manual property panels and the AI builder, so anything you can set by hand, the assistant can set too.
## The editor
Open **Marketing** > **Website** to launch the editor. **Website** is the first Marketing tab and the page Marketing opens on. The editor drops the page header to give you room, but keeps the Marketing column beside it, so the other Marketing tabs stay one click away. It has three regions:
* **Section list (left)** every section on the current page, in order. Drag to reorder. Each row has a context menu: **Edit**, **Move up/down**, **Duplicate**, **Translate**, **Delete**.
* **Live preview (right)** your site renders in an iframe that updates as you edit. Toggle between desktop and mobile widths.
* **Properties panel** opens when you select a section. Shows the section's fields (text, numbers, dropdowns, images, lists). Many fields support inline editing directly on the preview.
Behavior you can rely on:
* **Auto-save.** Edits save automatically about 1.5 seconds after you stop typing.
* **Undo.** The previous saved state of a section can be restored from its menu.
* **Image picking.** Image fields open the [Media library](/marketing/media). Upload new images from the same dialog; they are stored in your media library for reuse.
* **Inline editing.** Click a headline in the preview to edit it in place; click away to commit.
* **Unsaved-changes indicator.** A banner appears when your draft differs from the published release; that is your cue to publish.
There is also an **AI website builder** in the right sidebar. It is covered in full below.
## AI website builder
The AI website builder is an assistant that edits your site by chatting with it. Ask it to "add a pricing page", "make the hero more energetic", "add an FAQ about parking", or "translate the whole site into Spanish", and it makes the changes for you the same way you would in the editor.
What it can do:
* **Build and edit pages and sections.** It adds pages, adds or removes sections at a chosen position, and merges new settings into an existing section.
* **Write copy grounded in your data.** It reads your real organization facts - name, description, clubs, cities, amenities, sports, plans, branding - and writes headlines and body copy from them rather than inventing awards or history.
* **Pick images from your library.** It searches your [Media library](/marketing/media) by name (for example "pool" or "reformer") and sets the match on a hero or CTA. It never uploads on your behalf, so it only uses images you already have.
* **Manage layout, content, and CSS.** It can update the header and footer, create or edit reusable content items (blocks, FAQs, posts), and adjust custom CSS using theme tokens (never hardcoded colors).
* **Translate.** It fills missing translations per section across your locales.
* **Undo.** It keeps a short history of its own changes so you can revert the last one.
What it will not do:
* **It only edits the draft.** Like every edit in the builder, the assistant's changes stay in the draft. Nothing goes live until *you* publish a release.
* **It cannot publish, reset, or delete the site.** Those actions are yours alone, from the UI.
* **It respects the layout-versus-content rule.** It will not put a header inside a page or a hero into the site layout, and it always sets required fields (for example, a hero's variant).
### Starter content generation
When you first create a site from a template, the builder seeds it with copy written from your organization's stored facts: a home hero headline, a hero subheadline, and an About-page intro, generated in your default language. If generation is unavailable, the site falls back to sensible default copy. Either way, a brand-new site reads like *your* site from day one instead of showing generic placeholders you have to overwrite.
## Theming and branding
The website uses your **Settings** > **Member Apps** > **Branding** as the source of truth (logo, favicon, primary color, fonts). The site adds a few extras on top:
* **Header / dark color overrides** for tuning contrast.
* **Theme support** light only, dark only, or both (with a user-facing theme switcher). Pick the **default theme** when you allow both.
* **Custom CSS** site-level CSS for fine-tuning. Lives on the release, so it is versioned with the rest of the site.
## Navigation
The header section configures your top navigation:
* **Nav items** label + URL. Items can have **children** to render as a dropdown menu.
* **Site name / logo** sourced from branding by default; override per site if needed.
* **Login / Sign-up buttons** appear automatically when [authentication](/settings/overview) is enabled for the site.
The footer section configures footer links, grouped into categories (e.g. *Company*, *Programs*, *Legal*) plus contact info, social links, and legal links.
## Multi-language
A site can be published in several languages.
* **Supported locales** and a **default locale** are configured on the site.
* Each translatable field on a section or page (headline, subtitle, CTA label, meta title) has per-locale values.
* **AI translate** fills missing translations across all locales at once. Run it after you change the source-language copy to keep everything in sync. Review the suggested translations before publishing.
* Visitors see a language switcher in the header when more than one language is published.
## SEO and metadata
Per page: meta title (defaults to the page title), meta description, Open Graph image, and translations of each per locale.
Site-wide, the site automatically serves a `sitemap.xml` and `robots.txt`. Canonical URLs are generated from your domain and page slugs.
## Analytics and tracking
Configure analytics from the site settings panel:
* **Google Analytics** property ID (and any other tracking IDs you use).
* **Custom scripts** for pixels and tag managers.
Scripts can be gated behind cookie consent categories. *Necessary* scripts run always; *analytics* and *marketing* scripts only run after the visitor accepts the matching category in the cookie banner.
## Cookies and legal
Built into the site:
* **Cookie banner** with category-level consent (necessary / analytics / marketing).
* **Legal documents** privacy policy, terms, and cookie policy. Write them as documents under [Settings > Bookings > Waivers](/settings/waivers); the site links to them by slug.
## Domain and hosting
* Every site has a default 1Club URL generated from your organization slug.
* You can attach a **custom domain** in the site settings; point your DNS to 1Club following the on-screen instructions.
* HTTPS is provisioned automatically.
### Holding page
The public website is a plan add-on. If the add-on becomes inactive while you still have published content or a custom domain pointed at 1Club, visitors do not see an error or a 404. Instead, 1Club serves a generic, unbranded **holding page** that says the site is temporarily unavailable. It protects your public reputation, avoids leaking 1Club branding, and disappears the moment the add-on is active again - your live site returns exactly as it was, from the last active release. Reactivate the add-on under **Settings** > **Organization** > **Subscription** to bring the site back.
## Publishing and releases
Edits do not go live until you publish.
1. **Draft** everything you change in the editor is stored as the working draft.
2. **Publish** opens a dialog with two modes:
* **Amend** (default) updates the active release in place. Use this for typo fixes, small copy tweaks, or routine content updates. No new version is created.
* **Snapshot** creates a new versioned release. Use this for bigger changes you may want to roll back from cleanly.
3. **Active release** what the public site is serving right now.
4. **Releases history** every snapshot is kept. Open the history panel to view, compare, or **roll back** to a previous release; the chosen release becomes the active one.
5. **Rollback to no release** is the closest thing to an unpublish - use the history panel to roll back to an earlier release, or contact support if you need the site fully offline.
You can leave notes on each release ("Spring campaign launch", "Updated pricing page") to make the history easier to scan.
## Forms and leads
Contact-form submissions are first-class CRM events. Each submission:
* Creates a **contact** in 1Club with type *lead*, including any custom fields the form collected.
* Posts an **inbound message** to your staff inbox, so someone can reply.
* Is de-duplicated by email; if a contact with that email already exists, the form returns a friendly conflict rather than creating a duplicate.
From there you can convert the lead into a member, work it on the [Leads](/members/leads) board, tag them for [smart segmentation](/settings/smart-tagging), or send them a [promotion](/sales/promotions).
## Tips
Start from defaults, then prune. Add the prebuilt pages, delete the ones you do not need, and edit the rest. Faster than building from scratch.
Use live-data sections for anything that changes. Do not paste your class schedule into a hero; use the **Classes** or **Calendar** section so updates flow automatically.
Lead with one strong CTA per page. Hero CTA plus one or two supporting CTAs is plenty; more dilutes the action you want visitors to take.
Use Columns to break monotony. Pair a stats block with a quote, or a hero with a contact form, instead of stacking full-width sections everywhere.
Publish as Snapshot before big changes. Take a snapshot of the current site first; if the next round of edits goes sideways you can roll back instantly.
Translate after you stabilize copy. Running AI translation early means redoing it when you keep tweaking the English. Lock the source copy, then translate.
Wire the contact form to a tag. Tag new leads from the website so [automations](/marketing/automations) and [promotions](/sales/promotions) can find them.
## Troubleshooting
| Symptom | Likely cause | Fix |
| - | - | - |
| Edits don't show on the live site | Changes are saved to the draft, not published | Open the publish dialog and publish a release (**Amend** for small tweaks, **Snapshot** for bigger ones) |
| Publish button is disabled or fails | Publishing and rollback require an active **Website** add-on | Activate or renew the add-on under **Settings** > **Organization** > **Subscription**, then publish again |
| Public site shows a plain "temporarily unavailable" page | The Website add-on is inactive, so the holding page is served | Reactivate the add-on under **Settings** > **Organization** > **Subscription**; your last active release returns automatically |
| A live-data section is empty or missing | There is no data to show yet (no published plans, no live events) | Publish the underlying records; sections like **Events** self-hide until there is something to show |
| New languages show English text | Translations were not filled after you changed the source copy | Run **AI translate**, review the results, then publish |
| A plans link points somewhere odd | Plans always link to `/plans`; there is no `/pricing` page | Leave plan CTAs pointing at `/plans` and do not create a `/pricing` route |
## Related
* [Content](/marketing/content) - Blocks referenced from the website and posts surfaced by the News section.
* [Media](/marketing/media) - Images and videos used by site sections.
* [Reviews](/marketing/reviews) - Review pipeline that feeds the **Reviews** section.
* [Settings overview](/settings/overview) - Branding, member portal, and other site-adjacent configuration.
* [Plans](/sales/plans) - What the **Plans** section renders.
* [Smart tagging](/settings/smart-tagging) - Auto-tag the leads your contact form creates.
# Connect an assistant
Source: https://docs.1club.ai/mcp/connect
Connect Claude, ChatGPT, or another remote MCP client to 1Club with OAuth, and keep an existing API-key connection working.
Connect your assistant directly to 1Club with OAuth. You do not need to create, copy, or store an API token. If you already connected an assistant with an API key, that connection keeps working - see [Existing API-key connections](#existing-api-key-connections).
## Before you start
You need:
* A 1Club account with access to the organization you want to connect.
* The **settings.manage.organization** permission, which is what lets you authorize a connection. Owners and admins hold it.
* A paid 1Club plan. MCP and API access are locked on free plans and during a trial.
* A client that supports remote MCP servers over HTTP, with OAuth.
The admin MCP address is:
```text theme={null}
https://mcp.1club.ai/admin
```
The public discovery server is `https://mcp.1club.ai/discovery`. It searches published marketplace data and does not require sign-in.
## Connect the admin server
Open your assistant's apps, connectors, or MCP settings. Create a custom remote MCP connection and enter `https://mcp.1club.ai/admin`.
Select **Connect**, **Authenticate**, or the equivalent action. The client discovers 1Club's OAuth configuration automatically.
Sign in on the 1Club authorization page. If you manage more than one organization, pick the one the assistant should access. A connection is bound to exactly one organization.
The page lists the read access the client asked for under **This assistant will be able to**. Everything that changes data appears below it, under **Also allow it to make changes?**, as its own checkbox that starts unticked.
Leave every box unticked and the assistant gets read-only access, even if the client asked for everything. Tick only the capabilities you want it to have, then select **Allow access to** your organization.
Ask: "Which 1Club organization are you connected to?" The assistant should call `get_account` and return the selected organization and granted scopes.
**Allow** stays disabled until at least one capability is granted. That only
bites when a client requests no read access at all: it has nothing to list
under **This assistant will be able to**, so you have to tick one of its write
boxes to connect it. Every client that asks for any read scope can be approved
with nothing ticked.
Only connect assistants you trust. Review tool confirmations carefully before
allowing a write or delete action.
## Claude
Claude and Claude Desktop support OAuth-based remote MCP connectors.
1. Open **Settings** > **Connectors**.
2. Select **Add custom connector**.
3. Enter a name such as "1Club" and the URL `https://mcp.1club.ai/admin`.
4. Add the connector, then select **Connect**.
5. Complete the 1Club sign-in and authorization flow.
6. Enable the 1Club tools from Claude's tools menu when you want to use them.
Team and Enterprise workspace owners may need to add the connector for the organization before individual users can connect it. See [Anthropic's remote MCP connector guide](https://support.anthropic.com/en/articles/11175166-about-custom-integrations-using-remote-mcp).
## Claude Code
Claude Code speaks OAuth over HTTP MCP, so one command is enough and no header is needed:
```bash theme={null}
claude mcp add --transport http 1club-admin https://mcp.1club.ai/admin
```
The sign-in and authorization pages open when you first use the server. Run `claude mcp list` to confirm it is connected. For the anonymous marketplace server, use the same command with `https://mcp.1club.ai/discovery`.
## ChatGPT
ChatGPT exposes custom MCP connections as apps. Availability and write-action support depend on your ChatGPT plan and workspace settings.
1. Ask a workspace admin to enable developer mode or custom MCP apps if required.
2. Open **Settings** > **Apps** and create a custom app.
3. Enter `https://mcp.1club.ai/admin` as the MCP endpoint.
4. Choose OAuth authentication, then scan the tools.
5. Complete the 1Club sign-in and authorization flow.
6. Enable the app and select it from the tools menu in a new chat.
See [OpenAI's developer mode and MCP apps guide](https://help.openai.com/en/articles/12584461-developer-mode-apps-and-full-mcp-connectors-in-chatgpt-beta) for current plan and workspace requirements.
## Other remote MCP clients
Use the same endpoint and choose OAuth when the client asks how to authenticate:
```text theme={null}
https://mcp.1club.ai/admin
```
1Club supports dynamic client registration, the authorization code flow with PKCE, access-token refresh, and revocation. A compatible client discovers all of it from the endpoint alone, with no manual URLs and no client secret.
If you are building the client rather than configuring one, this is what it will find:
| Detail | Value |
| - | - |
| Transport | Streamable HTTP. JSON-RPC over `POST`, stateless, no SSE stream |
| Resource metadata | `https://mcp.1club.ai/.well-known/oauth-protected-resource` |
| Authorization server | `https://auth.1club.ai`, named by that metadata document rather than by the 401 itself |
| Client authentication | None. Clients are public and register themselves |
| PKCE | Required, `S256` only |
| Resource indicator | `https://mcp.1club.ai/admin`, required on the authorize and token requests |
| Grant types | `authorization_code`, `refresh_token` |
A request without a valid bearer token gets a `401` whose `WWW-Authenticate` header names the resource metadata document. That is the normal way to start the flow, not an error to work around.
## Existing API-key connections
OAuth is the recommended path and the one to use for a new connection. An assistant that was connected with a [Platform API key](/settings/api-tokens) before OAuth existed keeps working: the admin server accepts any bearer credential the Platform API accepts, so a `1club_sk_live_...` key sent on `Authorization` still authenticates. This path stays available for developer-managed clients that cannot complete an interactive sign-in.
The remote configuration is the same endpoint with a request header:
```text theme={null}
Authorization: Bearer 1club_sk_live_your_token_here
```
The key's own scopes decide what the assistant can do, exactly as they do for a direct API call, and revoking the key under **Settings** > **Organization** > **APIs & webhooks** cuts the assistant off.
To move an existing connection to OAuth:
Delete the custom connector, app, or MCP entry in the client. Clients cache server metadata, so re-adding it is more reliable than editing it in place.
Follow [Connect the admin server](#connect-the-admin-server) above. Choose OAuth when the client asks how to authenticate, and do not enter any header.
Once `get_account` returns the right organization over the new connection, revoke the API key that assistant was using, so the credential stops working everywhere.
## Local stdio setup
A developer can also run the MCP server locally over stdio, which is what an editor or a script on your own machine talks to. This path has no OAuth: it authenticates with a [Platform API key](/settings/api-tokens) and is meant for local development.
The server is not published to a package registry, so build it from the `mcp-server` workspace of the product repository and start it with `npm start`. It reads these environment variables:
| Variable | Purpose |
| - | - |
| `ONECLUB_API_KEY` | Your `1club_sk_...` key. The admin surface refuses to start without it |
| `BASE_URL` | The API to call. Set it to `https://api.1club.ai`, or it defaults to `http://localhost:8000` |
| `ONECLUB_MCP_SURFACE` | `admin` or `discovery`. Defaults to `admin` when a key is set and `discovery` when it is not |
Set `BASE_URL` explicitly. Left unset it points at a local API rather than failing, so an assistant aimed at production will simply find nothing there.
Point your client's stdio command at the built entry point. There is no global binary to install.
## Verify access
Confirm these checks after connecting:
1. Ask the assistant to call `get_account`. Confirm the organization is correct, and read the reach from `effectiveScopes` rather than `scopes`.
2. Ask for next week's classes. This confirms `classes:read` access.
3. Ask the assistant to list recent check-ins. This confirms `checkins:read` access.
4. Before testing a write, use a disposable record and review the tool confirmation.
## Review connected assistants
Go to **Settings** > **Artificial Intelligence** > **AI assistants (MCP)**. The page shows the endpoint to hand to a client, and lists every assistant connected to this organization with who authorized it, the scopes it holds, and when it was last used.
Connections appear here no matter who created them, because an assistant is authorized by whoever connects it rather than created on this page. Seeing what has access, and cutting it off, is what the page is for. Viewing it needs **settings.read.organization**; revoking needs **settings.manage.organization**.
## Disconnect or revoke
* Disconnecting in the MCP client removes the connection from that client.
* Revoking it in 1Club ends access immediately. The client must complete OAuth again to reconnect.
* There is nothing to rotate on an OAuth connection. The access and refresh credentials are managed by the client and 1Club, and never handled by you. An access token lasts an hour, and the client refreshes it silently. Each refresh issues a fresh pair, so an assistant in regular use stays connected indefinitely and 30 days is the idle limit, not a deadline.
* **An approved scope set cannot be edited afterwards.** To give an assistant a capability you did not tick, or to take one back, revoke the connection and authorize it again with the boxes you want.
Two things also revoke a connection without anyone pressing a button:
* **The authorizing person losing access.** A connection carries the permission of whoever approved it. If that person loses **settings.manage.organization**, or leaves the organization, the connection is revoked the next time the assistant calls a tool.
* **Long disuse.** A client that has not been used for 90 days is deleted along with its tokens, so it has to register and authorize again.
If setup fails, see [MCP troubleshooting](/mcp/troubleshooting).
# MCP overview
Source: https://docs.1club.ai/mcp/overview
Connect Claude, ChatGPT, and other AI assistants to 1Club through the Model Context Protocol, so they can answer questions and take actions against your gym's real data.
The Model Context Protocol (MCP) is an open standard that lets an AI assistant call real software instead of guessing. 1Club runs MCP servers so an assistant like Claude or ChatGPT can look up your classes, check availability, read your transactions, and create bookings, using your own data and your own permissions.
You connect once, and from then on you can ask an assistant things like "which classes next week still have space?" or "book court 2 on Thursday at 18:00 for Maria" and it uses 1Club to answer, rather than making something up.
## Two servers, two audiences
1Club exposes **two separate MCP servers**. Each has its own address, and an assistant connected to one never sees the other's tools.
| Server | Address | Who it is for | Authentication |
| - | - | - | - |
| **Admin** | `https://mcp.1club.ai/admin` | Your gym's staff and internal systems | 1Club OAuth sign-in, or a Platform API key |
| **Discovery** | `https://mcp.1club.ai/discovery` | Anyone searching the public 1club.me marketplace | None |
* The **admin server** reads and writes one organization's own data. You sign in to 1Club, choose the organization, and approve the requested access. Every response is limited to that organization. This is the one you want for running your gym. A developer-managed client that cannot sign in interactively can send a [Platform API key](/settings/api-tokens) as a bearer token instead; OAuth is the recommended path for everyone else.
* The **discovery server** searches the public marketplace across all listed gyms and finds bookable court times. It is anonymous by design, read-only, and can never reach a single organization's private data.
Pick the one that matches what you are trying to do, then follow [Connect an assistant](/mcp/connect).
The root address `https://mcp.1club.ai` is not itself a connectable server. If
you point a client at it, the connection fails with a message naming the two
addresses above. Always include `/admin` or `/discovery`.
## What an assistant can do
The admin server covers your operations, your catalogue, and your website:
* **Classes** - list, create, update, and delete class occurrences, and read a class roster.
* **Areas and availability** - list bookable areas, check when they are free, and block or reopen a court.
* **Plans** - find, create, update, retire, and delete membership plans.
* **Memberships** - create and update customer memberships.
* **Bookings** - create, cancel, and inspect bookings and their participants.
* **Check-ins** - list attendance and check in an existing booking.
* **Contacts and instructors** - resolve customers and coaches, and create contacts.
* **Products** - search the catalogue, reprice or retire a product, manage categories, and see how many are left to sell.
* **Website** - read and edit pages, sections, navigation, and custom CSS, then publish or roll back.
* **Media** - browse the media library, register hosted images and videos, and edit their alt text.
* **Content** - read, create, update, and delete CMS content such as posts, FAQs, documents, and reusable blocks.
* **Gyms** - read a gym's details, amenities, and rating.
* **Transactions** - list and open billing transactions (the records the admin calls [Charges](/sales/charges)).
The full list, with the OAuth scope each tool needs, is on the [Tools reference](/mcp/tools) page.
Website edits go to a draft, so nothing an assistant changes is public until it
publishes, and publishing is a scope of its own that you can withhold while
still letting the assistant edit. The one exception is the languages your
organization offers: those are organization-wide, need the separate
`organization:write` scope, and take effect immediately.
The discovery server answers three questions about the public marketplace: when can I play (`find_availability`, which returns priced, dated court times with a link that opens the booking), which clubs match what I am after (`search_clubs`), and what is this place like (`get_club`). It reads only published information and never writes - a booking is completed on 1club.me.
## Current boundaries
* **There is no member-facing server.** The admin connection acts on an organization. It does not sign in as a member or expose a member's private portal session.
* **MCP follows the Platform API.** Messaging, automations, invoices and payments, access control, staff administration, and reporting are not exposed. Anything absent from the [Tools reference](/mcp/tools) is unreachable through MCP today.
* **Check-in creation is booking-based.** The assistant can check in an existing booking. Front-desk walk-ins and payment collection remain in 1Club.
* **Stock is read-only.** An assistant can see how many of a product are left to sell, but receiving a delivery, correcting a count, and writing off damage stay in 1Club.
* **Some things are deliberately not deletable.** Contacts, memberships, and products have no delete tool. Retire a product with its update tool instead, which takes it off the till and keeps its sales history.
## Security model
* **OAuth keeps credentials out of setup.** You sign in directly on 1Club's authorization page. You do not paste a password or API token into the assistant.
* **Read-only is the default.** A client that requests no scopes gets a read-only connection, and every write capability a client does request is a checkbox on the authorization page that starts unchecked. Approving without ticking anything leaves the assistant able to look things up and nothing more.
* **Anything beyond reading is a deliberate opt-in, per capability.** Tick only what the assistant actually needs. That covers the writes and the three that go further - `media:delete` removes a file permanently, `website:publish` makes draft edits public, and `organization:write` changes settings such as your languages everywhere at once and immediately, rather than into a draft. Content the assistant reads - a member note, a website page - can contain text that tries to steer it, so these belong only on a connection you supervise.
* **Scopes are the limit.** An assistant can only use tools its granted scopes allow. A connection with `classes:read` cannot create or update a class. The set cannot be changed after approval: revoke the connection and authorize it again with different boxes ticked.
* **A connection never outlives the person behind it.** It carries the permission of whoever authorized it. If they lose **settings.manage.organization** or leave the organization, the connection is revoked on its next tool call. A client unused for 90 days is dropped as well.
* **Revocation is instant.** Revoking a connection under **Settings** > **Artificial Intelligence** > **AI assistants (MCP)** cuts the assistant off immediately.
* **Credentials are short-lived and rotated.** An access token is valid for an hour, and each silent refresh issues a fresh pair. A connection left idle for 30 days expires; one in regular use does not. Only hashes are ever stored, and replaying a spent refresh token revokes the entire connection.
* **Rate limits apply.** MCP calls count against the Platform API's limit of 100 requests per minute per organization.
## Before you start
* API and MCP access are **paid features**, locked on free plans and during a trial. The discovery server remains public and needs no sign-in.
* Authorizing a connection requires the **settings.manage.organization** permission. In practice that means admins and owners.
* Your MCP client must support remote HTTP MCP servers, and OAuth for the recommended setup. A client that can only send a fixed `Authorization` header can use a [Platform API key](/settings/api-tokens) instead. [Connect an assistant](/mcp/connect) covers both.
## Next steps
Step-by-step OAuth setup for Claude, ChatGPT, and other remote MCP clients.
Every tool on both servers, and the OAuth scope it needs.
What to do when the connection or a tool call fails.
See which assistants have access to your organization, and revoke one.
# Tools reference
Source: https://docs.1club.ai/mcp/tools
Every tool the 1Club admin and discovery MCP servers expose, what it returns, and the OAuth scope each one requires.
This is the complete tool list for both 1Club MCP servers. An assistant sees only the tools of the server it is connected to.
## Admin server
`https://mcp.1club.ai/admin` - authenticated, normally with 1Club OAuth. Every tool is scoped to one organization and needs the listed scope. A call whose scope is missing fails with a permission error, not with empty data.
| Tool | What it does | Scope required |
| - | - | - |
| `get_account` | Names the authorized organization and lists its granted scopes | None |
| `list_classes` | Lists your classes | `classes:read` |
| `get_class` | Opens one class by id | `classes:read` |
| `list_class_bookings` | Lists a class roster, with each attendee's status and what they paid | `bookings:read` |
| `create_class` | Creates one class occurrence | `classes:write` |
| `update_class` | Updates one class occurrence | `classes:write` |
| `delete_class` | Deletes one class or this and future recurring occurrences | `classes:write` |
| `list_class_types` | Lists your class types, with sport, age group, level, and the track each counts toward | `classes:read` |
| `get_class_type` | Opens one class type by id | `classes:read` |
| `create_class_type` | Adds a class type | `classes:write` |
| `update_class_type` | Changes a class type, including its age group, level, and track | `classes:write` |
| `list_progression_tracks` | Lists your progression tracks and the class types counting toward each | `classes:read` |
| `list_areas` | Lists your bookable areas | `areas:read` |
| `get_platform_area_availability` | Checks when an area is free, for a date or a date range | `areas:read` |
| `list_area_blocks` | Lists area blocks overlapping a date range | `areas:read` |
| `create_area_block` | Closes one court, or a whole gym, for a time range | `areas:write` |
| `delete_area_block` | Deletes a block, reopening the courts it covered | `areas:write` |
| `search_contacts` | Finds customers by name, email, or phone | `contacts:read` |
| `get_contact` | Opens one customer by id | `contacts:read` |
| `create_contact` | Creates a customer | `contacts:write` |
| `search_instructors` | Finds coaches by name, gym, or sport | `instructors:read` |
| `get_instructor` | Opens one coach by id, with hourly rate and bookability | `instructors:read` |
| `search_plans` | Finds membership plans the gym sells, with price and joining fee | `plans:read` |
| `get_plan` | Opens one plan by id | `plans:read` |
| `create_plan` | Creates a membership plan | `plans:write` |
| `update_plan` | Updates or retires a membership plan | `plans:write` |
| `delete_plan` | Deletes an unused membership plan | `plans:write` |
| `list_memberships` | Lists memberships, with sessions remaining | `memberships:read` |
| `get_membership` | Opens one membership by id | `memberships:read` |
| `create_membership` | Puts a customer on a plan | `memberships:write` |
| `update_membership` | Pauses, resumes, or cancels a membership, or adjusts its allowance | `memberships:write` |
| `search_products` | Finds products by name, description, SKU, or barcode | `products:read` |
| `get_product` | Opens one product by id | `products:read` |
| `create_product` | Adds a product to the catalogue | `products:write` |
| `update_product` | Reprices, recategorises, or retires a product | `products:write` |
| `get_product_availability` | Reports how many of a product are left to sell, per gym | `products:read` |
| `list_product_categories` | Lists the product categories, in display order | `products:read` |
| `create_product_category` | Adds a product category | `products:write` |
| `update_product_category` | Renames a category or moves it in the display order | `products:write` |
| `list_checkins` | Lists attendance records with filters | `checkins:read` |
| `get_checkin` | Opens one attendance record | `checkins:read` |
| `create_checkin` | Checks in an existing booking | `checkins:write` |
| `get_booking` | Opens one booking by id | `bookings:read` |
| `list_bookings` | Lists bookings in a date window, across every channel | `bookings:read` |
| `create_booking` | Creates a booking, optionally with a coach | `bookings:write` |
| `cancel_booking` | Cancels a booking | `bookings:write` |
| `list_booking_participants` | Lists the extra players on a booking | `bookings:read` |
| `add_booking_participant` | Adds a player to a booking | `bookings:write` |
| `remove_booking_participant` | Removes a player from a booking | `bookings:write` |
| `list_clubs` | Lists your gyms with their slug, status, and booking labels | `clubs:read` |
| `get_club` | Opens one of your gyms by slug, with amenities, rating, and booking labels | `clubs:read` |
| `update_club_booking_labels` | Renames a gym's private training, group activities, and areas booking cards, and translates them | `organization:write` |
| `list_content` | Lists CMS content - posts, FAQs, documents, and reusable blocks | `content:read` |
| `get_content` | Opens one published content item by slug | `content:read` |
| `create_content` | Creates a content item | `content:write` |
| `update_content` | Updates a content item, including taking it back to draft | `content:write` |
| `delete_content` | Permanently deletes a content item | `content:write` |
| `get_website` | Opens the website: pages, layout, theme, and its public and preview URLs | `website:read` |
| `list_websites` | Lists every website the organization owns, its default one first | `website:read` |
| `get_website_section_types` | Lists every section type a page can use, and the fields each takes | `website:read` |
| `get_website_page` | Opens one page with all of its sections and settings | `website:read` |
| `get_website_css` | Reads the website's custom stylesheet | `website:read` |
| `list_website_releases` | Lists the publish history, newest first | `website:read` |
| `update_website` | Updates site-level settings: name, theme support, SEO defaults, and the languages the organization offers | `website:write`, plus `organization:write` for the language fields |
| `create_website_page` | Adds a page to the website | `website:write` |
| `update_website_page` | Updates a page's title, SEO fields, or entire content | `website:write` |
| `delete_website_page` | Deletes a page | `website:write` |
| `update_website_section` | Changes one section on a page, leaving the rest alone | `website:write` |
| `update_website_layout` | Updates the header or footer, including navigation links | `website:write` |
| `set_website_css` | Replaces the website's custom stylesheet | `website:write` |
| `publish_website` | Publishes the draft, making every pending edit public | `website:publish` |
| `rollback_website` | Restores an earlier published version and puts it live | `website:publish` |
| `list_media` | Browses or searches the media library | `media:read` |
| `create_media` | Registers an already-hosted image or video in the library | `media:write` |
| `update_media` | Updates a media item's name, alt text, or caption | `media:write` |
| `delete_media` | Permanently deletes a file from the library | `media:delete` |
| `list_transactions` | Lists billing transactions, newest first | `transactions:read` |
| `get_transaction` | Opens one transaction by id | `transactions:read` |
| `create_transaction` | Records a settlement: revenue collected outside 1club, as one sum for a gym | `transactions:write` |
| `refund_transaction` | Records money given back out of a settlement | `transactions:write` |
The transaction tools work on the records the 1Club admin and member apps call
**Charges**. The tool and scope names keep the Platform API's word. See
[Charges](/sales/charges).
`get_account` needs no resource scope, so it works with any valid connection.
That makes it the right first call when an assistant is behaving as if it has
no access: if `get_account` succeeds, authentication worked and the problem is
usually the granted scopes.
**The website tools edit a draft.** Every change is invisible to the public
until `publish_website` runs, which is why publishing is its own scope. An
assistant that holds `website:write` but not `website:publish` can build a page
and hand you the preview URL, and cannot put it live. Read
`get_website_section_types` before composing a page, and take image URLs from
`list_media` rather than guessing them.
`supportedLocales` and `defaultLocale` on `update_website` are the exception.
They are organization-wide rather than website content, so they need
`organization:write` as well and apply immediately instead of waiting for a
publish - the same languages drive the member portal, the mobile app, and
outbound email.
Anything sold by name takes two steps. Booking someone is `search_contacts`
then `create_booking`; selling a membership is `search_contacts`,
`search_plans`, then `create_membership`. Read the plan first and you know the
real total, since a plan can charge a joining fee on top of its price. When a
name matches more than one person or plan, the assistant should ask which you
mean rather than guessing.
**Booking labels are titles only, and they are per gym.** Ask an assistant to
"call private training Coaching" and it changes the card title on that gym's
website, member portal, mobile app, and 1club.me page at once, with no publish
step. The line under each card stays 1Club's, and a card named after an area
type keeps that type's name. With more than one gym, say which one, or the
assistant should ask. A good assistant also translates the new title into the
languages you offer. See [Booking labels](/reference/organization-clubs#booking-labels).
### Reading the scope table
Scopes follow the `resource:action` format used across the [Platform API](/api-reference/introduction). A connection carrying `bookings:write` does not automatically carry `bookings:read`; authorize both if the assistant should look a booking up as well as create one.
### How a connection gets its scopes
The MCP client asks for a scope set, and you decide how much of that request to grant:
* **A client that requests nothing gets a read-only connection.** Every read scope in the table above, and nothing that changes data.
* **Requested read scopes are granted when you approve.** They are listed under "This assistant will be able to" on the authorization page.
* **Everything that is not a read is opt-in, one at a time.** Each appears under "Also allow it to make changes?" as a checkbox that starts unchecked - the writes, and also `media:delete` and `website:publish`, which delete a file for good and make draft edits public. Approving the page without ticking any of them produces a read-only connection, even if the client asked for everything.
* **The set is fixed once approved.** To add or remove a scope, revoke the connection and authorize it again - see [Disconnect or revoke](/mcp/connect#disconnect-or-revoke).
An assistant that authenticates with a [Platform API key](/settings/api-tokens) instead of OAuth uses that key's scopes, which you edit on the token itself.
The wildcard scope `*` means "everything", and the two credential types treat
it differently. On a Platform API key it is stored as-is and satisfies every
current and future scope. MCP OAuth accepts a request for `*` but never stores
it: the wildcard is expanded into the concrete scopes that exist at that
moment, so the authorization page can list each one and you can leave the
destructive ones unticked. A connection therefore does not silently gain new
powers as 1Club adds scopes - reconnect when you want the newer ones. For a
key, [explicit scopes are the better
choice](/settings/api-tokens#token-scopes) unless the integration is fully
trusted and internally managed.
Each admin tool is a thin wrapper over the documented Platform API, so the request limits, error shapes, and field meanings are the same as the [API reference](/api-reference/introduction).
## Discovery server
`https://mcp.1club.ai/discovery` - no token, no scopes. These search the public 1club.me marketplace across every listed gym and read only published information.
| Tool | What it does |
| - | - |
| `find_availability` | Free, priced court times across the marketplace in one call, each with a link that opens the booking |
| `search_clubs` | Finds gyms by name, sport, location, distance, price, rating, amenities, date, and the age group and level of their classes |
| `get_club` | One club's profile and the menu of what it sells - courts by the hour, its classes, its passes |
Reach for `find_availability` for any "when can I play" question. It searches, prices and dates in one step, so there is no need to find clubs first and then look their availability up separately. Give it a `date` when someone names a day - without one it walks forward from today and may never reach the day they meant.
Everything these tools return is the club's own: times are on the venue's clock (`localDay`, `localTime`, `localEndTime`, `localEndDay`, with its `timezone`), and prices come with their currency. Report them as given rather than converting them.
Tools address clubs by their marketplace **slug**, which is what comes back from a search - not the ids an assistant sees on the admin server. Do not mix ids between the two servers.
**Removed in September 2026:** `get_class_availability`, `get_area_availability` and
`get_instructor_availability`. They addressed classes, areas and instructors by database
id, which nobody holds in a conversation, and they overlapped with `find_availability`.
Move any client that called them to the three tools above - `find_availability` answers
the same questions from a place or a club slug instead of an id.
### Optional inline interface
The three discovery tools also carry a small inline interface - club cards, slot chips, a club profile - for a host that supports MCP Apps, such as ChatGPT. A client that does not render it is unaffected: every tool still answers in text, and the interface is only ever a view of the same answer.
Nothing on this server writes. A booking is completed on 1club.me, which is why every free slot comes with the `url` that opens it there.
## What is not exposed
The admin server is built strictly on the documented Platform API, so anything that API does not cover is unreachable through MCP today. That currently includes messaging and the inbox, automations and promotions, invoices and payments, access control, and reporting.
Contacts and memberships are readable and creatable but not deletable. Instructors are read-only and are configured in 1Club. Plans and individual class occurrences can be managed through their write tools; recurring-series creation remains in the 1Club dashboard.
Media can be registered but not uploaded: `create_media` records a file that is already hosted, so putting a new image online still happens in 1Club or through your own hosting.
Products and product categories can be created, edited, and retired, but not deleted - deleting a product destroys its sales history. Stock is read-only: `get_product_availability` reports how many are left to sell, while booking a delivery in, correcting a count after a stocktake, and writing off damaged stock stay in the 1Club dashboard. See [Products](/api-reference/channel-integration/products).
If you need one of these, the [Platform API](/api-reference/introduction) is where coverage grows first, and MCP follows it.
# MCP troubleshooting
Source: https://docs.1club.ai/mcp/troubleshooting
Fix common OAuth, scope, tool-list, and connection problems when using the 1Club MCP server.
Work through the symptom that matches what you see. If it still fails, contact [support@1club.ai](mailto:support@1club.ai) with the MCP client name, the endpoint, and the exact error text.
## The client does not open 1Club sign-in
Check these items:
1. The endpoint is exactly `https://mcp.1club.ai/admin`.
2. The client supports remote MCP servers with OAuth.
3. OAuth is selected as the authentication method, if the client asks.
4. Pop-ups are allowed for the client and `auth.1club.ai`.
5. A workspace administrator has enabled custom MCP apps or connectors when the client requires it.
Remove and re-add the connection after changing its authentication settings. MCP clients often cache the server metadata from the first setup attempt.
## Sign-in succeeds but authorization is denied
Authorizing an MCP connection requires organization settings management permission.
* Confirm you selected an organization where you are an admin or owner.
* Ask an owner to grant the required permission if you can use the dashboard but cannot authorize MCP.
* If the organization is on a free plan or trial, upgrade before connecting the admin MCP server.
The discovery server at `https://mcp.1club.ai/discovery` remains public and does not require authorization.
## A tool fails with a permission error
Ask the assistant to call `get_account`. It returns the authorized organization and the scopes on the connection. Read `effectiveScopes`, not `scopes`: a full-access API key reports `scopes` as `["*"]`, and only `effectiveScopes` spells out what that expands to. Compare that list with the [tools reference](/mcp/tools).
An OAuth grant's scopes are fixed once you approve them - there is no way to widen or narrow a live connection. To change them, revoke the connection under **Settings** > **Artificial Intelligence** > **AI assistants (MCP)**, disconnect it in the client, and authorize it again, ticking the write permissions you need this time.
If the assistant authenticates with a [Platform API key](/settings/api-tokens) rather than OAuth - which the admin MCP server also accepts, for developer-managed clients - then it is that key's scopes that apply, and you edit them under **Settings** > **Organization** > **APIs & webhooks**.
## The assistant is connected to the wrong organization
1. Revoke the connection under **Settings** > **Artificial Intelligence** > **AI assistants (MCP)**.
2. Disconnect it in the MCP client.
3. Connect again and choose the correct organization on the 1Club authorization page.
Every MCP connection is bound to one organization. It cannot switch organizations during a conversation.
## I see a mix of marketplace and organization tools
The client is using an old or incorrect endpoint. Remove the connection and add the correct address:
| Purpose | Endpoint |
| - | - |
| Manage one organization | `https://mcp.1club.ai/admin` |
| Search public marketplace data | `https://mcp.1club.ai/discovery` |
The admin and discovery servers never expose each other's tools.
## The tool list is missing new tools
Compare the client's list with the [tools reference](/mcp/tools). Re-scan tools or remove and re-add the connection. Many clients cache the tool catalog.
The admin server covers classes, areas and blocks, bookings, check-ins, contacts and instructors, plans, memberships, products, content, media, the website, gyms, and transactions. A missing OAuth scope causes a permission error when a tool runs. It does not normally remove the tool from the list.
## Everything worked, then stopped
Check these items:
* **The connection was revoked.** Review connected assistants in 1Club.
* **The person who authorized it lost access.** A connection carries the permission of whoever approved it. If they left the organization or no longer hold **settings.manage.organization**, the connection is revoked on its next tool call. Someone who still holds the permission has to connect the assistant again. Check this first when an assistant stops working around a staff change.
* **The organization downgraded.** API and MCP access stop when the paid feature is no longer available.
* **It sat unused for 90 days.** The client registration and its tokens are deleted, so the client has to register and authorize again.
* **The client lost its refresh credentials.** Disconnect and complete OAuth again. Replaying a refresh token that has already been rotated revokes the whole connection, so a client that restores an old copy of its credentials has to reconnect. Parallel refreshes are not affected: tool calls that all refresh at once are treated as one rotation.
* **The rate limit was reached.** MCP shares the Platform API limit of 100 requests per minute per organization. Wait a minute and retry.
The **Last used** column on the MCP page shows whether recent requests reached 1Club.
## A write fails even though reading works
Read and write scopes are separate. For example, `classes:read` does not satisfy `classes:write`.
The usual cause is that no write box was ticked on the authorization page. Every write capability is offered there as an individual checkbox that starts unchecked, so approving the page without touching it produces a read-only connection - which is the intended default, not a bug. Call `get_account` to see the granted scopes, then revoke and reconnect if the assistant genuinely needs to write.
The operation can also fail a business rule after authorization:
* A plan cannot be deleted while memberships reference it. Retire it by setting `isActive` to `false`.
* A class cannot be deleted while affected future occurrences have active bookings or waitlist entries.
* A booking cannot be checked in outside its configured window, when required payment is outstanding, or after it was already checked in. Only a `confirmed` or `pending` booking can be checked in - a cancelled or no-show booking is rejected.
Read the error returned by the tool before retrying. Do not repeatedly retry a validation or conflict error with a new idempotency key.
## The assistant invents data instead of calling a tool
Confirm the 1Club app or connector is enabled for that conversation. Ask the assistant explicitly to use the 1Club tools, or name the tool you expect, such as `list_classes` or `list_checkins`.
# Accounts
Source: https://docs.1club.ai/members/accounts
Group members into households or companies for shared billing, cross-member bookings, and consolidated reporting at /members/accounts.
Accounts group related members under a single shared entity: a **household** for a family unit, or a **company** for an employer or organization that pays for its people. Owners and managers create and manage accounts, and members with the right account role - a parent, or a company admin - can book and buy for the others on the account.
## When to use it
Accounts let you treat a family or a corporate client as one customer without merging their people into one profile. Memberships and bookings stay attached to each individual contact, but invoices, charges, and acting permissions roll up to the account.
Typical uses:
* **A family that pays together** - a padel family where the parent is the account owner, every invoice rolls up to the household, and the parent books courts for the children.
* **A corporate wellness client** - an employer whose employees each hold their own membership, with invoices sent to the company's billing email instead of each employee.
* **A parent booking for a minor** - a martial-arts school where a parent books jiu-jitsu classes for their child, who as a dependent can only book for themselves.
See [Worked examples](#worked-examples) for how to set up each of these.
## Account types
### Household
A household represents a family or shared unit (parents and children, partners, roommates).
* **Roles**: Owner, Adult, Dependent.
* **Acting on behalf of**: Owners and Adults can book, check in, and manage memberships for any household member. Dependents can only act for themselves, and only Owners or Adults can book for a Dependent.
* **Naming**: Households are named automatically after the owner's surname, so you rarely type a name.
* **Typical use**: A parent paying for their children's classes, or partners sharing a single membership invoice.
### Company
A company represents a business, employer, or institution that sponsors its people's memberships.
* **Roles**: Admin, Employee.
* **Acting on behalf of**: Admins can manage bookings and memberships for any employee on the account. Employees can only act for themselves.
* **Extra fields**: Legal name, tax number, billing email, billing address.
* **Typical use**: A corporate wellness benefit, a school paying for staff memberships, or an employer covering a fitness perk.
Staff with admin access can always act on behalf of any contact, regardless of account roles.
## Accessing accounts
Accounts live at `/members/accounts`.
1. Navigate to **Members** in the main menu.
2. Open the three-dot overflow menu next to **Add member** and click **Accounts**.
3. Use the tabs at the top to filter between **All**, **Households**, and **Companies**.
To see contacts grouped under their accounts inside the member list instead, open the **Accounts** tab of **Members**.
The list shows each account's name, type, primary contact, billing email, and member count. Click a row to open the account. When you have no accounts yet, the page shows an empty state prompting you to create your first one.
## Creating an account
1. From the Accounts page, click **Add account**.
2. Choose the type with the **Household / Company** toggle. Type is set at creation and cannot be changed afterward.
3. Fill in the details:
* **Name** - required for companies. Hidden for households, which are named automatically after the owner's surname (you can rename a household later by editing it).
* **Legal name**, **Tax number**, **Billing email**, **Billing address** - companies only.
* **Primary contact** - the owner (household) or admin (company). Pick an existing contact or quick-add a new one; this person becomes the first member.
4. For a **household**, add any **Family members** inline. Each row captures a new person's details and a role (for example, a Dependent child). They are created as contacts and attached to the household when you save.
5. Click **Save**.
The primary contact is added automatically as the first member, with the role **Owner** (household) or **Admin** (company).
## The account detail page
Open an account to see four tabs:
| Tab | What it shows |
| - | - |
| **Members** | The roster, each with their account role. Click a member to open their profile |
| **Memberships** | Every membership held by any member of the account |
| **Charges** | What account members owe, with the payments and refunds against it |
| **Invoices** | Invoices tied to any member of the account |
The header's **Add** menu offers **Add member**, **Add membership**, and **Add invoice**. **Edit** changes the account details, and **Delete** removes the account (requires manage access).
## Managing members
### Add a member
1. On the account, open the **Add** menu and choose **Add member** (or use the **Add** button on the account panel of a member's [profile](/members/member-profiles)).
2. Enter the new person's details. The fields follow your org's settings and the chosen role (a Dependent, for example, does not need their own email).
3. Pick a role. The options match the account type:
* Household: Owner, Adult, Dependent
* Company: Admin, Employee
4. Click **Save**. The person is created as a contact and attached to the account.
A contact can belong to more than one account (for example, an adult in their own household who is also on a company account through their employer).
### Change a role or remove a member
* Roles are shown per member on the **Members** tab.
* Use the member row's actions to **Remove member**. The primary contact cannot be removed while they hold that role. Removing a member detaches them from the account; their contact record, memberships, and history stay intact.
## Billing and consolidated invoicing
Accounts aggregate billing across all their members.
* **Invoices** - The account detail page's **Invoices** tab lists every invoice tied to any of its members. You can also raise an invoice against the account directly from the **Add** > **Add invoice** menu.
* **Charges** - What account members owe, with the payments and refunds against it, appears on the **Charges** tab.
* **Billing email** - For companies, invoices and billing emails are sent to the account's billing email, not the individual employee.
* **Memberships** - Memberships stay attached to the individual contact who uses them. The account's **Memberships** tab rolls them up so you can see every active membership the household or company is paying for.
If you delete an account, its members are detached but their contact records, memberships, and past invoices stay intact.
## Acting on behalf of another member
Account roles control who can book, check in, or buy for whom from the public app and front desk.
* A household **Owner** or **Adult** can book a class for a **Dependent** in the same household.
* A company **Admin** can book for any **Employee** on the account.
* **Dependents** and **Employees** can only act for themselves.
Staff with admin access can always act on behalf of any contact, regardless of account roles.
## Worked examples
### Set up a padel family with shared billing
1. Open the paying parent's [profile](/members/member-profiles) and use the account panel's **Add** to create a **Household** (the parent is locked in as **Owner**).
2. In the create dialog, add the partner as an **Adult** and each child as a **Dependent** in the inline family-members section.
3. Assign each person their memberships or court packages.
4. Every invoice now rolls up to the household for reporting and follow-up, and the parent can book courts for the children.
### Onboard a corporate gym client
1. Create a **Company** account with the employer's legal name, tax number, and billing email.
2. Set the HR or office manager as the **Primary contact** (role: **Admin**).
3. Use **Add** > **Add member** to add each covered employee with the role **Employee**.
4. Sell memberships to each employee. Invoices are sent to the company billing email.
### Let a martial arts parent book for a minor
1. On the child's household, confirm the child's role is **Dependent** and the parent's is **Owner** or **Adult**.
2. The parent can now book jiu-jitsu classes for the child from the portal or front desk; the child, as a Dependent, can only book for themselves.
## Tips & best practices
* Create households from the owner's member profile so the owner and naming are set for you.
* Use **Dependent** for children without their own login - they do not need an email, and only Owners or Adults can book for them.
* Set the company **billing email** to a monitored inbox (AP, office manager) so invoices are not missed.
* Keep memberships on the individuals who use them; rely on the account tabs for the consolidated view rather than assigning one shared membership.
* Prefer removing a member over deleting the account when someone leaves - their history is preserved either way, but removal is more precise.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| Name field missing when creating a household | Households are auto-named after the owner's surname | Leave it - the name is derived; edit the household later to rename it |
| Cannot change an account's type | Type is fixed at creation | Create a new account of the desired type and move the members |
| A member row has no **Remove** action | That member is the account's primary contact | Reassign the primary contact by editing the account, then remove them |
| Company invoices going to an employee, not the company | No billing email set on the account | Edit the company and add a **Billing email** |
| A dependent cannot book for a sibling | Dependents can only act for themselves | Have an Owner or Adult make the booking |
## Related
* [Member list](/members/member-list) - find and filter individual contacts
* [Member profile](/members/member-profiles) - a single contact's accounts, memberships, and history
* [Memberships](/sales/memberships) - plans assigned to individual contacts
* [Invoices](/sales/invoices) - billing documents that can be grouped by account
# Assessments
Source: https://docs.1club.ai/members/assessments
Score each member's training against your own criteria - create assessment templates, assess attendees from the class roster or a private lesson, send the result to the member and their parents, and review a member's history in the admin and the member portal.
An assessment is one instructor's score card for one member after one training: a score on each criterion of an assessment template, on that template's scale, plus an optional note. Instructors score their attendees after a class or private lesson, managers and the front desk review a member's history, and members and their parents read every card in the member portal.
## When to use it
Assessments give members regular, structured feedback between promotions. Saving a card can send it straight to the member and, for a child, to the parents on their household, so an academy tells every family how each session went without anyone copying scores into a message. Templates are your own criteria and scale, and the member portal keeps a running average per criterion.
Typical uses:
* **A junior programme with parents in the loop** - a tennis academy scores each child on Technique, Serve, Speed, and Focus after the group class, and both parents get the card by email within a minute.
* **Feedback after a private lesson** - a padel coach scores a member's bandeja and volleys from the booking page, and the card shows as **Private lesson** on their history.
* **Different criteria per programme** - a swim school runs one template for beginners and another for its competitive squad, each the default on its own class type.
* **Moving paper records in** - a martial-arts gym backfills past gradings from paper as cards without a session, without notifying anyone.
See [Worked examples](#worked-examples) for each of these in detail.
Assessments sit below [progression tracks](/settings/progression). A
progression level says where a member stands (a blue belt, swim level 3); an
assessment says how a session went between two promotions. Use both: the level
moves rarely, the assessments arrive after every training.
## Assessment templates
You define what gets scored under **Settings** > **Members** > **Assessments**. Instructors score from the class roster or the booking page. Staff review a member's history on their profile, and members read theirs under **My account** > **Assessments** in the member portal.
A template belongs to your organization and holds:
* **Template name** - what instructors pick and what members see on their card, for example "Tennis juniors" or "Swim school".
* **Scale** - the range every criterion is scored on: **1 to 3**, **1 to 4**, **1 to 5** or **1 to 10**.
* **Criteria** - between 1 and 20 named rows, for example Technique, Serve, Speed, Focus. Their order is the order on the score card and in the member portal.
* **Use this template** - the switch that makes the template available to instructors. A switched-off template cannot be picked for a new card; cards already scored on it stay readable and editable.
### Create a template
1. Go to **Settings** > **Members** > **Assessments**.
2. Click **Create template**.
3. Enter the **Template name** and pick the **Scale**.
4. Add your **Criteria**. Click **Add criterion** for each row, and drag the handle to reorder them.
5. Save. The template is active straight away.
Each template shows as a card listing its criteria, with a chip for the scale and a chip for how many assessments have been scored on it. The switch on the card turns it on or off; the card's actions edit or delete it.
### Set a default per class type
Open a class type under **Settings** > **Clubs** > **Class Types** and pick a **Default assessment template**. A card scored on a class of that type starts on that template, so an instructor with several templates does not have to choose one at the court side. Only active templates are offered. See [Class types](/settings/class-types).
Where an organization has a single active template, the picker is hidden altogether and every card uses it.
### Change a template that already has cards
A template with assessments behind it protects the meaning of the scores already given:
* **The scale is locked.** A 4 on a 1 to 5 scale is not a 4 on a 1 to 10 scale, so the scale selector is disabled and the editor says so. To score on a different scale, create a new template and switch the old one off.
* **Renaming a criterion** renames it everywhere, on old cards too. Rename only to fix wording, not to change what is being measured.
* **Removing a scored criterion** archives it. New cards no longer offer it; every card already scored keeps it, with its score.
* **Removing an unscored criterion** deletes it outright.
* **Deleting the template** switches it off instead of deleting it, and you see "This template already has assessments, so it was switched off instead of deleted." A template nobody was ever scored on is deleted for real.
## Assessing a member
An assessment is scored after a **session**: a class occurrence or a 1:1 booking such as a private lesson. It is offered from three places, and all three behave the same way:
* **Home** > **Today**, in the class roster drawer. See [Home](/home/overview#the-class-roster).
* **Schedule** > **Classes**, on the class page's attendance grid.
* **Schedule** > **Bookings**, on a booking's page, for a private lesson or any other 1:1 booking with a member on it.
The **Assess** action appears next to an attendee once:
* your organization has at least one active template,
* the session has started (a card cannot be written before the training happened),
* the attendee is on the session: booked on the class, a participant of the booking, or checked in to the class as a walk-in,
* and you may assess this session (see [Who can do what](#who-can-do-what)).
From **Today** open the class roster, or open the class page or the booking
page. Attendees who already have a card show **Edit assessment** instead of
**Assess**.
The card starts on the class type's default template, or on your only active
template. When more than one template is active, a **Template** picker lets
you switch.
Each criterion is one row of buttons from 1 up to the template's scale.
Every criterion needs a score; the dialog refuses to save with "Give every
criterion a score." There is no partial card.
The **Note** is optional and up to 2,000 characters. The member and their
parents read it exactly as written, so it is feedback for them, not a staff
remark. Anything the member must not see belongs in the member's
[notes](/members/member-profiles#notes) instead.
**Send it to the member and their parents** is on by default. Leave it on to
notify them (see [What the member and their parents
receive](#what-the-member-and-their-parents-receive)); switch it off to file
the card quietly. Save. You see "Assessment saved."
### Editing a card
Open the same roster action, now labelled **Edit assessment**, or use the edit action on the member's assessment list. You can change the scores and the note. You cannot change the template: the card is edited on the criteria and scale it was scored on, whatever the template has become since. Saving an edit updates the card the member already sees and does **not** send another notification.
Who may edit is about authorship, not the session. Anyone can correct a card they wrote themselves; correcting somebody else's card needs the club-wide edit permission (see [Who can do what](#who-can-do-what)).
### One card per session
A member holds at most one card per session. A second **Assess** on the same attendee opens their existing card instead, and an attempt to create a duplicate is refused with "This member already has an assessment for this session." A class booking always files under its class, so a member booked on a class gets one card for that training, never one for the booking and one for the class.
If the class or booking is later deleted, the card survives as a session-less card carrying the training's date.
### A card without a session
Sometimes a training was never in 1club: a trial before the member signed up, a camp run on paper, a lesson taught away from the gym. Club-wide staff can add such a card from the member's profile:
1. Open the member's profile.
2. Use the plus icon on the **Progress** tile and choose **Add assessment**, or open the assessment list and click **Add assessment**.
3. Pick the **Template** and the **Date** the training happened. The date cannot be in the future.
4. Score, add a note and save. **Send it to the member and their parents** works the same way.
A session-less card has no session for an instructor to "own", so it needs the club-wide create permission. By default that is a manager or the front desk, not an instructor.
## What the member and their parents receive
Saving a card with **Send it to the member and their parents** on notifies:
* **the member**, when they have a login: an in-app notification in the member portal plus an email, following their own notification settings;
* **the member by email only**, when they have no login, governed by your member notification policy;
* **the parents**, when the member is a **dependent** on a household account: the household's owner and every adult on it, each in their own language.
A household adult is never told about another adult's card, and a company account's administrator never receives an employee's. Read [Accounts](/members/accounts) for how households and dependents are set up.
The message lists each criterion with its score, the instructor's note, the session (the class name, or "Private lesson") and its date, and links to **My account** > **Assessments** in the portal. Its wording is fixed and is not editable under [Messaging templates](/settings/templates); it appears there as **Assessment** for reference only.
Two details worth knowing:
* **It rides the progression category.** A card is the same kind of news as a promotion, so a member or parent who turned progression notifications off does not receive assessments either. See [Notifications](/settings/notifications).
* **It goes out about a minute after you save.** A card deleted within that minute is not sent. A score corrected within it goes out corrected. Later edits are never re-sent.
## Reviewing a member's assessments
On the member profile, the **Progress** tile (the **Level** tile when progression tracks are also on) shows the member's overall average across every card, for example "Tennis juniors: 3.5/5", or "No assessments yet". The tile appears only when your organization has at least one template and you hold the club-wide view permission.
Click the tile to open **Assessments: **, a list of every card for the member, newest first:
* **Date** - when the session happened, or the date entered on a session-less card.
* **Session** - the class name, "Private lesson" for a 1:1 booking, or empty for a session-less card.
* **Scores** - each criterion with its score.
* **Note** - the instructor's note.
* **Instructor** - who wrote the card.
* **Template** - which template it was scored on.
From the list you can **edit** a card you may edit, **delete** a card (club-wide delete permission), and **Add assessment** for a card without a session. Deleting asks you to confirm: "Delete this assessment? The member will no longer see it."
## Smart tags from assessments
Turn on the **Assessment** category in [Smart tagging](/settings/smart-tagging#assessment-disabled-by-default) to tag members from their cards: **Not Assessed Recently** for members who train but haven't been assessed lately, **Top Performer** and **Needs Attention** from the average of their last 3 assessments. Filter the member list by these tags, or use them in an automation - for example, to remind coaches who is due an assessment.
## The member portal
Members read their assessments under **My account** > **Assessments**. The tab appears only once your organization has an active template, so a gym that does not score sessions never shows an empty page.
The page shows one summary card per person: the member themselves, and each dependent on their household. Each card shows how many assessments the person has and when the latest was, then one line per criterion with the **average** score and the **latest** score, grouped by template. **See all** opens the full history: every card with its date, session, instructor, scores and note.
A parent with two children sees three summary cards: their own and one per child. Members cannot edit or delete a card; corrections come from you.
## Who can do what
Assessments have their own module in **Settings** > **Organization** > **Permissions**. Its scopes carry two specific meanings:
* **Own** for viewing and creating means **a session this user teaches**: their own class, or a booking assigned to them.
* **Own** for editing means **a card this user wrote**, on any session. So the desk, which scores club-wide, can still fix its own typo, and an instructor can correct their own card.
| Role | Default | In practice |
| - | - | - |
| **Admin** | Full access, organization | Everything, everywhere. |
| **Manager** | View, Create, Edit, Delete at club | Scores any session, edits and deletes any card, adds session-less cards. |
| **Front Desk** | View and Create at club, Edit own | Scores any session and adds session-less cards, but edits only the cards it wrote and never deletes. |
| **Instructor** | View, Create and Edit at own | Scores and reviews the sessions they teach, corrects the cards they wrote. Cannot delete, cannot score a session they did not teach, cannot add a session-less card. |
Three consequences follow for an instructor on the defaults:
* The **Assess** action shows on their own classes and bookings only.
* The **Progress** tile and the assessment list on a member's profile need the club-wide view permission, so an instructor reviews cards on the roster and the booking page, not on the contact page.
* Deleting a card the member already received is a desk or manager decision.
Templates are organization configuration, not part of this module: creating, editing and deleting them needs the Settings permission, while any assessor can read them, since they need the criteria to fill a card. Full detail is in [Roles and permissions](/reference/roles-permissions#assessments).
## Worked examples
### Junior tennis programme, parents in the loop
An academy creates the template "Tennis juniors" with **Technique, Serve, Speed, Focus** on a **1 to 5** scale and sets it as the default on the "Junior group" class type. Each child is a dependent on a parent's household account. After Tuesday's class the coach opens **Today**, taps **Assess** beside each child, scores the four rows, adds "Great serve today, keep the toss higher" and saves. Both parents receive the card by email within a minute; the child's next portal visit shows it under **Assessments** with the running averages.
### Padel private lesson
A padel gym uses the template "Padel technique" with **Bandeja, Volley, Positioning, Footwork** on **1 to 10**. A member books an hour with a coach. When the booking has started, the coach opens the booking page and finds **Assess** in its actions; the card is filed against the booking and shows as **Private lesson** on the member's history. The member reads the note the same evening.
### Swim school with two templates
A swim school runs "Learn to swim" for beginners and "Squad" for competitive swimmers, both active. On a "Beginners" class the roster's **Assess** opens on "Learn to swim" because the class type names it as default; on a "Squad" class it opens on "Squad". A coach covering a class of the other type simply switches the **Template** picker.
### Backfilling paper cards
A martial arts gym scored gradings on paper before switching on assessments. The manager opens each student's profile, uses **Add assessment** on the **Progress** tile, picks the grading date and enters the scores, with **Send it to the member and their parents** switched off so nobody receives a card for a training they attended months ago. The history is complete and the portal averages are right from day one.
## Tips & best practices
* **Keep criteria few and observable.** Four to six criteria a coach can judge in a minute at the court side beat fifteen they will not fill in. The dialog demands a score on every criterion.
* **Pick the scale for the audience.** A 1 to 3 or 1 to 5 scale reads well for children and parents; 1 to 10 suits competitive squads. You cannot change it later, so decide before the first card.
* **Set a default per class type.** It removes the template picker from the instructor's flow, which is what makes scoring a whole roster fast.
* **Write the note for the member.** The member and their parents read it verbatim. Praise, one thing to work on, and nothing about payments, behaviour of other members, or staffing.
* **Switch off, do not delete.** When a programme ends, turn its template off. Scores stay readable and averages stay true.
* **Turn notifications off for backfills.** A card for an old training should not wake up a parent's inbox.
* **Pair with progression tracks.** Promote on the track when a member reaches the next level; assess after each session in between. Members see both in the portal.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| No **Assess** action on the roster | The session has not started yet, or your organization has no active template | Wait for the start time; create a template under **Settings** > **Members** > **Assessments** or switch one on |
| "Create an assessment template in Settings first." in the dialog | Every template is switched off | Switch one on, or create a new one |
| An instructor sees **Assess** on some classes and not others | Their permissions are Own-scoped, so only sessions they teach qualify | Expected. Grant the Instructor role View and Create at club scope to let them score any session |
| An instructor cannot open **Edit assessment** on a card | The card was written by someone else and the instructor holds Edit at own scope | Ask the author to correct it, or a manager, or grant Edit at club scope |
| "This member already has an assessment for this session" | One card per member per session; another instructor or the desk already scored them | Open the existing card and edit it |
| "This member is not on the class" or "is not on the booking" | The contact is not booked, not a participant, and not checked in | Book or check the member in first, or add a session-less card from their profile |
| The **Scale** selector is disabled | The template already has assessments | Create a new template with the wanted scale and switch the old one off |
| Deleting a template only switched it off | It has assessments behind it | Expected; the cards keep their criteria. It disappears from every picker |
| A criterion I removed still shows on old cards | It had scores, so it was archived rather than deleted | Expected; new cards no longer offer it |
| The member did not receive the card | **Send it to the member and their parents** was off, the member has turned progression notifications off, the member has no email and no login, or the card was edited (edits are never re-sent) | Check the member's notification settings and email; the card is always visible in the portal regardless |
| A parent did not receive their child's card | The child is not a dependent on the parent's household, or the parent is not the household owner or an adult on it | Fix the household under **Accounts** on the member's profile |
| The **Progress** tile is missing on a member's profile | Your organization has no template, or you lack the club-wide view permission | Create a template; ask for View at club scope |
| The **Assessments** tab is missing in the member portal | No active template in the organization | Switch a template on |
| A session-less card's date cannot be changed on an edited card | Only cards without a session carry an editable date; a session's card takes the session's date | Expected |
## Related
* [Progression tracks](/settings/progression) - the level ladder that assessments sit below.
* [Class types](/settings/class-types) - where a default assessment template is set.
* [Classes](/schedule/classes) and [Bookings](/schedule/bookings) - the sessions a card belongs to.
* [Member profiles](/members/member-profiles) - the **Progress** tile and the assessment list.
* [Accounts](/members/accounts) - households, dependents and the parents who receive a child's card.
* [Notifications](/settings/notifications) - the progression category that assessment notices follow.
* [Roles and permissions](/reference/roles-permissions#assessments) - the Assessments module in full.
# Document signatures
Source: https://docs.1club.ai/members/document-signatures
The ledger of signed waivers, terms, and policies - who accepted which document, when, how it was signed, and the evidence captured, including guardian signing for dependents.
Document signatures is the ledger of every accepted document in your organization. It records which member accepted which document, when, how it was signed, and the version of the document at the time - evidence you can produce for compliance. A signature is the proof that a member accepted a [required or optional document](/settings/waivers), captured when they sign through the member portal, the mobile app, the public signup wizard, or an acceptance link you send. Owners and admins use the ledger to check and prove who has signed.
## When to use it
The ledger answers "did this person sign, and what exactly did they agree to?" in one place. You can check it before someone trains and produce the evidence if an agreement is ever questioned.
Typical uses:
* **Check a waiver before a high-risk session** - before a member's first climbing session or sparring class, confirm their waiver shows **Signed**, not **Pending**.
* **Settle an onboarding dispute** - open the signature to see the document version, typed name, and timestamp, and download the signed agreement as a PDF.
* **Keep an accurate record for kids' programs** - when a parent enrolls a child, the waiver sits on the child's record with the parent recorded as the signer.
## How signatures are created
Signatures land in the ledger from several flows:
* **Public signup wizard** - The onboarding flow captures acceptances (including drawn signature, IP, and user agent) for the member and, where applicable, their dependents.
* **Member portal** - Members sign outstanding required documents from their own portal.
* **Mobile app** - Members can review and sign required documents from the 1Club mobile app.
* **Acceptance link** - Staff can generate an acceptance link for a specific member and document (an e-signature token), which opens the document for that member to review and accept.
* **Physical record** - A paper acceptance can be represented as a signature of type **Physical**.
A member is not asked to re-sign for cosmetic edits to a published document; a fresh acceptance is only recorded when a genuinely new signature is submitted. A repeat submission of the same document version on the same day is rejected to avoid duplicate rows.
## The signatures list
Open **Members**, then pick **Signatures** in the three-dot overflow menu next to **Add member** (route `/members/document-signatures`). The menu entry is shown to staff who can read settings. The signable documents and the rules that require them are configured separately under **Settings** > **Bookings** > **Waivers**.
The list shows the most recent acceptance per member and document, with these columns:
* **Document** - The title of the signed document.
* **Contact** - The member the signature belongs to. Click the name to jump to their [profile](/members/member-profiles).
* **Status** - **Signed** when an acceptance exists, or **Pending** when the document is required but not yet signed.
* **Signed Date** - When the latest acceptance was recorded.
* **Type** - **Electronic** (signed online) or **Physical** (a paper acceptance recorded in the system).
From the toolbar you can jump to **Documents and Waivers** to manage the documents and rules, or create a new document.
## Signature detail
Click any row to open its detail page.
* **Signature details** - The document (a link to its content item), the signed date(s), and the signature type.
* **Signed history** - Every acceptance for that member and document, newest first, each with its timestamp and the document version at signing time. Because a document can be re-signed (for example after a new version), the history preserves each event rather than overwriting it.
* **Status** - **Signed** or **Pending**.
* **Document Version** - The version of the document that was accepted, so you can prove what the member actually agreed to.
* **Typed name** - The full name the member typed, shown when the document required typed-name acceptance.
* **Member** - Contact card for the signer.
* **Signature evidence** - When present, a card with the drawn-signature image, the acceptance timestamp, the IP address, and the device / user agent.
The evidence card only appears when those fields were captured. Signatures collected by the signup wizard carry the drawn image, IP, and user agent; acceptances from older flows, typed-name-only acceptance, or a physical record entered by staff may show fewer fields, and the card is hidden when none are present.
### Downloading the signed agreement
The **Download** button on a signature detail page produces the signed onboarding agreement as a PDF - the member's details, household, the documents they accepted, and their signature, with an IP and device footer. The PDF is generated on demand and served behind your admin session; it is never stored at a public URL.
## Typed-name acceptance
When a [document rule](/settings/waivers) has **Require review and typed name** switched on, the member must scroll through the whole document and type their full name before the accept button unlocks. That typed name is stored on the signature and shown as **Typed name** on the detail page, alongside the timestamp and any captured evidence. This is the strongest self-serve acceptance you can require and is the recommended setting for liability waivers.
## Guardian and proxy signing for dependents
When a household enrolls a dependent (for example a parent registering a child for a kids' program), the guardian accepts the dependent's documents on their behalf during the signup wizard. In the ledger:
* The **subject** of the signature is the **dependent** - the row belongs to the child's contact and appears under their profile.
* The **signer** is the **guardian** - the acceptance is attributed to the account owner who actually completed the wizard.
This lets you route a minor's waiver to the `dependent` household role (see [Documents and waivers](/settings/waivers)) while keeping an accurate record that a responsible adult signed it. The guardian only signs documents they explicitly accepted in the wizard - the dependent's required documents are presented to the guardian, and each accepted one is recorded as a separate signature for the child.
## Tips and best practices
* **Use typed-name acceptance for waivers.** It forces the member to scroll and type their name, and the typed name is preserved on the signature as evidence.
* **Check status before high-risk activities.** A **Pending** row means a required document is unsigned - useful to confirm before a first climbing session, sparring class, or contact-sport match.
* **Download the agreement for your records** when onboarding disputes arise; the PDF captures the exact documents and signature.
* **Attribute minors correctly.** For dependents, expect the signature to sit on the child's record with the guardian as signer - that is the intended proxy model, not a data error.
## Troubleshooting
| Symptom | Likely cause | Fix |
| - | - | - |
| A member shows **Pending** for a document | The rule is mandatory but the member has not accepted yet | Ask the member to sign from their portal, or send an acceptance link |
| A signature has no evidence card | The acceptance came from a flow that did not capture a drawn image, IP, or user agent (for example a physical or typed-name-only acceptance) | Expected - the card only renders when evidence exists |
| No **Typed name** on a signature | The rule did not require typed-name acceptance, or the acceptance predates that requirement | Enable **Require review and typed name** on the rule under Documents & Waivers |
| A dependent's waiver shows the parent as signer | Guardian / proxy signing - the dependent is the subject, the guardian is the signer | Expected behavior for household dependents |
| A member was asked to sign again | A genuinely new signature was required; cosmetic edits alone do not re-gate | Confirm the latest version and that the earlier acceptance is still on file in the signed history |
| The document I need is missing from the ledger | It has never been signed by anyone, or it is not published / not required by any rule | Configure a rule under [Documents and waivers](/settings/waivers) and publish the document |
## Related
* [Documents and waivers](/settings/waivers) - Define signable documents and the rules that require them, including household-role targeting for dependents.
* [Accounts](/members/accounts) - Household accounts and the owner/adult/dependent roles behind guardian signing.
* [Member profiles](/members/member-profiles) - See a single member's signatures in the context of their record.
* [Settings overview](/settings/overview) - Where Documents & Waivers is configured.
# Leads and pipeline
Source: https://docs.1club.ai/members/leads
Work your sales pipeline on the Leads tab of Members - a board with a column per stage or a list, sources and assignees, Add lead, bulk moves, and the stages and sources you set under Settings > Members > Pipeline.
Your sales pipeline is the people you are working to turn into customers. It lives on the **Leads** tab of **Members**: each lead is a contact with a stage, a source, and a staff member assigned to follow up. You see them as a board with a column per stage, or as a list. Owners, managers, and front desk staff use it to follow up on enquiries and trials until people buy.
## When to use it
* **Turn trial enquiries into members** - a padel gym adds everyone who asks about a trial as a lead in **New lead**, moves them to **Trial booked** when they book, and lets them leave the board on their own when they buy a membership.
* **Let forms and the website fill the pipeline** - an open-day signup form adds every submitter to **New lead** under the **Event** source, and enquiries from your website's contact form arrive under **Website**.
* **Share out follow-ups** - a manager selects ten new leads on the list, assigns them to a coach, and an automation notifies that coach.
* **Keep quiet leads from cluttering the board** - leads with no activity for 30 days move to **Cold** overnight, and can warm up again later.
## How it works
The pipeline is part of the contact record, not a separate record. A contact is in the pipeline when they have a stage. The same stage, source, and assignee show on the board, the list, the contact's profile, and in automations.
* **A lead is a contact in the pipeline who has never been a customer.** Being in the pipeline is what makes a contact's type **Lead**. You don't set the type by hand.
* **Only someone who is not a customer right now can enter.** A plain contact, a lead, a lapsed member, or a drop-in can be added. A drop-in or lapsed member you are selling a membership to stays in their own type, and their card shows it.
* **Leads convert by themselves.** On their first paid purchase after entering the pipeline (a membership or a pass), a lead becomes a customer and leaves the board. A free or unpaid plan does not convert anyone. There is nothing to close by hand.
* **There is no won or lost.** The one **Cold** stage is for leads who went quiet. It is reversible and asks for no reason, because a "not now" in March can be a member in May. A Cold lead is still a lead.
* **A source is first-touch.** The source records where a contact first came from, and entering the pipeline again does not overwrite it.
Pipeline used to be a separate **Deals** feature under **Marketing**, with deal values, win probabilities, close dates, and custom deal fields. Deals were replaced by the pipeline on the contact. Each contact's latest open deal became their stage, source, and assignee (a lost deal became **Cold**), and deal notes became contact notes. Old **Pipeline** and **Deals** links open the Leads tab.
## The Leads tab
Go to **Members** and open the **Leads** tab. The tab is always shown, even when the pipeline is empty, because an empty board is where you add your first lead. Its badge counts everyone in the pipeline.
Use the icons in the toolbar to switch between **Board** and **List**. The page remembers your choice in this browser.
### Board
The board has one column per stage: your open stages in order, then **Cold** last. Each column header shows the stage name and the number of leads in it. Each card shows:
* the contact's name,
* their source, as a colored chip,
* their type, when they are not a lead (for example **Drop-in** or **Lapsed**),
* their next booking, when they have one,
* who they are assigned to, or **Unassigned**.
To move a lead, drag the card to another column. The three-dot menu on a card offers **View profile**, **Mark as cold**, and **Remove from pipeline**. Removing a contact from the pipeline keeps the contact and takes them off the board.
Click a card to open the lead: their contact details, their stage, source, and assignee under **Pipeline**, and their notes, which you can add to on the spot. **View profile** opens their full [profile](/members/member-profiles).
When a column holds more leads than it shows, click **Show more** at its foot. The list opens, filtered to that stage.
### List
The list shows the same leads as rows, with **Stage**, **Source**, and **Assigned to** right after the name. Columns a lead does not have yet, such as type, memberships, and levels, are left out. Click a row to open the contact's profile.
### Filters
Besides the usual member filters, the Leads tab filters by **Source** and **Assigned to**. The list also filters by stage. Filters stay in the URL, so you can bookmark "my leads from Instagram" or share it.
## Adding leads
* **Add lead** - on the Leads tab, the outlined button at the top right is **Add Lead**. It is also in the **+** menu on every Members tab. Fill in the contact details and, under **Pipeline**, the stage, source, and assignee. The stage starts at your default stage.
* **From a board column** - click **+** on a column header to add a lead that starts in that stage.
* **From a contact's profile** - the **Pipeline** block on the contact card has **Add to pipeline** for anyone who is not a customer. For someone already in it, change the stage, source, or assignee in place.
* **From a form** - turn on **Add submitters to the pipeline** on a [form](/marketing/forms) and choose a stage and a source. People who submit it become leads, unless they are already customers. A signup form can't do this, because whoever signs up becomes a customer.
* **From your website** - enquiries from the contact form on your [website](/marketing/website) arrive under the **Website** source.
* **From an automation or the API** - the **Move to pipeline stage** automation step and the platform API add and move contacts too.
## Bulk moves and assignment
Select leads on the list with the checkboxes. The bulk action bar adds **Move to stage** and **Assign to** to the usual member actions. The result tells you how many contacts were updated.
## Automations
[Automations](/marketing/automations) can work the pipeline for you:
* **Trigger** - "When a contact's pipeline stage changes", narrowed with **From stage (any of)** and **To stage (any of)**. A lead moved to **Cold** by the inactivity rule fires it too.
* **Steps** - **Move to pipeline stage** and **Assign to staff member**.
* **Entry conditions** - **In pipeline stage** and **Came from source**.
* **Recipient** - **Assigned staff member** notifies whoever the contact is assigned to.
## Pipeline settings
Stages and sources are set under **Settings** > **Members** > **Pipeline**. The page has two sections, **Stages** and **Sources**. Changing them needs organization settings manage access.
### Stages
Every organization starts with four stages: **New lead** (the default), **Contacted**, **Trial booked**, and **Cold**. Click **Add stage** to add one, or click a stage to edit it. Drag the handle to reorder stages. The board shows the open stages in this order from left to right, with **Cold** always last.
Each stage shows chips for its kind (**Open** or **Cold**), **Default** on the default stage, and how many contacts are in it. A stage has:
* **Name** and **Color** - the column label and its accent color.
* **Default stage** (open stages only) - new leads from **Add lead** and from forms start here. Exactly one open stage is the default. To move the default, turn it on for another stage.
* **Move leads here after this many days without activity** (Cold only) - leads with no activity for that many days (1 to 365) move to Cold overnight. Leave it empty to move leads to Cold only by hand.
Activity means a stage move, a booking, a form submission, or a message exchanged with the person. Campaign blasts and automated messages don't count, so they don't keep a lead warm.
You can't delete the **Cold** stage, the default stage, or a stage that still has contacts in it. Move its contacts to another stage first.
### Sources
Sources record where a contact came from. Every organization starts with **Walk-in**, **Website**, **Referral**, **Social media**, **Advertising**, **Event**, and **Other**. Click **Add source** to add one. Each source has a **Name**, a **Color** (used for the chip on the board), and an **Active** switch. Drag the handle to reorder.
Deleting a source leaves the contacts filed under it with no source. To keep their history, switch the source off instead.
## Reporting
The **Marketing Dashboard** in Analytics shows new leads, how many converted, cohort conversion within 30, 60, and 90 days, the median days to convert, and conversion by source, so you can see which sources bring people who buy. See [Marketing analytics](/analytics/marketing).
## Who can do what
| Action | Permission |
| - | - |
| See the Leads tab, the board, and the list | Members read |
| Move, assign, add, or remove leads | Members update |
| Add a lead | Members create |
| Change stages and sources | Organization settings manage |
Members permissions apply to the whole organization, not to a single **Club**. See [Roles and permissions](/reference/roles-permissions).
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| **Add to pipeline** is missing on a contact | The contact is a customer right now (a member with a live plan) | Only non-customers can enter the pipeline |
| A lead disappeared from the board | They made their first paid purchase and converted, or someone removed them | Open the contact. A converted lead is now a member or drop-in |
| You can't drag cards | Your role lacks members update access | Ask an admin to grant it |
| You can't delete a stage | It is the Cold stage, the default stage, or still has contacts | Move its contacts to another stage, or make another stage the default |
| A form doesn't add leads | **Add submitters to the pipeline** is off, or it is a signup form | Turn it on in the form's settings. Signup forms can't add leads |
| Leads never go Cold on their own | The Cold stage has no inactivity rule | Set **Move leads here after this many days without activity** on the Cold stage |
## Related
* [Member list](/members/member-list) - the other tabs of Members
* [Member profile](/members/member-profiles) - the **Pipeline** block on a contact
* [Forms](/marketing/forms) - add form submitters to the pipeline
* [Automations](/marketing/automations) - pipeline triggers and steps
* [Marketing analytics](/analytics/marketing) - lead conversion reporting
# Member authentication
Source: https://docs.1club.ai/members/member-authentication
How members sign in - the identifier your gym chooses, social sign-in, one-time phone codes, passwords, verification, invitations, and what happens when someone returns.
Member authentication is how members get into your member portal, website booking, and mobile app. All three send members to the same sign-in screen, so whatever you configure here applies everywhere they meet you. An owner or admin chooses how members are identified, managers invite members and check how each one signs in, and members manage their own passwords and sign-ins.
## When to use it
One gym-wide setting decides whether members sign in with an email address, a phone number, or either. Matching it to how your members actually reach you keeps signing in simple for them, and the sign-in methods on each profile tell you what will work for a member today.
Typical uses:
* **A phone-first gym** - your members book from their phones and rarely check email, so you switch to phone sign-in and they get in with a six-digit code by WhatsApp or text, with no password to remember.
* **Bringing members you have on file online** - you invite them from the Member list; an email invitation has them set a password, and a WhatsApp or text invitation has them enter a code sent to that number.
* **A member who says they can't sign in** - the **Sign-in methods** on their profile show whether they never accepted their invitation, have an unverified number, or have a verified number your gym doesn't accept for sign-in.
* **Someone coming back** - a member you archived cannot sign in to your gym until you unarchive them, while a member who deleted their own account starts over with a fresh one.
## The contact and the account
Two records describe the same person, and it helps to keep them apart:
* **The contact** is your gym's own copy - name, email, phone, memberships, invoices, signatures. You own it, and it exists whether or not the member has ever signed in.
* **The account** is the person's login identity, shared across every gym on 1club. It holds their password, their social sign-ins, and their verified email and phone.
Staff edit the contact. Members control the account. A contact with no account is a member you have on file who has never signed in - the Member list shows them as `unregistered`.
## Choosing how members are identified
Go to **Settings** > **Members** > **Contacts** and pick under **How members are identified**:
| Choice | Members sign in with | At sign-up they give | Email address |
| - | - | - | - |
| **Email** (default) | Email and password, or a social account | Email and password | Required |
| **Email or phone number** | Either | Email and password | Required |
| **Phone number** | Either, and the screen asks for the phone first | Name and phone number, then a code | Optional |
This is the one gym-wide switch. Everything else on this page follows from it.
Two things to know before you move off **Email**:
* **The platform has to be able to send a code.** If no WhatsApp or SMS route is set up, the options are disabled and the page tells you so. Nothing you choose here can take effect until a code can actually reach a member.
* **Normalize your phone numbers first.** Sign-in matches a member against the number on their contact. Numbers typed by staff or carried in from an import are often written inconsistently, and a member whose number does not match lands on a brand-new record instead of their own. Ask support to run the normalization script for your gym before switching phone sign-in on.
## The ways a member can sign in
A member may collect several of these on one account. Any of them gets them in.
### Email and password
The default. Members type their email address, then their password. Passwords are theirs to set - staff never see or choose one.
### Social sign-in
Members can use **Google**, **Apple**, or **Facebook** where those are switched on for the platform. Google is available on 1club generally; Apple and Facebook are enabled per deployment, so if you do not see a button for one, it is not turned on rather than broken.
What matters operationally is **how a social sign-in is matched to an account**:
* It matches on the provider's own account, not on the email address the provider reports. A member who renames their work Google mailbox, or whose Apple relay address rotates, still lands in the same account.
* The address the provider reports is recorded next to the sign-in for your reference. It is often **not** the same as the address on their account or on your contact record. A member invited at `name@company.com` who signs in with a personal Gmail has all three.
* A social sign-in confirms the email address it carries, so a member who joins this way never has to click a confirmation link.
Social buttons do not appear inside an in-app browser (a link opened from within another app). Members there use email or phone instead.
### Phone number with a one-time code
Available once you pick **Email or phone number** or **Phone number**. The member types their number and gets a six-digit code by WhatsApp, or by text message if WhatsApp cannot reach them. Entering the code signs them in - and if the number is new to 1club, it creates their account at the same time. There is no password to choose.
Only a **verified** number identifies a member. A number staff typed is not proof of anything, so it does not resolve an account until the member has proven it by entering a code.
### Phone number with a password
A member who has both a verified number and a password can use them together. This works only where you have allowed phone sign-in.
## Seeing how a member signs in
The member profile and the staff lists show **Sign-in methods** for the linked account: one mark per social sign-in, a key when they hold a password, and a phone when a verified number is a way in for them.
When it shows **None**, hover it - the reason is the useful part, and there are three:
| What it says | What it means | What to do |
| - | - | - |
| No sign-in method yet | Nothing to work with. Usually an invitation that has not been accepted. | Invite them, or resend it. |
| The number has not been verified | You have a number on file, but nobody has proven it. | Ask them to verify it from their profile, or invite them by phone. |
| The number is verified but this gym does not allow phone sign-in | Their number is proven, but your identifier setting is **Email**. | Switch the setting, or get them a password or social sign-in. |
A verified phone only counts as a sign-in method when your gym actually accepts one, so this column tells you what will work today rather than what exists in theory.
## Getting a member in for the first time
### Invitations
Invite from the Member list and choose how the invitation travels - see [choosing how an invitation reaches a member](/members/member-list#choosing-how-an-invitation-reaches-a-member).
How the member accepts follows how you sent it, and this is deliberate:
* **Sent by email** - they open the link and set a password. Opening a link from their inbox is what proves the address, so accepting this way confirms it.
* **Sent by WhatsApp or text** - they open the link and enter a code sent to that number. The code is what proves the number is theirs. It is required even when their account also holds an email address, and accepting this way does **not** confirm that address.
An invitation link is a bearer secret: whoever holds it can use it. That is why what the link proves depends on where it was sent, and why a link sent to a number can only ever open an account that number can stand for.
### Self sign-up
Members can also sign up themselves from your portal or website. What they are asked for follows your identifier setting - email and password under **Email** or **Email or phone number**, name and phone number under **Phone number**. Everything else you require of a member (date of birth, waivers, household) is collected right after they get in, not on the sign-up form.
## Verifying an email address and a phone number
Members verify both themselves, from **My account** > **Profile** in the portal:
* **Email address** - they get a confirmation link. A member who joined by social sign-in or accepted an invitation from their inbox is already confirmed.
* **Phone number** - a check mark shows a verified number. Otherwise a send icon appears next to the field; tapping it texts or WhatsApps a code, and entering it verifies the number.
Changing either one clears its verification, because a proven address or number is proof about that specific one and nothing else.
A member does **not** need a confirmed email address to use their account. Verification matters for two things: it is what lets a phone number sign them in, and it is what stops an unproven address being taken over.
## Passwords
Members set their own password when they accept an email invitation, when they sign up, or from their profile. Staff cannot set one for them.
If a member is locked out, point them at **Forgot password** on the sign-in screen. It sends a reset link to their email address. A member with no email address on file has no reset path - they sign in with a code to their phone instead, which is the point of a phone-first gym.
## How long a member stays signed in
| Where | Stays signed in for |
| - | - |
| Mobile app | 180 days |
| Member portal and website | 90 days |
| Admin dashboard (staff) | 30 days |
Members are not asked to sign in again during that window. Behind it, their session is refreshed every fifteen minutes without them noticing, so signing out on one device does not sign them out everywhere.
## When someone comes back
Three situations look similar and behave very differently:
| Situation | What happens when they return |
| - | - |
| **Never accepted their invitation** | They can still claim the account - self sign-up with that email leads them into it rather than refusing them as a duplicate. |
| **You archived them** | They cannot sign in to your gym by any method. The screen tells them the account is no longer active and to contact you. Bringing them back is your decision: unarchive the contact. Archiving is per gym, so they may still be an active member elsewhere. |
| **They deleted their own account** | Their login is gone permanently. Signing up again gives them a fresh, empty account. Your contact record and its financial history stay with you. |
## Blocked email addresses
1club staff can block an email address platform-wide. A blocked address cannot sign in to or sign up for anything, including your gym. This is a platform-level safeguard, not something you control - contact support if you believe an address is blocked in error.
## Troubleshooting
**Issue**: The **How members are identified** options are greyed out.
**Solution**: The platform cannot send a one-time code, so a phone could never be verified. Contact support to have a WhatsApp or SMS route configured for your deployment.
**Issue**: A member signed in and landed on a brand-new record instead of their own.
**Solution**: Their number on your contact record does not match the one they signed in with, usually because it was written in a local format. Ask support to normalize your contact phone numbers, then merge the duplicate record.
**Issue**: An invitation you sent by WhatsApp was refused with a message about the number not being verified.
**Solution**: That member already has an account, and the number on their contact has not been proven on it - so a link sent there could be used by whoever holds that number. Send the invitation by email instead, or have the member verify the number first.
**Issue**: A member says the invitation link asks for a code, not a password.
**Solution**: You sent it to their phone. The code proves the number is theirs. If they cannot receive it, resend the invitation by email and they will get a password form instead.
**Issue**: The Sign-in methods column shows **None** for a member you know has signed in.
**Solution**: The column describes the linked account. If the member accepted at a different gym, or the contact is not linked to their account, there is nothing to show - check the account link on the member profile.
**Issue**: A member's social sign-in shows an email address you do not recognise.
**Solution**: That is the address their provider reports, which is often personal and different from the one you hold. It is recorded for reference only and is not used to match them.
## Related
* [Member list](/members/member-list) - inviting members and choosing the channel
* [Member profile](/members/member-profiles) - the per-contact view, including account links
* [Member portal](/settings/member-portal) - what members can see and do once they are in
* [Contact settings](/settings/overview) - the identifier setting and required contact fields
* [WhatsApp](/settings/integrations/whatsapp) - connecting your own number for codes and invitations
# Access cards
Source: https://docs.1club.ai/members/member-cards
The Cards page under Members - the access cards and PINs members present at a reader or kiosk to check in, how to issue a card, take it off a member, or keep it as club stock.
**Cards** is the org-wide registry of the access cards and PINs your members use to identify themselves at the door or desk. Each card has a type, a code, and the member it is issued to, or no one when it is club stock. The front desk issues cards, managers keep the registry tidy, and members present their card at a reader or kiosk to check in.
## When to use it
A card identifies a member at the door or desk, so nobody has to find their name in a list. When a member presents one at a reader or kiosk, the system matches it to a contact and runs the same check-in flow you would run by picking the person by hand. The registry shows who holds what, when each card was last used, and lets you shut off a lost card at once.
Typical uses:
* **An unattended front door** - a 24/7 strength gym issues every member an access card and removes it when they cancel, so it stops working at the door immediately.
* **No hardware at all** - a reformer Pilates studio uses the QR member code each member already has in the member portal and app.
* **A keypad entrance** - a bouldering gym gives each member a PIN, and replaces it when someone forgets or shares theirs.
* **Pre-encoded stock** - a padel gym loads a box of new cards as club stock, then hands one over at the desk by scanning it on the member.
See [Worked examples](#worked-examples) for how to set up each of these.
## Card types
There are three types, matching the identification methods configured under [Access control](/settings/access-control):
* **Access card** - a physical card or fob with a code printed or encoded on it (for example an NFC UID).
* **PIN code** - a number the member types at a kiosk or keypad. Leading zeros count.
* **QR code** - a code shown at a reader or kiosk.
A member's QR code is their member code, which every member with a portal account already has. It needs no issuing, so you only issue access cards and PINs. Card codes are encrypted at rest and only decrypted for display in the admin.
Which types you can issue depends on the identification methods enabled for the gym, under **Enabled Identification Methods** in access control settings. Only enabled types are offered when you issue a card.
## How cards interact with check-in
How strict the door is, and what happens when a card is presented, depends on your check-in mode. See [Check-in models](/reference/check-in-models) for the full matrix. Quick summary:
* **Strict** mode requires the member to have an active booking or membership.
* **Flexible** mode allows walk-ins and anonymous check-ins.
* A scanned card is matched to the member who holds it, and the rest of the check-in flow proceeds as if you had selected that member manually.
Each successful scan updates the card's **Last used** date, so a card that never shows a scan is a useful signal that it is faulty or was never handed out.
## Opening the page
Go to **Members**, open the three-dot overflow menu next to **Add member**, and pick **Cards** (URL `/members/access-cards`). The entry is shown to staff who can read operations. The page lists every card on file in the org, newest first.
The **Settings** button in the top right jumps to [Access control settings](/settings/access-control), where the check-in mode and identification methods are configured.
## Grid columns
* **Assigned To** - the member the card is issued to, with avatar. A `+N more` chip appears when a card is shared by more than one member. A card with no holder shows **Unassigned**: it is club stock.
* **Type** - **Access card**, **PIN code**, or **QR code**.
* **Card number** - the card's code.
* **Issued Date** - when the card was recorded.
* **Issued by** - the staff member who issued it.
* **Last used** - the last successful scan.
## Issuing a card
Click **Issue card** in the top right, on a member's profile (the **Cards** block, or **Add** > **Card**), or at the point of sale. One dialog handles every case:
1. Pick the **Card type**, if the gym issues more than one.
2. For an access card, hold it against the reader or scan its barcode. The code appears by itself. If there is no reader, use **Type the code instead**. For a PIN, type the number.
3. Pick the **Member**, or leave it empty to record the card as club stock with no holder yet.
4. Turn on **Make this their main card** if this should be the member's main card.
5. Save.
A card already on file as club stock is issued the same way: scan it and pick the member. If the code is already issued to someone else, the dialog names them, so you can take the card back first or use a different one. On a stock card's row in the grid, **Issue card** opens the dialog with the code already filled in.
### Self-enrollment at a kiosk
If credential enrollment is enabled for a gym in access control settings, a member can enroll a physical card themselves at a kiosk: they identify with their QR code and then tap the new card on the reader. Cards enrolled this way appear in the grid alongside the ones staff issue.
### Automatic PINs
When a member needs an access grant (for example, when their membership or booking becomes active) and has no main card yet, the system gives them a random PIN, but only if PIN is an enabled identification method. This is why a member can appear in the grid with a PIN nobody issued. Nothing is created for an org that never enabled access control.
## Taking a card off a member
On an issued card's row, or in the **Cards** block of the member's profile, use **Remove from member**. The card is deleted and stops working right away, whether it came back over the counter or was lost. Scanning the same card again later issues it fresh to whoever gets it next.
A stock card with no holder can be deleted from its row with **Delete**. Deleting is permanent and removes everything recorded against the card.
## Worked examples
### Issuing physical cards for a gym
A 24/7 strength-and-conditioning gym wants a fob for every member so the front door works unattended.
1. In [Access control](/settings/access-control), enable access cards as an identification method and set the check-in mode to **Strict** so only members with an active membership get in.
2. For each new member, open their profile, click **Issue card**, and tap the fob on the reader.
3. Hand over the fob. When a member cancels, use **Remove from member** so it stops working at the door immediately.
### QR check-in for a Pilates or yoga studio
A reformer Pilates studio runs class-based check-in and does not want to distribute hardware.
1. Enable QR code as the identification method.
2. There is nothing to issue: each member shows their QR member code from the member portal or app.
3. At the desk or kiosk, scan the member's QR at the start of class. In Strict mode the scan is accepted only if they have a spot in that class.
### PIN keypad for a climbing gym
A bouldering gym has a keypad at the entrance and wants no phones or cards involved.
1. Enable PIN code as the identification method.
2. New members get an automatic PIN when they gain access, or you issue a specific one with **Issue card** > **PIN code**.
3. If a member forgets or shares their PIN, remove it and issue a new one.
### Loading pre-encoded stock
A padel gym buys a batch of pre-encoded cards.
1. Click **Issue card**, scan each card, and leave **Member** empty. Each one is recorded as club stock and shows **Unassigned** in the grid.
2. When a member signs up, open their profile, click **Issue card**, and scan a card from the box. It is issued to them in one step.
## Tips & best practices
* Enable only the identification methods you actually use. The card types offered and the automatic PINs both follow the gym's enabled methods.
* Keep one main card per member, so it is clear which card a tap resolves to before a busy check-in period.
* For lost cards, remove the old card from the member first, then issue the replacement, so there is never a live duplicate.
* Use the number printed on the card as its code, so the **Card number** column is easy to match at the desk.
## Troubleshooting
| Symptom | Likely cause | Fix |
| - | - | - |
| A card type is missing when issuing a card | That identification method is not enabled for the gym | Enable it under **Enabled Identification Methods** in [access control settings](/settings/access-control) |
| Issuing fails because the card is already issued to someone | The code is held by another member | Remove it from that member first, or use a different card |
| Issuing fails because the code is already used by a product, pass, or plan | A catalog item uses the same barcode | Use a different card, or change the item's barcode |
| A member is rejected at the reader | The card was removed, or check-in mode is Strict and they have no matching booking or membership | Check the grid and their profile; issue a card again, and confirm their booking or membership - see [Check-in models](/reference/check-in-models) |
| A member has a PIN nobody issued | It was created automatically when they gained an access grant | Expected. Remove it and issue a different PIN if you need to |
| Kiosk tap does not enroll a card | Credential enrollment is off for that gym | Enable enrollment in access control settings, then have the member tap again after scanning their QR |
## Related
* [Access control](/settings/access-control) - check-in mode, identification methods, organization-level card settings
* [Check-in models](/reference/check-in-models) - how cards are honored at the reader
* [Member profile](/members/member-profiles) - the per-member **Cards** block
* [Attendance](/schedule/attendance) - the check-in log
# Member list
Source: https://docs.1club.ai/members/member-list
The contacts grid under Members for finding, filtering, and bulk-acting on members, drop-ins, leads, and lapsed contacts, and for adding new members and leads.
The Member list is the contacts grid: every person tracked in your org - members, drop-ins, leads, and lapsed contacts - in one searchable list, sorted by created date by default. Front desk staff use it to find a person and open their profile, and managers use it to add members and work through groups of contacts at once.
## When to use it
Open the Member list when you need to find someone, pull out a group of contacts, or act on many of them together. Tabs split the list by contact type, the filter drawer narrows by tag, plan, status, or progression level, and bulk actions invite, merge, or archive contacts. It is also where you add new members, leads, children, memberships, and accounts, and where your sales pipeline lives on the **Leads** tab.
Typical uses:
* **Nudge members who never set up their portal** - filter by plan and **Unregistered** status, then invite everyone in one batch.
* **Sign up a family** - add a parent at a martial-arts gym, then add each child as a dependent on the household.
* **Clear out cold leads** - move the leads from a padel open day that never converted to **Cold** in one go.
* **Chase payments before a bill run** - check the **Unpaid** tab for anyone carrying a pending or failed payment.
See [Worked examples](#worked-examples) for the steps.
## Opening the list
Go to **Members** in the main menu. The Member list is the default page, at the canonical path `/members/list`.
A member belongs to your whole organization, not to one of its **Clubs**. Staff at every location see the same list, and where a member trains lives on what they do: each membership, booking, check-in, and order records its own **Club**.
## Tabs
Tabs filter the grid. **All** and **Leads** are always present. The others only show when their bucket has at least one contact, so a new org may see fewer tabs than a busy one. In order:
* **All** - every non-archived contact.
* **Members** - contacts with `type = member`. Default tab when present.
* **Drop-ins** - one-off visitors (`type = drop_in`).
* **Leads** - your sales pipeline: everyone with a pipeline stage, as a board or a list. See [Leads and pipeline](/members/leads).
* **Unpaid** - contacts carrying the system **Unpaid** smart tag (pending or failed payment). Only shown when that tag exists in your org.
* **Accounts** - contacts grouped under household or company accounts, shown grouped by account. This tab is read-only (no row selection).
* **Lapsed** - members whose membership ended without renewal (`type = lapsed`).
**Members**, **Drop-ins**, and **Lapsed** describe membership history, so they only appear for staff who can read memberships. Staff without that access land on **All**.
Each tab shows a count badge from the server. The page remembers the last tab you used per browser. The former **Households** tab was renamed to **Accounts**, so old `?tab=households` links redirect there automatically.
## Columns
The grid shows, by default:
* **Name** with avatar, plus email below.
* **Phone**, with a WhatsApp badge when the number is WhatsApp-verified.
* **Age** computed from date of birth.
* **User status** - whether the contact has a member-portal account. If they have no account but do have an email address or a phone number, this cell shows an **Invite** button. Otherwise it shows a status chip (`unregistered`, `invited`, `incomplete`, `active`, `inactive`, or `suspended`).
* **Type** chip (member, lead, drop-in, etc.).
* **Memberships** - active membership plan names (shows "No memberships" when none).
* **Accounts** - household and company accounts the contact belongs to, each a link, with the account type underneath.
* **Progression** - skill and level chips per enabled track. Only shown when progression tracks are configured.
* **Rating** - when ratings are enabled, the contact's strongest sport rating, with the rest in a tooltip.
* **Engagement** - latest engagement score chip (see [Engagement analytics](/analytics/engagement)). Only shown when at least one contact in the current view has a score.
* **Tags** - manual and smart tags as chips.
On the **Leads** tab, **Stage**, **Source**, and **Assigned to** follow the name, and the columns a lead does not have yet (type, memberships, progression, accounts, and user status) are left out.
Hidden by default: **Invitation status** and **Created at**. Any custom contact fields your org has defined are added as extra columns, also hidden by default. Toggle visibility from the column menu. On the Accounts tab, the per-row Accounts column is hidden because the grouping header already shows it.
## Search and filter
Use the search bar to match by name, email, or phone. The filter drawer (top right) holds:
* **Tags** - any combination of manual and smart tags. Only tags currently applied to at least one non-archived contact appear.
* **Status** - the contact's user-account status.
* **Plan** - one or more active plans. Only plans that currently have at least one active membership are listed.
* **Level** - progression levels, grouped by track when more than one track is enabled.
* **Stage**, **Source**, and **Assigned to** - on the **Leads** tab only. The board view filters by source and assignee, since its columns already are the stages.
The drawer loads its in-use option lists the first time you open it, so early on it may briefly show every option before narrowing to those actually in use. Filter selections live in the URL, so a filtered view can be shared as a link or bookmarked.
## Tags
Members carry two kinds of tags: **manual tags** that you add per contact, and **smart tags** that the system applies automatically based on rules. Both appear as chips and both work as filters here, in automation entry conditions, and in promotion segments.
Each tag can have an optional color, set as a hex code in **Settings** > **Members** > **Smart Tags**. Text color is auto-picked for contrast.
Smart tags ship with a default rule set covering **Attendance** (All-Star, Regular, Casual, Low Activity, Ghost), **Financial** (Unpaid, Failed, Overdue, Lapsed), **Lifecycle**, **Loyalty** (Legend, Champion, Pro, Rising Star, Beginner), **Membership**, and **New contact**. See [Smart tagging](/settings/smart-tagging) for the full list and rule editor.
## Adding a member
Click **Add member** at the top right to open the create dialog. It contains the contact form plus an optional membership section:
1. Fill in the contact details. **First name** is always required. **Last name**, **email**, **phone**, **date of birth**, and **gender** are required only if your org marks them required under **Settings** > **Members** > **Contacts**. You can also set a profile image, tags, address, and any custom fields. There is no **Club** to pick: a member belongs to the organization, and each membership or booking records its own.
2. To sell a plan at the same time, click **Add membership** and pick a plan in the membership section. Leave it off to create the contact without a membership.
3. Save. The contact is created and you land on their [profile](/members/member-profiles).
The contact's `type` is derived from what they actually hold - attaching a membership makes them a member, and a bare contact with no membership or booking stays a plain contact. A contact becomes a lead only by entering the pipeline. You don't set the type by hand.
On the **Leads** tab the outlined button becomes **Add Lead**, which adds the contact straight into the pipeline with a stage, source, and assignee.
### The create (+) menu
The primary **+** button next to **Add member** opens a menu of create actions:
* **Add Member** - the dialog above.
* **Add Lead** - the same contact form plus a **Pipeline** section (stage, source, assignee). See [Leads and pipeline](/members/leads#adding-leads).
* **Add Child** - the same contact form with an account picker, preset to the Dependent role. Use it to add a child to a household, for example a parent's second kid joining the same kids climbing program.
* **Add Membership** - open the membership dialog standalone and pick the contact inside it. Only shown to staff who can create memberships.
* **Add Account** - create a household or company account, then jump straight to it.
### Header quick links
The three-dot overflow menu (next to the **+** button) links to related pages. Each entry only shows if you can open the page behind it:
* **Memberships** - opens [**Sales** > **Memberships**](/sales/memberships).
* **Cards** - the [access cards](/members/member-cards) registry.
* **Matches** - opens [**Events** > **Matches**](/events/matches). Only when ratings are enabled.
* **Accounts** - the [accounts](/members/accounts) list.
* **Contact analysis** - a check of your roster for missing details and possible duplicates.
* **Signatures** - the [document signatures](/members/document-signatures) ledger.
* **Archive** - opens **Settings** > **Data Tools** > **Archive**.
## Per-row actions
Click a row to open the member's [profile](/members/member-profiles). The row action menu also offers:
* **View** - open the profile (needs `members.read.organization`).
* **Edit** - open the contact edit dialog (needs `members.update.organization`).
* **Archive** - soft-delete the contact (needs `members.update.organization`).
* **Merge** - open the merge dialog with this contact on one side, then pick the duplicate (needs `members.delete.organization`).
Members permissions are organization-wide: there is no per-**Club** access to the member list.
## Bulk actions
Select rows with the checkboxes. The bulk action bar appears with the count of selected contacts and its actions:
* **Move to stage** and **Assign to** - on the **Leads** tab only. See [Leads and pipeline](/members/leads#bulk-moves-and-assignment).
* **Invite** - opens the invite dialog, where you choose how the invitation travels, then sends it to every selected contact who is eligible (reachable on that channel, and either unregistered or holding an expired invite). The info icon shows how many of your selection are eligible. Invitations go out in a single request that the server governs against your gym's daily invite budget; it reports back how many were invited, how many each channel carried, and how many failed, were throttled, skipped, could not be reached on your chosen channel, or were ineligible, so a problem with one contact never hides another. Invite is offered for any selection. When nobody selected can be invited, the dialog says why. It needs `members.update.organization`.
* **Merge** - appears when exactly two contacts are selected. Opens the merge dialog for the pair. It needs `members.delete.organization`.
* **Archive** - archives each selected contact. Contact records are never hard-deleted, so financial history and audit trails stay intact. Archiving runs one request per contact and reports partial success if some fail. This action needs `members.update.organization`.
Archived contacts disappear from the list. To get back to them, use **Settings** > **Data Tools** > **Archive**.
## Choosing how an invitation reaches a member
Every invitation asks how it should travel, whether you are inviting one member
or fifty. There is no gym-wide default, because the reason to send one member a
WhatsApp invitation is usually that member: no email address on file, or a number
they actually answer.
| Choice | What happens |
| - | - |
| **Whatever reaches them** | Email if the member has an address, otherwise WhatsApp, otherwise a text message. |
| **Email** | Only email. A member with no address is reported instead of being invited another way. |
| **WhatsApp** | Only WhatsApp, from your own connected number if you have one, otherwise from ours. |
| **Text message** | Only SMS. |
A named channel is a requirement, not a preference. If it cannot carry the
invitation, that member is reported rather than quietly emailed instead, so you
can see who was missed. The result tells you which channels actually carried the
batch, which is how you spot a WhatsApp choice that is silently going out as
email because WhatsApp is not connected yet.
Two things narrow who a phone channel can reach:
* **WhatsApp needs the member's consent.** Sending from our number requires them
to have opted in, and a member who has replied `STOP` is never messaged on
either phone channel.
* **A number has to identify the member.** If the contact already has a portal
account and the number on their contact has not been verified on it, the
invitation is refused. Otherwise a link sent to a stale, shared, or mistyped
number could be used by whoever holds it to get into that member's account.
Send it by email instead, or have the member verify the number first.
Staff invitations always go by email. A staff role cannot be handed to whoever
holds a phone number.
For the whole picture - what members can sign in with, social sign-in, verification,
and what happens when someone returns - see [Member authentication](/members/member-authentication).
### What the member does next
How they accept follows how you sent it:
* **Invited by email** - they open the link and set a password.
* **Invited by WhatsApp or text** - they open the link and enter a code sent to
that number. This is how the invitation proves the number belongs to them, and
it is required even when their account also has an email address. Their email
address is not marked as confirmed by accepting this way.
## Worked examples
### Find everyone on a plan who hasn't set up their portal
You just launched online booking and want to nudge members who never activated their account.
1. Open the filter drawer, set **Plan** to your membership plan, and set **Status** to **Unregistered**.
2. Select all rows and click **Invite**, pick how the invitation should travel, and confirm. Everyone the chosen channel can reach gets a portal invitation in one batch.
### Onboard a family at a martial arts gym
A parent signs up two kids for the kids jiu-jitsu program.
1. Click **Add member** and create the parent as the account owner (or **Add account** to create the household first).
2. Use the **+** menu > **Add dependent** twice, picking the household each time, to add both children with the Dependent role.
3. Attach the kids program plan to each child in the membership section or from their profile.
### Clean up stale leads after a padel open day
Your open day generated a batch of leads that never converted.
1. Open the **Leads** tab and switch to **List**.
2. Filter by **Source** (for example **Event**), or by created date if you show the Created at column.
3. Select the ones that went quiet and use **Move to stage** > **Cold**. They stay in the pipeline and can warm up later. To drop someone from the pipeline entirely, use **Remove from pipeline** on their card instead.
### Spot unpaid members before a billing run
1. Open the **Unpaid** tab (visible when the system Unpaid tag exists).
2. Review who is carrying a pending or failed payment, then follow up before the next [bill run](/sales/bill-runs).
## Tips & best practices
* **Share filtered views as links.** Because tab, search, and filter state live in the URL, you can bookmark "active members on the coaching plan" or paste it to a colleague.
* **Use the inline Invite button for one-offs.** You don't need to select a row - the User status column shows an **Invite** button for any unregistered contact you can reach. It opens the same dialog, so a single invitation gets the same channel choice as a batch.
* **Turn on Created at when auditing signups.** It's hidden by default; toggle it from the column menu when you need to sort or eyeball recent joins.
* **Let the type calculate itself.** Attach a membership rather than hand-setting `type = member`; the system keeps the type in step with what the contact actually holds.
* **Archive, don't fear data loss.** Archiving is a soft delete that preserves charges and history, so it's safe to tidy the list aggressively.
## Troubleshooting
**Issue**: A tab I expect (Members, Drop-ins, Lapsed, Unpaid) isn't showing.
**Solution**: Tabs other than All and Leads only appear when their bucket has at least one contact, and Unpaid also requires the system Unpaid smart tag to exist. Members, Drop-ins, and Lapsed also need permission to read memberships. Add a matching contact or enable the tag, then reload.
**Issue**: The invite dialog says nobody in the selection can be invited.
**Solution**: Only contacts who are unregistered or holding an expired invite, and reachable on the channel you pick, can be invited. Contacts who are already active, still have a pending invite, or have no email address and no phone number are skipped. Picking **WhatsApp** or **Text message** also narrows eligibility to contacts who have a phone number.
**Issue**: Some invitations weren't sent, or the result says "throttled".
**Solution**: Invites are capped by your gym's daily invite budget. The result breaks the batch into invited, failed, throttled, skipped, could-not-be-reached, and ineligible counts. Throttled invitations exceeded the budget - retry later or contact support to raise the limit. An invitation that could not be delivered does not count against the budget.
**Issue**: A plan or tag is missing from the filter drawer.
**Solution**: The drawer only lists options currently in use - plans with at least one active membership and tags applied to at least one non-archived contact. If nothing matches yet, the option won't appear.
**Issue**: I archived a contact by mistake.
**Solution**: Archiving is reversible. Go to **Settings** > **Data Tools** > **Archive** to find and restore the contact; their history is intact.
## Related
* [Member profile](/members/member-profiles) - the page a row click opens
* [Memberships](/sales/memberships) - the membership grid
* [Accounts](/members/accounts) - household and company grouping
* [Leads and pipeline](/members/leads) - the Leads tab
* [Smart tagging](/settings/smart-tagging) - how the automatic tags are computed
* [Engagement analytics](/analytics/engagement) - where the engagement score comes from
* [**Sales** > **Plans**](/sales/plans) - the plans that back memberships
# Member profile
Source: https://docs.1club.ai/members/member-profiles
The per-contact page that pulls together a member's identity, balances, pipeline stage, memberships, bookings, check-ins, charges, documents, notes, cards, and account links.
The member profile is the page for one contact: it pulls together their identity, balances, memberships, history, and integration links into one view. A **contact** is your organization's own copy of a person's record - name, email, phone, plus their memberships, invoices, and signatures. Front desk staff open it when a member is at the desk, and managers use it to fix records, set up access, and follow up on money owed.
## When to use it
The profile answers the questions staff ask most at the desk - does this person owe anything, have they checked in today, how active are they - without opening a tab. From the same page you add a booking, membership, check-in, or voucher redemption, keep internal notes, and manage the member's portal account, pipeline stage, cards, and household or company links.
Typical uses:
* **A padel member who owes money checks in** - the **Due now** tile shows last week's unpaid court fee, so you record the visit and take payment in one place.
* **Getting a new jiu-jitsu member onto the portal** - confirm their email and send the invitation from the **User Account** block.
* **A door PIN for a climber** - add a PIN from the **Cards** block when access control is on.
* **Belt progression for a martial-arts student** - assign their current belt from the **Level** tile so it stays visible to the front desk.
See [Worked examples](#worked-examples) for the steps.
## Page layout
You land on the member profile after clicking a row in the [Member list](/members/member-list). The URL is `/members/list/{contactId}`.
The page has two columns:
* **Left sidebar** - the contact card: avatar, name, email, phone, accounts, custom fields, the **Pipeline** block, the user-account section, cards (when access control is on), integration links (Stripe, WebLock), payment methods, and a notes panel.
* **Main column** - an info strip across the top, then the underlined tabs underneath.
## Info strip
Tiles at the top of the main column summarize the contact's current state. Three tiles always show, and a fourth appears when progression tracking or assessments are enabled:
* **Due now** - what this member owes that has already fallen due. It turns red when anything is due. Underneath, **Upcoming** shows unpaid charges that are not due yet, such as a pay-on-arrival court booked for tomorrow or a membership that starts next month. Upcoming charges are never added to **Due now** - see [What counts as due now](#what-counts-as-due-now). A **Wallet** sub-line shows any wallet credit. What is due and the credit are shown separately and never netted against each other: a member who owes 100 and holds 100 in credit still shows 100 due now. Use the redeem icon in this tile to redeem a voucher against the account.
* **Total paid** - lifetime amount paid, with an "of X billed" sub-line so you can see paid versus billed at a glance.
* **Attendance** - a sparkline of recent check-ins. The dot lights up when the member has already checked in today. Use the overflow icon to change the window: **Last 4 weeks**, **Last 12 weeks**, **Last 6 months**, **Last 12 months**, or **All time**. Your choice is remembered for the next profile you open.
* **Level** / **Progress** - current progression level, shown when progression tracks are enabled, and the member's overall assessment average (for example "Tennis juniors: 3.5/5") once your organization has an assessment template and you may view assessments club-wide. The tile is titled **Progress** when both are on. When the next level sets a number of sessions, the tile also shows the member's count toward it (for example "13 / 20 sessions to Blue belt") or **Eligible for promotion** - see [Sessions to the next level](/settings/progression#sessions-to-the-next-level). The plus icon assigns a level or adds an assessment; clicking the average opens the member's full [assessment history](/members/assessments#reviewing-a-members-assessments).
### What counts as due now
**Due now** adds up the member's unpaid [charges](/sales/charges) that have fallen due. A charge that is not due yet counts as **Upcoming** instead:
* **Bookings** - a booking's charge (a court, an instructor session, a class, an event, or one session of a recurring booking) is upcoming until the visit starts. It falls due when the visit starts or the member checks in, whichever comes first. If the booking is cancelled or marked as a no-show, any cancellation charge is due at once.
* **Everything else** - other charges fall due on their date. A membership's charge, its renewals, and its signup fee are dated at the start of the period they pay for, so a membership that starts next month stays upcoming until then. Products and walk-in visits are due when sold.
The same **Due now** figure appears wherever staff see what a member owes: the red marker next to their name in the bookings, check-ins, and attendance lists, the card that opens when you hover over it (which also shows **Upcoming**), the point of sale, and booking details. The marker only shows while something is due now.
### Notices above the info strip
Two kinds of notice can appear between the header and the info strip:
* **Alerts** (red, or orange for warnings only) - what to resolve with the member before serving them: a required document still unsigned, a missing required field, a wallet below its required deposit, gear out or overdue. What the member owes is not repeated here, because the **Due now** tile already shows it.
* **Missing details** (blue) - a contact detail your gym requires that the record lacks, with a **Fix** button that opens the contact form. The notice names the phone number or email only when that field is required under **Settings** > **Members** > **Contacts**, on the staff form or at signup. Out of the box only email is required, so a contact without a phone number is not flagged unless you make the phone field required.
## Header actions
For an active contact the header has an **Add** menu, an **Edit** action, and a three-dot overflow menu holding **Suspend** (or **Reinstate**), **Stop SMS** (or **Allow SMS again**) when the contact has a phone number, and **Archive**. The **Add** menu creates, depending on your permissions and the features you use:
* **Booking** - opens the schedule pre-filtered to this contact.
* **Membership** - opens the membership dialog with the contact pre-selected.
* **Check-in** - records a visit (against an existing booking or membership, or a new booking or membership in one step).
* **Wallet** (or **Deposit** when a deposit is required) - tops up the member's wallet.
* **Voucher** - redeems a voucher against this contact's account.
* **Payment** - takes a payment toward what the member owes, upcoming charges included, so a member can pay ahead. Disabled while the member owes nothing.
* **Card** - issues an access card, when access control is on. See [Access cards](/members/member-cards).
* **Payment card** - saves a payment card to charge.
* **Assessment** - adds an assessment card, when assessments are set up.
* **Rental** - lends gear, when Rentals is on.
* **Message** - opens a new message pre-addressed to the contact in the [Inbox](/inbox/overview).
**Edit** opens the contact form. **Suspend** pauses the member without changing anything, and **Archive** removes them from active use. See [Suspend and archive](/members/suspend-and-archive) for which to reach for.
If the contact is archived, the header collapses to a single **Unarchive** action (visible to staff with the `members.update.organization` permission). An **archived member** is retained for financial and legal history but is hidden from listings, has its memberships and future bookings cancelled, and cannot log in until an admin unarchives it.
A **suspended member** keeps everything - record, plan, bookings, staff role - but cannot book, check in, or sign in until someone reinstates them. The profile shows a banner with a **Reinstate** button while they are suspended.
Both are scoped to your gym: the same person can stay an active member elsewhere.
## Tabs
Each tab shows a count badge and loads its own data on demand, so opening the profile stays fast even for contacts with long histories.
| Tab | What it shows |
| - | - |
| **Activity** | Engagement gauge and activity timeline |
| **Bookings** | All bookings, including cancelled. Click through to the booking detail |
| **Memberships** | Every membership ever assigned. Edit, pause, resume, renew, or cancel from here. Shown to staff who can read memberships |
| **Check-ins** | Visit history |
| **Charges** | What this contact owes, with the payments and invoices against it |
| **Documents** | Signed documents (waivers, contracts), form responses, and documents the member still owes |
| **Notifications** | Which notifications the member receives, with any overrides of your defaults |
| **Matches** | Match history, shown only when ratings are enabled |
| **Rentals** | Gear the member has out, shown only when Rentals is on. The badge turns red while anything is overdue |
### Activity tab
The **Activity** tab stacks two elements:
* **Engagement gauge** - the member's engagement score and level. See [Engagement analytics](/analytics/engagement) for how the score is computed.
* **Activity timeline** - a chronological feed of the member's bookings, check-ins, messages, and membership activity.
## Editing the contact
Click **Edit** in the header. The form covers name, contact details, date of birth, profile image, address, and any custom fields configured under **Settings** > **Members** > **Contacts**. A contact has no **Club** of their own: they belong to your whole organization, and each membership or booking records its own.
The contact's **type** (member, lead, drop-in, lapsed, contact, staff) is not edited here. It follows what the contact holds and does, and every change is logged. Click the type chip in the page header to open its type-change history.
## Pipeline
The **Pipeline** block on the contact card shows the contact's stage, source, and assigned staff member, each changed in place. For a contact who is not a customer and not yet in the pipeline, it offers **Add to pipeline**. See [Leads and pipeline](/members/leads).
## Notes
The notes panel at the bottom of the sidebar is a free-form running log. Type a note and press the add button (or Ctrl+Enter) to save it. Each note is timestamped; editing a note updates its timestamp. Use the pencil and trash icons on a note to edit or delete it. Notes are internal - members never see them.
## User account
The **User Account** block in the sidebar shows whether the contact has a member-portal login. Possible states:
* **Unregistered** - no portal account exists. When the contact has an email, an **Invite** button appears to send a portal invitation.
* The account exists (statuses such as **Active**, **Inactive**, **Suspended**, or an invitation still pending). The block shows the status chip, and the invitation expiry date while an invitation is outstanding.
The overflow menu on this block offers, depending on state:
* **Reset password** - available for an active account when you are an admin and the account is not your own.
* **Send invitation** / **Resend invitation** - re-sends a portal invitation while one is outstanding.
* **Delete** - deletes an outstanding invitation, or removes the portal login link, for anyone but yourself.
## Cards and PIN
When [access control](/settings/access-control) is enabled, the **Cards** block lists every access card, PIN, or QR code this contact holds, with a star on their main card. From here you can:
* **Issue card** - scan a card on the reader (or type its code, or enter a PIN) to give it to this member. A card already on file as club stock is issued the same way.
* **Remove from member** - takes a card off the member. It stops working right away, and scanning it again later issues it fresh.
A **PIN** is a number the member types at a kiosk or door. See [Access cards](/members/member-cards) for card types and how issuing works.
## Accounts panel
The Accounts block in the sidebar shows every household and company the contact belongs to, with the contact's role on each. Click an account name to open the [account detail page](/members/accounts).
* When the contact belongs to **no account**, the block shows an **Add** button that creates a new account (household or company) with this contact locked in as the owner.
* When the contact **is on an account**, each account row shows the other members with their roles. The row's **Add** button attaches a new person to that account, and the trash icon detaches a member without deleting their contact record.
## Integrations
When the contact has been pushed to external systems, the sidebar shows:
* **Stripe customer** - customer ID, email, and creation date. **Sync** re-pulls Stripe's record.
* **WebLock** - the WebLock user ID, or a prompt to sync. **Sync** re-pushes every active access grant for this contact to WebLock and reports how many grants were pushed.
These blocks only appear when the corresponding integration is installed and connected for your org.
## Payment methods
Saved cards and bank accounts on file appear in the sidebar. They are the same payment methods available when adding payments to invoices or membership charges.
## Worked examples
### Front-desk check-in for a padel member who owes money
1. Open the member from the [Member list](/members/member-list).
2. The **Due now** tile shows 30 in red - a court fee from last week.
3. Use **Add** > **Check-in** to record today's visit against their membership.
4. On the same profile, open the **Charges** tab and take payment for the outstanding 30, or redeem a voucher from the **Due now** tile if they have credit.
### Onboard a new jiu-jitsu member to the portal
1. Create or open the contact and click **Edit** to confirm the email is correct.
2. In the **User Account** block, click **Invite**. The block switches to show the invitation and its expiry.
3. If the member does not accept in time, use the overflow menu to **Resend invitation**.
### Give a climbing member a door PIN
1. Make sure access control is enabled for your org, with PIN as an identification method.
2. In the **Cards** block, click **Issue card**, choose **PIN code** as the card type, and enter the number. Leading zeros count.
3. Turn on **Make this their main card** if the PIN should be the member's main card, and save.
### Track belt progression for a martial arts student
1. Enable progression tracks so the **Level** tile appears in the info strip.
2. Use the plus icon on the **Level** tile to assign the student's current belt.
3. The **Activity** tab's timeline and the info strip keep the level visible for front-desk staff.
## Tips & best practices
* Read the info strip before every front-desk interaction: it answers "do they owe anything?", "have they checked in today?", and "how active are they?" without opening a tab.
* Keep sensitive context (injuries, disputes, access notes) in the **Notes** panel, not in message threads - notes stay internal.
* Prefer **Archive** over deletion when a member leaves: archiving preserves their financial and legal history and can be reversed with **Unarchive**.
* Redeem vouchers and add wallet credit from the **Due now** tile so the credit balance updates in place.
* When a family shares billing, create the household from the paying member's profile so they are set as the owner automatically.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| No **Invite** button in the User Account block | The contact has no email address | Click **Edit** and add an email, then reopen the block |
| **Due now** shows nothing, but the member has an unpaid booking | The booking has not started yet, so its charge is upcoming | Check **Upcoming** under **Due now**. To collect it ahead of time, use **Add** > **Payment** |
| No "No phone number" notice for a contact without a phone | Your gym does not require a phone number | Make the phone field required under **Settings** > **Members** > **Contacts** |
| **Reset password** missing from the overflow menu | The account is not active, you are not an admin, or it is your own account | Have an admin reset it; you cannot reset your own password from another user's profile |
| **Cards** block not visible | Access control is not enabled for the org | Enable it under [access control settings](/settings/access-control) |
| **PIN code** missing from the card types | PIN is not an identification method enabled for the club | Enable it under [access control settings](/settings/access-control) |
| Stripe or WebLock block missing | The integration is not installed or connected, or the contact has not been pushed yet | Connect the integration, then use **Sync** on the block once it appears |
| Only an **Unarchive** button in the header | The contact is archived | Unarchive to restore active use (requires `members.update.organization`) |
## Related
* [Member list](/members/member-list) - the grid this page opens from
* [Memberships](/sales/memberships) - the org-wide membership grid
* [Accounts](/members/accounts) - households and companies
* [Attendance](/schedule/attendance) - check-in workflows
* [Access cards](/members/member-cards) - cards and PIN management
* [Leads and pipeline](/members/leads) - the Pipeline block and the Leads tab
* [Engagement analytics](/analytics/engagement) - how the gauge is computed
# Member onboarding
Source: https://docs.1club.ai/members/onboarding
How a new person becomes a member - the signup wizard, required fields, family members, verification, and passwordless guest signup.
Member onboarding is the path from a brand new person to a fully set-up member: they complete a signup form or book as a guest, verify their email, accept any required documents, and land in your member list with the right plan attached. New members, and parents signing up their children, go through it on your website and member portal. You design the form itself under [Forms](/marketing/forms); this page explains the experience a new member goes through and the settings that shape it.
## When to use it
A short signup that collects only what you need turns interest into a paying member in one sitting, with documents signed and the plan attached. The settings on this page decide how short that path is: which fields are asked, whether family members can join in the same flow, and whether a first booking can create the account on its own.
Typical uses:
* **Prospects from an ad** - someone who clicks a trial-plan ad picks the plan, fills in a few details, signs the waiver, and pays in one short flow.
* **Parents enrolling kids** - a parent registers two children for a junior martial-arts program without becoming a member themselves, and each child gets their own record with a date of birth.
* **A first-time drop-in** - a visitor books a class from your website with just their email and a one-time code, and ends up with a member account they can sign in to.
See [Worked examples](#worked-examples) for the settings behind each of these.
## The signup wizard
New members complete signup through a step-by-step wizard (plan selection, details, optional documents and signature, review, and payment). The wizard guides them and validates as they go:
* **Step intros** explain each step. For example, the details step shows "We'll use your email to create your account.", and the review step shows "Check that everything's correct before you submit."
* **Inline validation on Next** - if a required field is missing, the wizard scrolls to and focuses the first invalid field instead of failing silently.
* **Editable review** - each section on the review step has an **Edit** link that jumps back to that step.
* **Already-registered email** - if the email is taken, the form shows "This email is already registered." with a "Sign in here" link, rather than a generic error.
* **Guardian-only signups** - when the buyer is signing up family members but not themselves, the wizard shows "You'll manage this account but won't be a member yourself." The **I'm joining too** option appears only once at least one family member has been added.
Drafts are saved in the member's browser as they go, so a half-finished signup survives a page refresh. Passwords and drawn signatures are never persisted in the draft.
### Combine plan and details into one step
Each signup form has a **Combine plan & user details into one step** toggle (off by default). Its help reads: "Show plan selection and the sign-up details together as the first step, instead of two separate steps." Turn it on to shorten the wizard to a single first step that shows plan selection and account details together.
## Configuring which fields appear
The visibility of optional system fields on the public signup form is set per organization under **Settings** > **Members** > **Contacts**. For each of **Phone**, **Date of birth**, and **Gender** you choose one of:
* **Required** - the field is shown and must be filled in to submit.
* **Optional** - the field is shown but can be left empty.
* **Hidden** - the field is not shown at all.
The server enforces these choices on submission - a hidden field cannot be back-filled by editing the request, and a required field that is empty rejects the signup.
## Family members on signup
The same **Settings** > **Members** > **Contacts** page has a **Show family members on signup** toggle:
* **On** - the signup form shows a "Family members" section where the primary signer can add additional members (children, partner, etc.) in the same flow. Each family member submitted carries its own date of birth.
* **Off** - the family-members section is hidden. The server rejects any request that includes family members with HTTP 400, which protects you against forged requests that try to enrol additional people while the feature is disabled.
When **Date of birth** is set to **Required**, the requirement applies to **every** family member as well - submitting a family member without a date of birth fails the signup. The public website and member portal validate this before the request is sent, so the user sees an inline message rather than a server error.
## Verification
* **Email verification** - a new member verifies their email with a one-time code to confirm the address and activate the account.
* **Browser check (Turnstile)** - signup is protected by a Cloudflare Turnstile check. If a privacy browser or ad blocker blocks it, the member sees an actionable message rather than a silent failure: "We couldn't verify your browser. If you use a privacy browser (like Brave) or an ad blocker, turn off its protection for this page or try a different browser, then try again." The same message appears on the public sign-in email form and in the portal.
## Passwordless guest signup
When a [booking policy](/settings/booking-policies) has **Allow guest bookings** turned on, a logged-out visitor can book from your public portal, website, or network without creating a password first. They enter their email, verify it with a one-time code, and complete the booking. 1Club then creates a passwordless member account and signs them in, so they can see and manage the booking. If the email already belongs to an active member, the visitor is routed to sign in instead. This turns a first booking into a member account with no extra signup step.
## Worked examples
* **Ad landing page** - share a signup form link that offers a single trial plan, with Phone optional and documents required, so prospects convert in one short flow.
* **Junior program** - use a form set to register children under a guardian account, with Date of birth required for every child, so each junior is enrolled with their own record.
* **Walk-in trial** - turn on guest bookings so a first-time visitor can book and pay for a drop-in without creating a password, then finish their profile later.
## Tips & best practices
* **Keep required fields minimal.** Every required field is a drop-off point; mark only what you truly need at signup and collect the rest later.
* **Attach documents through rules, not the form.** The wizard automatically shows any [document rules](/settings/waivers) that target new members, so a waiver added once applies to every form.
* **Test each form end to end** from a real signup before sharing the link.
## Related
* [Forms](/marketing/forms) - Build the form and its wizard steps.
* [Memberships](/sales/memberships) - What a selected plan creates for the new member.
* [Documents and waivers](/settings/waivers) and [Document signatures](/members/document-signatures) - The documents shown during signup.
* [Member portal](/settings/member-portal) - Where the new member signs in afterward.
* [Booking policies](/settings/booking-policies) - The guest-booking toggle behind passwordless signup.
# Members overview
Source: https://docs.1club.ai/members/overview
How contacts, members, leads, accounts, and cards fit together in 1Club, and which pages make up the Members section of the admin.
Everyone you track lives in one table: **contacts**. A contact becomes a **member** the moment you sell or assign them a membership. The same record may also be a lead in your sales pipeline, a drop-in, a dependent on a household account, or a staff member - the `type` field decides which bucket they show up in across the UI.
The **Members** section in the admin gives you that table and what hangs off it: the sales pipeline, accounts (households and companies), access cards, and document signatures. Three related pages moved to other sections: memberships are under **Sales**, matches under **Events**, and attendance under **Schedule**.
## What's in this section
The contacts grid. Tabs split it into all, members, drop-ins, leads, unpaid, accounts, and lapsed. Filter by tag, plan, status, or progression level.
A single contact's page: activity, bookings, memberships, check-ins, charges, documents, cards, notes, and account links.
The **Leads** tab: your sales pipeline as a board or a list, with stages, sources, and assignees.
Group contacts into households or companies for shared billing and acting-on-behalf-of permissions.
The **Cards** page: access cards and PINs members use at a reader or kiosk.
The ledger of waivers and other documents members have signed.
The section also covers:
* [Assessments](/members/assessments) - instructor scorecards after a class or private lesson.
* [Member authentication](/members/member-authentication) - how members sign in, and how you invite them.
* [Suspend and archive](/members/suspend-and-archive) - pausing a member, or archiving the record.
* [Member onboarding](/members/onboarding) - what a new member goes through when they sign up.
### Pages that moved
| Page | Now under | Docs |
| - | - | - |
| **Memberships** | **Sales** > **Memberships** | [Memberships](/sales/memberships) |
| **Matches** | **Events** > **Matches** | [Matches](/events/matches) |
| **Attendance** | **Schedule** > **Attendance** | [Attendance](/schedule/attendance) |
The overflow menu on the member list still links to **Memberships** and **Matches**, so you can reach them from Members too.
## Contact vs member
The two terms are not interchangeable in the data model, but the UI uses "member" almost everywhere:
* A **contact** is any person record in your org. Type can be `contact`, `member`, `lead`, `drop_in`, `lapsed`, `staff`, or `archived` (the "deleted" disposition - records are soft-deleted, never removed).
* A **member** is a contact with `type = member`, which typically means they have or had a membership.
* A **lead** is a contact who has never been a customer and is in your [pipeline](/members/leads).
* The page is labelled **Members**, but the underlying table is `contacts`. The admin URL is `/members/list`, while API calls hit `/contacts`.
The type follows what the contact actually holds and does, so you don't set it by hand. Click the type chip on a profile to see its type history.
## A member belongs to the organization
A contact belongs to your whole organization, not to one of its **Clubs**. Staff at every location see the same member, so a person registered at one location is not added again at another. Where a member trains is recorded on what they do: each membership, booking, check-in, and order records its own **Club**.
Members permissions follow the same rule. Access to the member list and profiles is granted for the whole organization, and members access has no per-**Club** scope in [Permissions](/reference/roles-permissions).
## Plans vs memberships
Plans are templates; memberships are instances:
* A **plan** lives under [**Sales** > **Plans**](/sales/plans). It defines price, billing frequency, signup fee, max uses per period, validity window, scope (which classes, areas, or instructors it covers), and whether it auto-renews.
* A **membership** is what you assign to a specific contact when they sign up. It snapshots the plan's terms at the moment of sale, so editing the plan later does not retroactively change existing memberships. Find them all under [**Sales** > **Memberships**](/sales/memberships).
The same contact can hold more than one active membership at a time. A padel gym, for example, might sell a member both a "Court access" plan and a separate "Group coaching" plan that run in parallel.
## Accounts roll up billing
[Accounts](/members/accounts) let you group contacts under a shared billing entity:
* **Household** accounts use the roles Owner, Adult, and Dependent to keep a family unit together. A parent enrolling two kids in a jiu-jitsu kids program is a household.
* **Company** accounts use the roles Admin and Employee to bill an employer for its people, for example a corporate gym membership.
Memberships stay attached to the individual contact. Invoices and charges roll up to the account, and account roles control who can book or check in on behalf of whom.
## Cards are how members get in
If you run access control, members carry one or more [cards](/members/member-cards): a physical access card or a PIN. One is marked as their main card. Members with a portal account also have a QR member code they can show from the portal or app.
How strict the door is depends on your check-in mode. See [Access control](/settings/access-control) for the configuration and [Check-in models](/reference/check-in-models) for what each mode allows.
## Related
* [Member list](/members/member-list) - find, filter, and bulk-act on contacts
* [Member profile](/members/member-profiles) - the page a row click opens
* [Leads and pipeline](/members/leads) - the sales pipeline on the Leads tab
* [Memberships](/sales/memberships) - the membership grid and lifecycle, under Sales
* [Accounts](/members/accounts) - households and companies
* [Smart tagging](/settings/smart-tagging) - automatic tags driven by rules
* [**Sales** > **Plans**](/sales/plans) - the plan templates memberships are built from
# Suspend and archive
Source: https://docs.1club.ai/members/suspend-and-archive
Pause a member's access without unmaking them, or archive the record for good. How the two differ, what each one stops, and how to reverse them.
Two actions stop a member using your gym, and they are not interchangeable.
* **Suspend** pauses a member. Their record, plan, and history stay exactly as they are, they cannot book or check in, and one click puts them back. Reach for this first.
* **Archive** removes the member from active use. It cancels their memberships and future bookings, hides them from your lists, and keeps the record only for financial and legal history.
Both live in the three-dot overflow menu on the [member profile](/members/member-profiles), and both apply to your gym alone. The same person can stay an active member somewhere else.
## Which one do I want?
| | Suspend | Archive |
| - | - | - |
| Member can book or check in | No | No |
| Member can sign in to the portal | No | No |
| Memberships and future bookings | Untouched, still billable | Cancelled |
| Shows in the Member list | Yes, marked suspended | No, moves to the Archive |
| Staff role and permissions | Kept | Removed |
| Reversing it restores everything | Yes | No, the role and account links are gone |
| Permission needed | `members.update.organization` | `members.delete.organization` |
Use **suspend** for a member you expect to come back, or one you are pausing while something is settled - unpaid invoices, a disputed charge, a conduct issue, a doctor's note. Because nothing is cancelled, you can still invoice them for what they owe while they are suspended.
Use **archive** for a member who has left and is not coming back.
Suspending a member does not stop their billing. If they should not be charged while they are away, pause their membership too - see [Memberships](/sales/memberships).
## Suspend a member
1. Open the member profile from the [Member list](/members/member-list).
2. Click the three-dot menu in the header, next to **Edit**.
3. Choose **Suspend** and confirm.
The profile then shows a banner saying the member is suspended, with a **Reinstate** button inside it. The member is signed out of the member portal straight away and cannot sign back in to your gym.
Only a member with an active account can be suspended. A member who has been invited but has not accepted yet, or who has not finished signing up, has no active account to pause - invite or archive them instead.
## Reinstate a member
Click **Reinstate**, either in the banner or in the same three-dot menu. The membership returns to active with the role, plan, bookings, and history it had before. Nothing needs re-entering.
You cannot reinstate your own account. If your access has been suspended, another admin has to lift it.
## Suspending a colleague
Staff are members too, so the same action pauses a colleague's access to the admin. Seniority applies:
* Pausing anyone with admin access needs staff-management permission on top of `members.update.organization`.
* A manager cannot suspend an admin or an account owner.
* Only an admin or an account owner can suspend an owner.
* You cannot suspend yourself, and you cannot suspend the last active account owner - somebody has to be able to administer the gym.
A suspended colleague is signed out of the admin within a minute and cannot sign back in until someone reinstates them.
## Archive a member
Archiving is in the same three-dot menu, below **Suspend**. It needs the `members.delete.organization` permission, and unarchiving needs `members.update.organization`. Members permissions apply to the whole organization, so there is no per-**Club** version of either.
An archived member is kept for financial and legal history but is hidden from your lists, has memberships and future bookings cancelled, and cannot sign in. Find archived records under **Settings** > **Data Tools** > **Archive**, also reachable from **Archive** in the overflow menu on the [Member list](/members/member-list).
To bring someone back, open their record from the Archive and click **Unarchive**. Their contact details and history return, but their staff role and account links do not - re-add those by hand.
Archiving cancels memberships and future bookings, and unarchiving does not restore them. If you only want to stop someone temporarily, suspend them instead.
## A member says they cannot sign in
Work through these in order:
1. **Are they suspended?** Open their profile. A suspended member shows a banner. Click **Reinstate**.
2. **Are they archived?** Check **Settings** > **Data Tools** > **Archive**. Unarchive the record if they should be back.
3. **Have they confirmed their email?** An unconfirmed account is refused with a different message. Resend the invitation from their profile.
4. **Is their membership at another gym?** Suspension and archiving apply to your gym only. Being blocked here says nothing about anywhere else.
If none of these apply and the member is told their account cannot be used at all, the address has been refused across the whole 1club platform rather than by your gym. Contact support at [support@1club.ai](mailto:support@1club.ai) - no gym can set or lift that.
# Point of sale
Source: https://docs.1club.ai/pos/point-of-sale
Ring up memberships, passes, bookings, packages, and retail products in person at the front desk, take payment, apply promotions, and check members in - all from the POS register.
The point of sale (POS) is the in-person register your staff use at the front desk. From one screen you can sell a membership, a class booking, a day pass, a retail product, or a bundled package, take payment, and check the member in - in a single order.
The POS is a thin, fast way to build an [order](/sales/orders) and settle it. Everything it creates (bookings, memberships, product sales, charges, and payments) lands in the same records as the rest of 1Club, so a sale rung up at the desk shows up in [Charges](/sales/charges), [Payments](/sales/payments), and on the member's profile exactly as it would from any other surface.
## When to use it
Use the POS whenever someone is standing at the desk and wants to buy something. Memberships, passes, class and area bookings, events, packages, and retail products all go in one order and are paid together, and bookings can be checked in the moment the order completes.
Typical uses:
* **A new member joining at the desk** - you create the contact without leaving the register, add a membership with its start date and signup fee, and take payment by card or cash.
* **Four friends booking a padel court** - you add an area booking for the next hour and a few drinks, take one payment, and the booking is checked in as the order completes.
* **A coffee for a passer-by** - a product-only order completes without a member attached.
* **An order to finish later** - a member is not ready to pay yet, so you save the order as a draft and load it back from the **Orders** button when they are.
## Opening the register
Go to **Point of Sale** in the left rail. The URL is `/pos`. The register is its own rail item, separate from **Sales**, where you manage the catalog and review orders and payments afterwards.
The POS only appears when it is turned on for your organization. If you do not see it, an admin can enable it under **Settings** > **Sales** > **Point of Sale** (see [POS settings](#pos-settings) below).
## The register layout
The register has two panes:
* **Catalog** (main area) - a tabbed grid of everything you can sell.
* **Order summary** (right sidebar) - the current cart, the member it is for, running totals, and the action buttons.
### Catalog tabs
The catalog is split into tabs so you can find what you are selling quickly:
* **Products** - retail products, grouped by [category](/sales/products).
* **Rentals** - gear you lend out rather than sell, with what is still out listed below it so you can take items back. Shown when [rentals](/sales/rentals) are on, or while gear is still out.
* **Passes** - one-time plans (day passes, single-entry passes).
* **Memberships** - recurring plans.
* **Classes** - today's classes.
* **Events** - live, bookable [events](/events/events); events with ticket tiers open a menu to pick a tier.
* **Areas** - bookable areas, priced per hour.
* **Instructors** - bookable instructors, priced per hour.
* **Packages** - fixed-price [packages](/sales/packages) that bundle plans and products.
Click any card to add it to the order. Products, passes, packages, and event tickets can be added more than once and show a quantity stepper in the cart.
### Building the order
Each line in the order summary can be adjusted before you take payment:
* **Quantity** - use the stepper on products, passes, packages, and event tickets.
* **Membership start date** - set per membership line. If the plan has a signup fee, it shows as its own sub-line.
* **Booking date and time** - area and instructor bookings default to the next full hour; adjust with the date and time picker. Class and event bookings inherit their time from the class or event.
* **Entrance method** - every booking line needs one: pay directly, use one of the member's eligible memberships, or use an external program. The price updates to match the method you pick (a membership entry resolves to zero, direct payment uses the resource price).
* **Notes** - add a per-product note (for example a preparation instruction), and an order-level note for the whole ticket (for example kitchen instructions or a buzzer number).
The sidebar shows a live **subtotal**, **tax**, and **total**, recalculated on the server as you change the cart.
### The member
Use the contact selector at the top of the sidebar to attach the order to a member, or click the add button to create a new contact without leaving the register.
A member is required when the cart contains a booking, a membership, or a package (packages can deconstruct into memberships). Product-only orders can be completed without a member attached.
## Completing an order
Click **Complete** to open the confirmation dialog, then confirm. Completing an order runs three steps in sequence:
1. **Create and execute the order** - the line items become real bookings, memberships, and product charges.
2. **Take payment** - a payment is recorded against the generated charges using the payment method you selected.
3. **Check in** (optional) - when "check in on complete" is on, each booking created by the order is checked in immediately. This is on by default, since the whole party is usually standing at the desk.
### Payment methods
The payment method selector appears whenever the order has an amount left to pay. Which methods are offered depends on what your organization has enabled under [Payment methods](/settings/payment-methods):
* **Cash and other manual methods** - recorded by the operator at the desk.
* **Member wallet credit** - draws down the member's wallet balance.
* **Card via Stripe** - charged online through Stripe.
* **Card on a terminal** - card tenders settled on a connected payment terminal (for example myPOS).
If a promotion brings the payable total to zero (for example a 100%-off pass code), the order completes without asking for a payment method.
### Applying a promotion
When the cart has a booking or membership line and a member is selected, you can apply a [promotion](/sales/promotions) code in the confirmation dialog. The discount is applied to a single eligible line and recorded as a discount line on the order.
### Saving for later
You do not have to finish an order in one go:
* **Save and close** stores the order as a **draft** you can resume later.
* If you owe money but have not chosen a payment method, completing instead saves the order as a draft.
* **Discard** removes the current order.
The **Orders** button above the catalog lists your in-progress drafts. Pick one to load it back into the register, or open **All orders** to jump to the full [Orders](/sales/orders) list.
## POS settings
POS is a per-organization feature you turn on and off.
Go to **Settings** > **Sales** > **Point of Sale** and toggle it on. When off, the **Point of Sale** rail item is hidden and the register is unavailable.
Turning POS on or off requires organization settings permission. Viewing the setting requires read access to settings.
## Who can use the POS
The POS is permission-gated, not role-gated. Staff need POS or billing permissions for the location they are selling at:
* Reading the register and its catalog uses read access to POS or billing.
* Creating, completing, and cancelling orders uses create access to POS or billing.
* Lending rental gear also needs create access to rentals.
Front-desk staff have these permissions by default. See [Roles and permissions](/reference/roles-permissions) for the full model.
## Related
* [Orders](/sales/orders) - the records the POS creates, and how they are settled.
* [Products](/sales/products) - the retail catalog the Products tab draws from.
* [Packages](/sales/packages) - fixed-price bundles sold on the POS.
* [Equipment rentals](/sales/rentals) - gear lent out from the Rentals tab and taken back at the till.
* [Plans](/sales/plans) - the membership and pass templates behind the Memberships and Passes tabs.
* [Payments](/sales/payments) - where POS settlements are recorded.
* [Payment methods](/settings/payment-methods) - which tenders the POS can accept.
* [Charges](/sales/charges) - the obligations a completed order generates.
# Quickstart
Source: https://docs.1club.ai/quickstart
Get your 1Club admin up and running in a few steps
## Get started with 1Club admin
This guide walks you through the first steps: sign in, complete your profile, add a club and a plan, and create your first member and booking.
## Step 1: Sign in
1. Open your organization's admin URL (for example **[https://admin.1club.ai](https://admin.1club.ai)** or your custom domain).
2. Sign in with **Google**, **Apple**, **Facebook**, or the email and password your administrator gave you. See [Signing in](/reference/sign-in) for all supported methods.
3. If you belong to multiple organizations, select the one you want to work in.
## Step 2: Complete your profile
1. Open the account menu at the bottom of the left rail and choose **Profile Settings**.
2. Update your name and contact information. See [Profile](/settings/profile).
3. Choose **My Notifications** in the same menu if you want to adjust email or app notification preferences.
## Step 3: Set up your organization and gym
1. Go to **Settings** > **Organization** > **Details** and check your organization name and slug.
2. Go to **Settings** > **Clubs** > **Clubs** and add at least one club (for example, your main location) with its name, address, and contact details.
3. Optionally configure **Settings** > **Clubs** > **Area types** and **Instructor types** so you can define areas and assign instructors later.
## Step 4: Configure billing (optional but recommended)
1. Go to **Settings** > **Integrations** and connect **Stripe** so you can collect payments.
2. Go to **Sales** > **Plans** and create at least one membership plan (name, price, billing interval). See [Plans](/sales/plans).
3. Optionally set up **Settings** > **Billing** > **General** (currency, tax rates, payment methods) and **Settings** > **Billing** > **Automated billing** for bill runs.
## Step 5: Add your first member
1. Go to **Members** and add a new member (or use the member portal signup if enabled).
2. Sell them a membership from their profile. See [Memberships](/sales/memberships).
3. Optionally issue an access card if you use RFID or other access credentials. See [Member cards](/members/member-cards).
## Step 6: Create a booking or class
1. Go to **Settings** > **Clubs** > **Areas** and make sure you have at least one area (for example, a court or room). See [Areas](/settings/areas).
2. Go to **Schedule** and create a booking for the member and area. See [Calendar](/schedule/calendar).
3. Or open **Schedule** > **Classes** to create a class and assign an instructor; members can then book into the class. See [Classes](/schedule/classes).
## Next steps
* Open [Home](/home/overview) to run the day from **Today** and check your key [metrics](/home/metrics).
* Set up the [Inbox](/inbox/overview) and [templates](/settings/templates) for member communication.
* Configure **Settings** > **Member Apps** > **Branding** and the [Member portal](/settings/member-portal) so members can log in and book online.
* Read [Welcome to 1club](/) for a tour of every admin section.
# AI assistants
Source: https://docs.1club.ai/reference/ai-assistants
The AI copilot built into 1Club - an admin chat assistant that books, configures, messages, and reports, plus AI reply suggestions, a day overview, a website builder, and a member-facing assistant.
1Club has AI woven through the product. In the admin, staff get a chat copilot that can take real actions on their behalf: check members in, create bookings, change settings, draft messages, and answer questions about your data. On the member side, your portal and public website can carry an assistant that answers members' questions and books classes for them. This page maps out every AI surface and what each one lets you do.
## When it helps
* **Doing a task in one sentence** - type "check in Sarah for the 6pm class" and the copilot routes it to the booking assistant, does it, and confirms exactly what happened.
* **Asking about your numbers** - ask for peak hours, month-over-month comparisons, or revenue for a period in plain language.
* **Replying to the inbox faster** - generate a reply grounded in the message and your organization's data, check its tone and confidence score, and edit it before sending.
* **Letting members help themselves** - turn on the member assistant for your portal or website, so members can ask about classes, availability, and plans, and create or cancel their own bookings.
## The admin AI copilot
Open the AI panel from anywhere in the admin to chat with your copilot. Ask it something in plain language and it figures out what you want, then either answers or performs the action. Behind the panel is a set of specialized assistants, and a router reads your message and hands it to the right one automatically.
You don't have to think about which assistant handles what. Type "check in Sarah for the 6pm class" and it routes to booking; type "how do I set up pricing rules?" and it routes to help. When the copilot isn't confident which assistant you need, it asks a quick clarifying question instead of guessing.
### The agent picker
Next to the chat input is an **agent picker**. Leave it on the default and the router chooses for you. Or pick a specific assistant to keep the whole conversation focused on one job (for example, force every message to the configuration assistant while you set up your gym).
### What each assistant does
Check members in for classes and activities, create and cancel bookings,
look up member schedules, and create memberships. It searches members, lists
classes and plans, and reads the schedule to complete the action.
Find a member by name, email, or phone, then draft a message to them. The
draft is saved for you to review and send from the inbox - the assistant
composes it, you send it.
Change organization settings and create or edit entities - areas, area
types, policies, templates, plans, and more. It can also draft gym events to
engage members, translate entities, and toggle progression tracks.
Answer questions about your business data - bookings, memberships,
check-ins, contacts, and revenue. Ask for trends, peak hours,
month-over-month comparisons, or totals for a period.
Answer how-to and what-is questions grounded in the 1Club documentation and
your live settings, and point you to the right page. This assistant is
read-only - it never changes anything.
Design and update your public website by chat - add pages and sections,
populate them from your organization data, edit the header and footer, and
adjust styling. See [Website](/marketing/website).
### Actions and confirmations
When an assistant completes something with a real effect - a booking created, a setting changed, a message drafted - the reply confirms exactly what happened. Assistants also surface clickable suggestion chips below their answers, so you can jump to a relevant page or continue with a follow-up in one tap.
## AI day summary on Home
The line under the greeting on [Home](/home/overview) is a one-sentence AI summary of the most relevant thing about today, such as the next class, how busy the day is, or an overdue invoice. It is scoped to the person reading it, so an instructor gets a summary of their own day, and it only mentions money and membership figures they are allowed to see.
## AI reply suggestions in the inbox
Inside a message thread, the compose pane has a **Generate AI reply** action. It drafts a response grounded in the original message plus your organization's data (retrieved with pgvector similarity search), and lands the suggestion in the editor for you to edit before sending. Each suggestion carries a tone and a confidence score so staff can review before anything goes out. The feature works on replies, not brand-new sends. See [Messaging](/inbox/overview).
## Page suggestions
On some screens the copilot offers page-aware suggestion chips - context-specific next actions for where you are in the admin. Suggestions are gated on the permission needed to act on them, so you're never shown an action you can't take. Screens with no suggestions simply show none.
## AI business audit
1Club can generate an AI-powered audit of your gym's online presence. The report scores your public business profile, review sentiment, website performance, and AI discoverability, then gives you recommendations to improve. A report is generated automatically after onboarding, and you can regenerate it from the admin. See [Business audit](/home/ai-visibility-report).
## Member-facing assistant
Your members can have their own AI assistant, available in the member portal and on your public website. It answers questions about classes, availability, instructors, plans, and gym locations, and it can act on the member's behalf - creating and cancelling their bookings, and looking up their profile, bookings, and memberships.
The member assistant is off by default and enabled per surface:
* **Portal** - turn on the assistant for your member portal.
* **Website** - turn on the assistant for your public website (the site must be active).
Member chat is rate-limited so a single account can't fire off unlimited requests.
## Configure agents and review history
Go to **Settings** > **Artificial Intelligence** to manage the AI experience:
* **AI assistants (MCP)** connects outside AI clients to your organization. See [MCP](/mcp/overview).
* **Agents** (`/settings/ai/agents`) lists the available admin assistants with descriptions and example prompts, so your team knows what the copilot can do.
* **Conversation History** (`/settings/ai/conversations`) lets you browse past AI chat sessions and read the full back-and-forth of each one.
## Availability
AI features depend on your organization's plan and configuration. The admin copilot and AI reply suggestions need AI enabled for your organization; the member-facing assistant is off until you turn it on for the portal or website; and the day summary on **Home** stays blank when AI can't run. If an AI surface isn't showing up, check your plan and the relevant settings first.
## Related
* [Home](/home/overview) - The first page in the admin, with its AI day summary.
* [Inbox](/inbox/overview) - Where AI drafts reply suggestions.
* [Business audit](/home/ai-visibility-report) - The AI report on your online presence and discoverability.
* [Schedule](/schedule/calendar) - The bookings and classes the booking assistant works against.
* [Website](/marketing/website) - The site the website builder assistant edits.
* [Settings overview](/settings/overview) - Where to configure agents and review conversation history.
# Check-in models
Source: https://docs.1club.ai/reference/check-in-models
Choose how members enter your gym - book and pay in advance, walk in with a payment check, or flexible check-in - and configure the settings that enforce it.
1club supports the three industry-standard ways members enter a gym. An owner or admin picks the model that matches how the front desk runs and configures a small set of booking-policy settings to enforce it; the front desk then works to that model at every check-in. The model is not a single switch - it is the combination of when you enforce payment and whether people can be checked in without a booking.
## Why the model matters
Every gym has its own front-desk culture. Some require members to book and pay before they set foot in the building. Others welcome walk-ins as long as the member has a paid membership or pays at the door. A third group - community gyms, martial-arts schools, kids' programs - log attendance on the spot and trust staff to handle payment separately. The model decides whether someone can check in without a booking, and whether a membership, an external program, or a direct payment must be confirmed first. How members identify themselves at the door (QR code, access card, PIN) is a separate question, covered by [Access control](/settings/access-control).
### The three check-in models
| Model | Booking required | Payment enforced | Typical gyms |
| :- | :- | :- | :- |
| **Book and pay** | Yes | Yes - at booking | Class-based studios, boutique fitness, racket sports |
| **Walk-in with payment check** | No | Yes - at check-in | Drop-in yoga, casual gyms, day passes |
| **Flexible check-in** | No | No - staff judgment | Community gyms, martial-arts schools, kids' programs |
## The two settings that define a model
A check-in model is the combination of two things, and both live on the **booking policy** that matches the booking or visit. See [Booking policies](/settings/booking-policies) for how to create and scope policies.
* **Enforce payment** - a single dropdown with three options: **At booking**, **At check-in**, or **None**. This decides when payment is required.
* **Allow walk-ins** and **Allow anonymous check-ins** - two toggles that decide whether someone can be checked in without an underlying booking.
* **Allow walk-ins** (**Settings** > **Bookings** > **Booking and cancellation policies**) - "Check in a contact who has no booking." Use this for members who arrive without reserving.
* **Allow anonymous check-ins** - "Check in a visitor with no contact record." Use this for guests and trial visitors who have no profile yet. It is independent of walk-ins.
These two toggles live on the booking policy, alongside **Enforce payment**.
They replace the older Flexible (Anonymous) Check-in toggle that used to live in
Access control settings. [Access control](/settings/access-control) now covers
only physical identification - member enrollment and which credentials (QR code,
access card, PIN) the scanner accepts - not whether walk-ins are allowed.
### Book and pay
Members must book in advance and pay before the booking is confirmed. No booking, no entry. The schedule is the source of truth.
To configure:
* Set **Enforce payment** to **At booking** on the matching booking policy.
* Turn **Check-in required** on so unclaimed bookings auto-mark as no-shows.
* Leave **Allow walk-ins** and **Allow anonymous check-ins** off.
### Walk-in with payment check
Members can show up without a booking, but the front desk must confirm an active membership, an external program, or collect a direct payment before letting them in.
To configure:
* Set **Enforce payment** to **At check-in**.
* Turn **Allow walk-ins** on so staff can check in a member who has no booking.
* Turn **Allow anonymous check-ins** on as well if you also want to admit trial visitors who have no profile yet.
When the front desk checks someone in, they pick an entrance method (membership, external program, or direct payment) - see below.
### Flexible check-in
Staff log attendance as members arrive. Payment status is visible but does not block entry. The front desk uses judgment to follow up on overdue accounts.
To configure:
* Set **Enforce payment** to **None**.
* Turn **Allow walk-ins** on so staff can log attendance without a booking.
* Turn **Allow anonymous check-ins** on if you want to record guests and trial visitors who have no profile.
## At the front desk
**Enforce payment** and **Allow walk-ins** are rules for members checking themselves in - at the kiosk, in the check-in app, or at a scanner. They don't refuse staff. When an admin, manager, front desk, or instructor checks someone in from the admin dashboard or the instructor portal:
* **Payment** - the dialog says when the gym asks for payment at check-in. Staff can still let the member in; without a payment, the amount stays owed.
* **Walk-ins** - staff can check in a known contact who has no booking even when **Allow walk-ins** is off. The dialog notes that walk-ins are turned off for members.
* **The check-in window** - staff can check in outside the hour-before-to-end-of-day window. See [Attendance](/schedule/attendance#check-in-window).
**Allow anonymous check-ins** is different. Only staff can check in someone with no contact at all, so it stays enforced for them: with it off, the check-in needs a contact.
## Entrance methods at check-in
When the front desk checks a member in, they pick how the member is entering. Three entrance methods exist:
* **Membership** - the check-in counts against the member's active membership. If the plan has a session limit, usage is decremented automatically, using the same rule as a booking.
* **External program** - the member enters via a third-party program (a partner curriculum, a corporate-wellness benefit, a federation pass), optionally with an extra per-visit fee. See [External programs](/settings/external-programs).
* **Direct payment** - the member pays for this single visit at the front desk.
A booking that was made without an entrance method needs one picked before check-in, whatever the policy says - otherwise there is nothing to record the visit against. Beyond that, the matching booking policy decides how hard payment is asked for. If **Enforce payment** is set to **At check-in** (or to **At booking** with an outstanding balance on the booking), members can't check themselves in until it's paid, and the front desk is reminded to collect but can still admit them. If **Enforce payment** is **None**, payment is never asked for. Free classes never require a payment method.
Because check-in reuses the booking pipeline, membership consumption, external-program accounting, and direct-payment charges are recorded identically whether the member booked ahead or walked in.
### Repeat scans
A membership check-in with no booking and no class is idempotent for a short window. If the same member is scanned again within about five minutes on the same membership, 1club treats it as the same entry rather than creating a second attendance row or debiting a second session. Beyond that window, a genuine second visit the same day is allowed - there is no daily cap.
## Picking your model
Most gyms settle on one model per location, sometimes with a different policy for personal training, a wellness area, or special programs. If you run multiple locations under one organization, scope different booking policies to each - see the **Scope** section in [Booking policies](/settings/booking-policies).
If you're not sure where to start:
* A studio that sells classes online and runs a tight schedule wants **Book and pay**.
* A neighborhood gym with lots of drop-ins wants **Walk-in with payment check**.
* A martial-arts school or community gym where members come and go wants **Flexible check-in**.
You can change settings at any time. The new policy applies from the moment you save it. Existing bookings are not retroactively re-validated.
## Worked examples
### Padel and tennis club - book and pay
You sell court time online and want guaranteed revenue per reservation.
* **Enforce payment**: At booking.
* **Check-in required**: on (no-shows are marked automatically at end of day).
* **Allow walk-ins** / **Allow anonymous check-ins**: off.
* Scope the policy to your court area types so the whole racket-sports schedule follows the same rule.
### Drop-in yoga or Pilates studio - walk-in with payment check
You want walk-ins welcome, but reception must collect payment or confirm a pass first.
* **Enforce payment**: At check-in.
* **Allow walk-ins**: on (members without a booking can be checked in).
* **Allow anonymous check-ins**: on if you also run trial classes for visitors with no profile.
* At the door, staff pick **Membership** for pass holders or **Direct payment** for a single class.
### Jiu-jitsu or muay-thai academy - flexible check-in
Students come and go around open-mat and class times; you handle billing on monthly memberships, not per visit.
* **Enforce payment**: None.
* **Allow walk-ins**: on so coaches can tap students in as they arrive.
* **Allow anonymous check-ins**: on for prospective students trying a first session.
* Payment status still shows on each check-in, so staff can follow up on overdue accounts without blocking the mats.
### Climbing gym with day passes and members - mixed
Members enter freely; day-pass visitors pay at the desk.
* **Enforce payment**: At check-in.
* **Allow walk-ins**: on for members without a reservation.
* **Allow anonymous check-ins**: on for day-pass visitors with no account.
* Add a **Day pass** as an [external program](/settings/external-programs) or use **Direct payment** for the single visit.
### Corporate-wellness partner - external program entry
A local employer sponsors sessions for its staff.
* Create an [external program](/settings/external-programs) for the employer, with an optional per-visit fee billed to the partner.
* At check-in, staff pick **External program** so the visit is accounted to the partner, not the individual.
## Tips and best practices
* **Model your front desk, not your ambitions.** Set **Enforce payment** to what reception actually does today. If staff always collect at the door, use **At check-in**.
* **Walk-ins and anonymous check-ins are different.** A walk-in is a known contact without a booking; an anonymous check-in has no profile at all. Turn on only what you need.
* **Keep Check-in required for the book-and-pay model.** It is what turns unclaimed bookings into no-shows and keeps attendance reports accurate.
* **Use scope instead of many policies.** One org-wide policy plus a scoped exception (for example, personal training) is easier to reason about than several overlapping policies.
* **Test end to end.** After changing a policy, make a real booking or walk-in and check someone in before going live.
## Troubleshooting
**Issue**: Front desk can't check a member in - the system asks for an entrance method.
**Cause**: The booking was made without an entrance method, so there is nothing yet to record the visit against.
**Fix**: Have reception pick an entrance method - confirm the member's **Membership**, apply an **External program**, or take a **Direct payment** - then complete the check-in.
**Issue**: A member can't check themselves in without a booking.
**Cause**: **Allow walk-ins** is off on the matching booking policy. It binds members checking themselves in; the front desk can still check them in from the admin dashboard.
**Fix**: Turn **Allow walk-ins** on in **Settings** > **Bookings** > **Booking and cancellation policies** for the policy that applies to that gym or class.
**Issue**: Can't check in a trial visitor who has no profile.
**Cause**: **Allow anonymous check-ins** is off. Walk-ins cover known contacts only.
**Fix**: Turn **Allow anonymous check-ins** on. It is independent of **Allow walk-ins**, so enable both if you want either kind of no-booking entry.
**Issue**: I looked for a "Check-in mode" or "Flexible" switch in Access control and couldn't find it.
**Cause**: That toggle was replaced. Walk-in and anonymous check-in are now booking-policy settings.
**Fix**: Use **Allow walk-ins** and **Allow anonymous check-ins** on the booking policy. [Access control](/settings/access-control) now only configures physical identification (QR code, access card, PIN).
**Issue**: Scanning a member twice created only one attendance record.
**Cause**: A repeat scan of the same membership within about five minutes is treated as the same entry, so it never double-counts or debits a second session.
**Fix**: This is expected. A genuine second visit later the same day is recorded normally.
## Related
* [Booking policies](/settings/booking-policies) - Configure **Enforce payment**, **Allow walk-ins**, and **Allow anonymous check-ins**.
* [Access control](/settings/access-control) - Physical identification at the door: enrollment and QR/card/PIN methods.
* [External programs](/settings/external-programs) - Add partner offerings and corporate-wellness benefits as an entrance method.
* [Attendance](/schedule/attendance) - Record and review check-ins. Attendance and Check-ins live under **Schedule** in the admin.
# Organizations and clubs
Source: https://docs.1club.ai/reference/organization-clubs
How 1Club's multi-tenant model works - organizations as the top-level tenant, clubs as locations, org and club switching, and how every record is scoped by organizationId.
Every record in 1Club belongs to an **organization** - the top-level tenant that owns your members, bookings, plans, invoices, messages, and settings, and the unit of billing, branding, and permissions. An organization has one or more **clubs**, the physical locations (a gym, studio, court complex, or venue) where activity actually happens. A single user can belong to more than one organization, with a different role in each, and when they open the admin they work inside one organization at a time.
## When this matters
* **Opening a second location** - add it as another club in the same organization, so the two share memberships, billing, and branding while each keeps its own address, hours, sports, and amenities.
* **Running two separate businesses** - keep them as two organizations under one login, each with its own Stripe account and member base, and move between them with the organization switcher.
* **Comparing locations** - admins switch to **All clubs** for the org-wide picture, while front-desk staff work within one club.
* **A member visits two locations** - members belong to the organization, not to a club, so front desk at either location finds the same member record instead of creating a duplicate.
* **Data that seems to be missing** - most "missing data" reports are really the wrong organization or club selected.
## Organizations
An organization is your 1Club account. Its identity and account-wide settings live under **Settings** > **Organization** (see [Organization settings](/settings/organization)).
The organization owns:
* **Identity** - name, slug, and description used in emails, member portals, and admin headers. The organization **Email** is the fallback sender for organization-wide mail; see [Email sending](/settings/email-sending).
* **Locale and formatting** - time zone, currency, supported locales, and default locale. The time zone is the default for every date-related feature: today/this-week widgets, recurring jobs, and exports.
* **Billing and branding** - Stripe mode and connection, plan, and brand assets apply to the whole org and are shared by all of its clubs.
* **People** - members, contacts, staff, and the users who can sign in to the admin. A member belongs to the organization, not to one of its clubs: their memberships, bookings, check-ins, and orders each carry their own club.
Because everything is scoped by `organizationId`, a member, booking, or invoice created in one organization is invisible in another - even if the same person administers both.
"Organization" is an internal term. It is never shown to members in the mobile app or portal. To a member, your brand is simply the club (or a set of clubs) they choose from. Use "organization" in the admin and in staff-facing docs, not in member-facing copy.
### Switching organization
If your user belongs to more than one organization, an organization switcher appears in the organization menu at the top of the left rail. Selecting a different organization:
1. Calls the switch-organization flow, which reissues your session for the new org (the org context is carried in your JWT).
2. Drops all cached queries so the previous organization's records cannot linger on screen.
3. Reloads every list and detail view against the new organization's data.
Your role, permissions, currency, and time zone all follow the organization you switch into. If a page or menu item disappears after switching, it is because your role in the new org does not grant it - not a bug.
Creating a new organization is a platform-level action available only to 1Club staff (super admins). To add another organization to your account, contact support rather than looking for a "New organization" button in settings.
## Clubs
A club is a physical location that belongs to an organization. Every organization has at least one club; multi-location businesses add more. Manage them under **Settings** > **Clubs** > **Clubs**, which lists each club as a card showing its name, slug, address, contact details, status, and 1Club review rating.
Bookings, areas, classes, and schedules are associated with a club, so the club you have selected determines which location's activity you are looking at.
### Creating or editing a club
Click **Add** on the Clubs list to create a club, or open a card to edit one. The club form is grouped into sections:
**Basic information**
* **Club name** - the location's display name.
* **Slug** - the URL-safe identifier used in public links (lowercase letters, numbers, and hyphens). Change it carefully; existing links break.
* **Subtitle** and **Description** - short tagline and richer copy shown on public booking pages.
* **Status** - **Active**, **Hidden**, or **Closed**. **Active** is visible to everyone. **Hidden** keeps the club fully open to your staff (schedule, bookings, and reports keep working) but takes it off your website, the member portal, and the 1Club marketplace. **Closed** retires it: it leaves the schedule, the club switcher, and every club picker. Use **Set closing date** on the club page to close it automatically on a given day.
* **Club type** - an optional preset (for example a racket-sports or fitness template) that seeds sensible defaults.
* **Club managers** - staff contacts who receive contact-form messages sent to this club. When empty, those messages fall back to organization-level staff.
**Contact information**
* **Phone**, **Email**, and **Website** - the location's public contact details. **Email** does double duty: it is also the **From** address on most member email for this club, so keep it on a domain you have verified. See [Email sending](/settings/email-sending).
* **Address** - typed or picked from autocomplete; latitude and longitude are stored so the location can be mapped and its time zone derived.
* **Social profiles** - links shown on public pages.
**Media**
* **Club logo** and **Club images** (up to 8) - shown on the public booking and marketplace pages.
**Features**
* **Sports** - the activities offered at this club. These feed area types, class setup, and marketplace discovery.
* **Amenities** - club-wide services such as showers, parking, or a cafe. Amenities describe the whole location, distinct from per-area [attributes](/settings/areas) that describe an individual court or room.
**Operating hours**
* Per-day open and close times. Areas can fall back to these when they do not set their own hours.
Editing uses a floating save bar: change any field and **Save** appears at the bottom. Nothing is auto-saved.
### Club extras
* **Daily summary** - toggle **Send to admins** and pick a send time to email a per-club daily summary.
* **Google Business Profile** - connect a club to import Google reviews, ratings, and business data. See [Google Business](/settings/integrations/google-business).
* **OpenStreetMap** - connect to enrich the club with address, sports, amenities, opening hours, and contact details.
* **Visibility** - a club can be Public or Private, controlling whether its public page is reachable.
* **Booking labels** - your own names for the booking cards members choose from. See [Booking labels](#booking-labels).
### Booking labels
When members book, they first pick what they want to book: time with a coach, a place in a class, or a court or room. Each choice is a card, titled **Private training**, **Group activities**, and **Areas** by default. Booking labels let each gym give those cards its own names, so members read the words your gym actually uses.
* **A tennis academy** renames **Private training** to "Lessons", because that is what its coaches sell.
* **A boutique studio** renames **Group activities** to "Classes".
* **A padel venue** whose courts have no area type renames **Areas** to "Courts".
To set them, open **Settings** > **Clubs** > **Clubs**, open the club, and expand **Booking labels** in the right-hand column, below **Google Business Profile** and **Daily summary**. The section starts collapsed because most gyms never change it. Type a name, then click **Save** on the save bar.
| Field | Default card title | Where members see it |
| - | - | - |
| **Name for private training** | Private training | The card for booking a coach's time |
| **Name for group activities** | Group activities | The card that lists your classes |
| **Name for areas** | Areas | The card for areas that have no area type of their own, and the single card shown when all areas are shown together |
How the labels behave:
* **Only the title changes.** The line under each card is always set by 1Club: a count such as "4 available", or, under group activities, the names of your class types.
* **A named area type keeps its own name.** A card for an area type such as "Padel court" shows the area type's name, not your areas label. To change that card, rename the area type, which renames it everywhere it appears. See [Areas](/settings/areas).
* **Each club has its own labels.** In an organization with several clubs, set each club separately, so two locations can sell different things under the same card.
* **Up to 30 characters, on one line.** Each translation has the same limit.
* **Leave a field blank to use the default.** The default titles are already translated into every language 1Club supports. Clearing a label also removes its translations.
* **Changes are live as soon as you save**, on your website, the member portal, the mobile app, and 1club.me.
To translate a label, click the **Translations** icon beside its field. Use **Auto Translate** to fill every language your organization offers, or type each one yourself. A member reading in a language with no translation sees the label as you wrote it.
AI assistants connected through [MCP](/mcp/tools) can set the same labels and their translations with `update_club_booking_labels`, which needs the `organization:write` scope (the same one that sets the languages your organization offers). Integrations can call `PATCH /v1/platform/clubs/{slug}` on the [Platform API](/api-reference/introduction).
### Switching club and "All clubs"
When an organization has more than one club staff are running, the club is the first crumb of the breadcrumb at the top of each page (for example **All clubs** > **Sales** > **Finance** > **Invoices**). Click it to switch. Switching club re-scopes club-specific views (bookings, schedule, areas, and analytics) to the selected location. A **Hidden** club stays in the list with a badge, because staff still run it.
Users with organization-wide operations access (admins by default) can also select **All clubs** to see aggregated data across every club at once - useful for comparing locations in analytics. Other roles work within a single club. Your last selection is remembered per organization, so switching orgs and back returns you to where you were.
### Deleting a club
Deleting a club is available from the club detail page. A club that still has associated data cannot be deleted - the admin blocks it with "Cannot delete club: it has associated data. Remove or reassign it first." Prefer setting a club to **Closed** when you want to stop activity at a location while keeping its history, or **Hidden** when you only want members to stop seeing it.
## Why the split matters
* **Permissions and access** - Roles (Admin, Manager, Front desk, Instructor, Member) are assigned per organization. When you invite a user you place them in an organization with a role; they see only that organization's data when it is selected. Permission scope (`organization` includes `club` includes `own`) then decides whether they see all clubs, one club, or only their own records. See [Roles and permissions](/reference/roles-permissions).
* **Billing and branding** - Stripe, plan, branding, member portal, and marketplace listing are configured at the organization level, so every club under the org shares the same billing account and brand.
* **Reporting** - Analytics can be broken down by club so you can compare locations, and admins can flip to "All clubs" for the org-wide picture. See [Clubs analytics](/analytics/clubs).
* **Operations** - Bookings, areas, classes, and check-ins are club-scoped, so front-desk staff always act in the context of one location. Members are not: member permissions are always organization-scoped, so every location's desk sees the same member list.
## Worked examples
### Single-location padel club
You run one padel venue. You keep a single organization with one club. Members never see the word "organization" - to them your brand and your one location are the same thing. You set opening hours and sports on the club, configure Stripe once at the org level, and never touch the club switcher because there is only one active club.
### Multi-location fitness brand
You operate three gyms under one brand. You create one organization and three clubs (for example "Central", "Riverside", and "Harbor"). Shared plans, branding, and Stripe live at the org level, so a membership sold at one gym is recognized across the brand. Front-desk staff at each location get the Front desk role scoped to their club, while regional managers use "All clubs" to compare performance. Each gym sets its own address and operating hours.
### Martial arts school plus affiliate gym
You own two legally separate businesses - a jiu-jitsu academy and a strength gym - each with its own Stripe account and member base. You keep them as two organizations under one login. In the morning you work in the academy org; a click on the organization switcher moves you to the gym org, where your role and currency may differ. Nothing crosses between them.
### Yoga studio piloting a second room
You run a yoga studio and open a second reformer Pilates space nearby. You add it as a second club rather than a second organization so the two share memberships, billing, and branding. During the soft-launch you set the new club to **Hidden** so it stays off the website and member portal while staff set it up, then flip it to **Active** on opening day.
## Tips & best practices
* **One org per brand and billing account.** Use separate organizations only when you need genuinely separate billing, branding, or member bases. Otherwise add clubs, which share plans and Stripe.
* **Add clubs, do not clone orgs, for new locations.** Locations that share memberships and billing belong in the same organization as additional clubs.
* **Set the time zone and currency at the org level early.** They drive dates and money everywhere; changing them later shifts how history is reported.
* **Prefer Closed over Delete for clubs.** A club with any associated data cannot be deleted anyway. Closed removes it from the switcher and every club picker while preserving its history.
* **Assign club managers** so contact-form messages route to the right people instead of falling back to org-level staff.
* **Confirm the active org and club before you act.** Most "missing data" reports are really "wrong org or club selected." The organization menu and the breadcrumb show where you are.
## Troubleshooting
**Issue**: A member, booking, or invoice you expect is missing.
**Solution**: Confirm you are in the right organization. Data is scoped by `organizationId`, so records created in another org are invisible here. Use the organization switcher in the organization menu, then re-check.
**Issue**: A club's bookings or schedule look empty.
**Solution**: Check the club in the breadcrumb. You may be viewing a different club, or an admin "All clubs" view that spreads the data across locations. Select the specific club.
**Issue**: A club is missing from the switcher or from members' booking choices.
**Solution**: Members only see **Active** clubs, and a **Closed** club leaves the staff switcher too. Open **Settings** > **Clubs** > **Clubs** and check the club's status.
**Issue**: You can't delete a club.
**Solution**: A club with associated data (bookings, areas, memberships, and so on) is protected from deletion. Reassign or remove that data first, or set the club to **Closed** instead of deleting it.
**Issue**: A page or menu item disappeared after switching organization.
**Solution**: Roles are per organization. Your role in the org you switched into may not grant that module. Check your role for that org under **Settings** > **Organization** > **Users** (an Admin can confirm it). See [Roles and permissions](/reference/roles-permissions).
**Issue**: The wrong currency or time zone shows across the admin.
**Solution**: Those are organization settings, not per-club. Update them under **Settings** > **Organization** > **Details**.
## Related
* [Organization settings](/settings/organization) - Edit org identity, time zone, currency, and locales.
* [Settings overview](/settings/overview) - Where organization and club settings live.
* [Roles and permissions](/reference/roles-permissions) - How roles and permission scope decide what each user sees.
* [Areas](/settings/areas) - Bookable spaces within a club.
* [Clubs analytics](/analytics/clubs) - Compare performance across locations.
* [Signing in](/reference/sign-in) - How one login resolves across organizations.
# Repeating schedules
Source: https://docs.1club.ai/reference/repeating-schedules
How a repeating class, booking, or instructor rotation is created - the three end conditions, why every occurrence exists as its own row, and the 520-occurrence limit.
Classes, area and instructor bookings, and instructor rotations all repeat the same way. Whoever sets up the schedule picks a pattern (daily, weekly by weekday, or monthly) and an end condition, and 1club creates the individual occurrences. Every occurrence is a real row: a repeating class is not a rule evaluated when someone looks at the calendar - it is N separate classes, each with its own bookings, attendance, price, and check-ins.
This page explains what gets created when, because the answer decides what your schedule shows for dates months ahead.
## When this matters
* **Holding a court for a season** - an end date puts every occurrence on the calendar the moment you save, so the court shows as occupied months ahead and conflicts surface while you are still planning.
* **Running a standing class with no end** - a series set to **Never** keeps only about eight weeks on the schedule and extends itself every night.
* **Planning a long series** - one series can cover up to 520 occurrences, about two years of a weekly slot or a year of a twice-weekly one.
## The three end conditions
| Ends | What you set | What gets created |
| :- | :- | :- |
| **Until date** | A calendar date | Every occurrence from the start through that date, immediately |
| **After N times** | A number of repeats | Exactly N occurrences, immediately |
| **Never** | Nothing | A rolling window of roughly the next eight weeks, extended automatically each night |
### Until date and After N times
These are the ones to reach for when you know how long something runs: a term of classes, a court held for a season, a coach's contract through June.
Both are created in full when you save. If you book a court every Tuesday until the end of the season, all of those Tuesdays show as occupied straight away, so the calendar answers "is this court free in four months?" correctly and conflicts surface while you are still planning.
A series that ends on a date or after a set number of repeats is complete once
you save it. Nothing appears later and nothing needs to catch up.
### Never
Use **Never** for something with no planned end - a standing weekly class you intend to run indefinitely, or a court blocked for a members' social every week.
Because there is no end to create up to, 1club keeps roughly the next eight weeks materialized and extends the series automatically every night. The schedule therefore shows about two months of an endless series at any time, and the horizon moves forward a day each day.
If you need to see further ahead than that, use **Until date** with a date far enough out instead. The trade is that the series then genuinely ends on that date rather than running forever.
## The 520-occurrence limit
One series can hold at most 520 occurrences. In practice:
| Pattern | How far 520 reaches |
| :- | :- |
| Weekly | About two years |
| Twice a week | About a year |
| Weekdays only | About two years |
| Daily | About seventeen months |
If the end date or repeat count you choose would exceed the limit, 1club refuses to create the series and tells you so, rather than quietly creating part of it. Choose an earlier end date, a lower repeat count, a less frequent pattern, or **Never**.
The limit exists because occurrences are real rows. A daily class "until 2040"
would be over five thousand classes, each with its own bookings and
attendance, created inside one request.
## How far ahead a booking may reach
A repeating **booking** also has to fit the **Max. ahead** window on the
[booking policy](/settings/booking-policies) that matches it - and because a bounded
series is created in full, the window is measured against its **last** occurrence, not its
first. A weekly court repeating for a year is refused at a gym that only lets members book
14 days out, even though the first session is next week.
**Max. ahead** binds members. Staff creating the series from the admin are not
held to it: the series is created in full, and the confirmation says it reaches further
ahead than members can book. A series set to **Never** is exempt, since it has no last
occurrence and is written a rolling eight weeks at a time.
The weekly series behind a [season pass](/schedule/season-passes) is also exempt, and no
override is needed. **Max. ahead** exists to stop one member helping themselves to scarce
near-term inventory ahead of everyone else. A season pass is the opposite: the gym sold
those hours deliberately, months in advance, and the member paid for every week. So a venue
can keep a two-week horizon for casual bookings and still sell a six-month season.
Classes are not affected: **Max. ahead** governs what members may book, not what you may
schedule.
## Editing and deleting a series
Every edit and delete on a repeating item asks for a scope:
* **This instance** - only the occurrence you opened. Move one week's class to a different court without touching the other weeks.
* **All future** - this occurrence and every one after it. Past occurrences are never changed, so your history and attendance stay intact.
Changing the pattern or the end condition with **All future** replaces the upcoming occurrences. Shortening a series removes the occurrences past the new end and cancels any member bookings on them, with the refund and notification the matching [booking policy](/settings/booking-policies) calls for. Lengthening one adds the new occurrences immediately, up to the same 520 limit.
## Where repeating schedules appear
Repeat a class daily, weekly by weekday, or monthly. Each occurrence carries
its own capacity, roster, and check-ins.
Repeat an area or instructor booking. Each occurrence is priced and checked
for availability on its own date.
An assignment with an **Effective until** date generates the whole roster;
an **Ongoing** one uses the rolling eight-week window.
## Tips & best practices
* **Set a real end date for anything seasonal.** A term, a season, or a contract has a last day. Using **Until date** puts the whole thing on the calendar at once, which is what makes court occupancy and conflict checks trustworthy months ahead.
* **Reserve Never for genuinely open-ended things.** It is the only option that shows a short horizon, so a season entered as **Never** looks two months long on the schedule.
* **Check the last occurrence after you save.** On a class, the **Information** card shows the recurrence summary; on a booking, the **Bookings** tab lists every sibling occurrence. Both are the fastest confirmation that the series is the length you meant.
* **Move one week with This instance.** A one-off court change or time shift does not need to touch the series.
## Troubleshooting
**Issue**: Only the next few weeks of my repeating class or booking appear on the schedule.
**Solution**: The series is set to **Never**, which keeps a rolling eight-week horizon. Edit it, choose **All future**, and set **Until date** to the real end of the season or term. Every occurrence up to that date is created when you save.
**Issue**: I get an error saying the series is too long.
**Solution**: The pattern would exceed 520 occurrences. Pick an earlier end date, a lower repeat count, or a less frequent pattern. For something with no real end, use **Never** instead.
**Issue**: An occurrence in the middle of my series is missing.
**Solution**: For a repeating booking, an occurrence is skipped when it cannot be placed - the area is blocked or already taken at that time, or the class it attends is full or does not run that day. Check [Area blocks](/schedule/area-blocks) and the other bookings on that resource for the missing date, then add the single occurrence by hand.
**Issue**: I changed a rotation and an instructor's shift did not update.
**Solution**: A shift you edited by hand is a manual override, and rotation regeneration deliberately leaves it alone. Use **Cancel shift** to drop it, or edit it again to the value you want.
# Roles and permissions
Source: https://docs.1club.ai/reference/roles-permissions
How 1Club authorizes admin users - the five built-in roles, the permission model (module, action, scope), what every permission allows, and how to change a role's permissions in Settings > Organization > Permissions.
1Club decides what an admin user can do from **permissions**, not from their job title. Each admin user holds one **role**, and a role is a default bundle of permissions. Every operation in the admin is gated on a specific permission, so two people with different roles can do the same thing whenever they hold the same permission.
This page covers the five roles, the permission model behind them, exactly what each permission allows, and how to change a role's permissions in **Settings** > **Organization** > **Permissions**.
## Overview
* **Five built-in roles** - Admin, Manager, Front Desk, Instructor, and Member. Every admin user holds exactly one.
* **Permissions decide access** - a permission is written `module.action.scope`, and a role is just the set of permissions its holders start with.
* **Four actions and a shortcut** - View, Create, Edit, and Delete, plus **Full access**, which grants all of them at once.
* **Three scopes** - Organization, Club, and Own, from widest to narrowest.
* **Customizable per role** - an admin edits which permissions each role grants in **Settings** > **Organization** > **Permissions**. You cannot create a sixth named role.
* **Owner and super admin** - two capabilities outside the role list that bypass permission checks by design.
## Roles
There are five roles and every admin user holds exactly one. The **Member** role has no admin access at all; members use the member portal and public apps.
| Role | Typical use | Default access |
| - | - | - |
| **Admin** | Owner or main administrator | Full access to every module at organization scope, including settings and the permission editor. Cannot be customized. |
| **Manager** | Gym or department manager | Runs the gym: members and memberships, the timetable, bookings, check-ins, attendance, assessments, marketing, analytics, inventory, rentals, cash, and the till. Manages staff organization-wide. Views and takes payments, voids charges, and refunds payments, but does not configure billing. No settings access. |
| **Front Desk** | Receptionist or front-of-house staff | Works the desk: looks members up, signs them up to a plan, books them in, checks them in, scores their training, runs the till, lends gear, handles the inbox. Reads the timetable and catalog without editing them. No staff, marketing, or settings access. |
| **Instructor** | Coach or teacher | Own world only: reads their own timetable, runs the bookings and attendance on it, assesses the members of the sessions they teach, reads their own earnings and stats, sends their own messages, manages their own profile. |
| **Member** | End member | No admin access. Uses the member portal and public apps. |
You may see **staff** on some screens and reports. That is a label for any organization user who can reach the admin (Admin, Manager, Front Desk, or Instructor), not a sixth stored role.
### Owner
Within the Admin role, an organization can mark one or more users as **owner**. Owners bypass permission checks entirely, and only an owner can delete the organization. Owner is a flag on the user's membership, not a role. See [Users](/settings/admin-users).
### Super admin
Super admin is a 1Club platform capability used by the 1Club team for support and provisioning. It bypasses organization permission checks and is not something you assign to your own staff.
## How a permission is built
Every permission has three parts: `module.action.scope`. For example `booking.read.club` is "view bookings, within a single club".
* **Module** - the area of the product, such as Members, Bookings, or Billing.
* **Action** - what you may do there: View, Create, Edit, Delete, or Full access.
* **Scope** - how much of the organization it reaches: Organization, Club, or Own.
An operation always requires the **narrowest** permission that actually fits it. A screen that only lists things requires View. A button that changes something requires Create, Edit, or Delete.
### Actions
| Action | Permission key | What it means |
| - | - | - |
| **View** | `read` | See the data. Every list, detail page, and picker in the module. |
| **Create** | `create` | Add new records. |
| **Edit** | `update` | Change records that already exist. |
| **Delete** | `delete` | Remove records, and in several modules cancel them (see the module reference below). |
| **Full access** | - | Everything the module offers. Granting Full access grants View, Create, Edit, and Delete together, plus any configuration the module keeps behind it. |
Not every module offers all four actions. The editor only shows an action when granting it changes something real, so Marketing offers View and Full access while Members offers all four. The [module reference](#what-each-permission-allows) lists what each one actually has.
**Full access is a shortcut, not a fifth kind of access.** On screen it is a single checkbox labelled **Full access**. Checking it ticks and locks the other actions in that module, because it already grants them, and it also carries whatever configuration that module keeps behind its individual actions.
### Scopes
| Scope | Reaches |
| - | - |
| **Organization** | Everything in the organization, across every club. |
| **Club** | Records in a single club. |
| **Own** | Only records the user owns or is assigned to. |
**Club** here is 1Club's own word for one of your locations, which may be a gym, a studio, a court complex, or any other venue. It is the name of the scope, not a facility type. See [Organizations and clubs](/reference/organization-clubs).
A wider scope automatically covers the narrower ones: Organization covers Club, and Club covers Own. So a user holding "View bookings (Organization)" satisfies any check for "View bookings (Club)".
Scope is why an Instructor stays in their own world. Their permissions are Own-scoped, so they run their own classes, bookings, and check-ins but cannot reach organization-wide surfaces like billing, staff, or settings, even where the action matches.
### How the hierarchy plays out
Both hierarchies apply at the same time, so one grant can satisfy several checks:
* Full access covers View, Create, Edit, and Delete on the same module.
* Organization covers Club, and Club covers Own.
A Manager holding **Bookings: Full access (Club)** therefore passes a check for "View bookings (Own)" without holding it literally. That is the point of the model: you grant the least a person needs, and broader holders pass automatically.
Widening the scope of a Full access grant can hand over more than the editor shows. A few sensitive operations are deliberately pinned to **Organization** scope so that, by default, only an admin or owner reaches them - deleting the website, deleting a payment record, undoing a check-in that has already been recorded. If you grant a role **Full access (Organization)** on Marketing, Billing, or Check-ins, it satisfies those checks too, with no separate checkbox saying so. Grant Organization scope deliberately.
## What each permission allows
The modules below are in the order they appear in **Settings** > **Organization** > **Permissions**. Each one lists the actions it offers and what they unlock in the product.
### Profile
Your own account details: name, photo, contact details, password, and language. Own scope only.
| Action | Allows |
| - | - |
| **View** | Open your own profile. |
| **Full access** | Change your own profile. |
Every staff role holds this by default. It never gives access to anyone else's profile - that is the Members module.
### Notifications
Your own notification preferences: which emails, push messages, and SMS you personally receive. Own scope only.
| Action | Allows |
| - | - |
| **View** | See your own notification settings. |
| **Full access** | Change your own notification settings. |
Setting notification defaults for a whole role is a different thing. **Settings** > **Organization** > **Notifications** is organization configuration and needs settings access, so it is admin and owner only. Editing another member's notification settings rides the same settings access, so it is admin and owner only too.
### Members
The people and their records: contacts, members, tags, custom fields, and billing accounts.
A member belongs to the organization, not to one of its gyms - the membership, booking, check-in, and order each carry their own club. So Members permissions are always **Organization** scope, and the editor offers no Club scope for them. Front desk at one location finds a member who registered at another instead of creating a duplicate.
| Action | Allows |
| - | - |
| **View** | The member list, a member's profile, and every member picker in the admin. |
| **Create** | Add a member or contact. Also creates billing accounts, tag definitions, custom-field definitions, and recorded matches. |
| **Edit** | Amend an existing record: details, notes, household links, PIN, tags, email preferences, invitations, member type, progression, and archiving or unarchiving. |
| **Delete** | Delete a member or a billing account, purge the archive, delete a tag or custom-field definition, **and merge two profiles**. |
| **Full access** | All four. |
Two things are worth knowing here:
* **Merging is a Delete.** A merge absorbs one profile into another and removes it, so a role with Edit can correct a duplicate but cannot resolve it by merging.
* **Archiving is an Edit.** Archiving is reversible, so it does not need Delete.
* **Viewing a member does not include their memberships.** A role with member View but no membership View sees the profile - name, contact details, tags, bookings - with the memberships section empty. The expiring-memberships block on Home disappears the same way.
### Memberships
The plans and passes a member holds: subscriptions, punch cards, and their remaining allowance.
| Action | Allows |
| - | - |
| **View** | The memberships list and detail, plus the active, expiring, and stats views. |
| **Create** | Sign a member up from their profile or from the memberships list. This charges the payment, honours the plan's upfront-payment rule, and records the charge. |
| **Edit** | Extend, renew, pause, resume, top up remaining visits, apply or remove a promotion, and reconcile. Also allows selling on credit by overriding the upfront-payment requirement. |
| **Delete** | Delete a membership, **and cancel one**. |
| **Full access** | All four. |
* **Cancelling is a Delete.** It ends the membership and triggers its refunds, so a role with Edit can extend a membership but cannot call it off.
* **Selling a pass as part of a visit is not this module.** The "Add plan or pass" tab inside the booking and check-in dialogs, and selling at the till, ride along with the booking or check-in itself. Whoever may check a member in may also sell them the thing that gets them in.
### Member directory
A deliberately limited roster: names and membership status, with no contact details. It exists so an instructor can see who they teach without being handed the member database.
| Action | Allows |
| - | - |
| **View** | The limited directory. At **Own** scope it is the attendees of their classes and the bookings assigned to them. |
| **Full access** | The same reads. There is nothing to write here. |
### Messages
The inbox, message threads, templates, and shared email inboxes.
| Action | Allows |
| - | - |
| **View** | Read messages and threads. At **Own** scope, only their own. |
| **Create** | Compose, edit a draft, delete a draft, and send. |
| **Full access** | Also the shared surfaces: create, verify, and delete organization email inboxes, and create, edit, and delete message templates. |
### Classes
The timetable: classes, their instructors, capacity, and schedule.
| Action | Allows |
| - | - |
| **View** | The class list, class detail, and booking counts. |
| **Create** | Create a class, including duplicating one on the calendar. |
| **Edit** | Change an existing class: rename, reschedule, change instructor or capacity. |
| **Delete** | Delete a class, delete in bulk, **and cancel a class**. |
| **Full access** | All four. |
* **Cancelling is a Delete.** Cancelling a class cancels every member booking on it and triggers their refunds, so a role with Edit can reschedule and rename a class but cannot call it off.
* **Own scope for a class write means sole instructor.** An Own-scoped user may only change a class they teach alone, before and after the change, and a class they create can name only themselves. **Own** on View is looser: they see any class they teach, including one co-taught with a colleague, while the write actions disappear on it.
* **Viewing a class does not include its attendees.** The roster hanging off a class is Bookings: View.
### Bookings
Bookings and waitlists, for classes and for area or court reservations.
| Action | Allows |
| - | - |
| **View** | The booking list and detail, plus the attendee roster on a class. |
| **Create** | Make a booking, and add someone to a waitlist. |
| **Edit** | Edit or reschedule a booking, confirm it, add or remove a participant, and process a waitlist. |
| **Delete** | Cancel a booking, and remove someone from a waitlist. |
| **Full access** | All four, plus two overrides: booking into a blocked area or an already-taken slot (the latter with a note), and waiving the cancellation policy to issue a full refund. |
* **Delete means cancel.** There is no hard delete for a booking. Cancelling carries refunds, notifications, and a knock-on to any order it belongs to.
* **Own scope is not "bookings I made".** It is bookings assigned to the user, plus every booking on a class they teach. Unlike class writes there is no sole-instructor rule, so a co-taught class's bookings belong to all of its instructors. Editing a whole recurring series additionally requires that no later occurrence belongs to another instructor.
* **Cancelling a booking that is already checked in** needs the Check-ins override described below, on top of Bookings: Delete.
* Bookings also get cancelled by neighbouring operations under their own permissions: cancelling a class, cancelling an order, cancelling an event, and resolving an instructor time-off conflict.
### Check-ins
Recording and undoing attendance.
| Action | Allows |
| - | - |
| **View** | The attendance log and check-in detail. |
| **Create** | Record a check-in, including outside the check-in window, which binds only members checking themselves in. |
| **Edit** | Change an existing check-in, in practice converting the entrance method used for a visit. |
| **Delete** | Undo a check-in. |
| **Full access** | All four. At **Organization** scope it also recovers a no-show. |
Three overrides live behind **Full access (Organization)** specifically, and they are the reason to think before widening this module's scope:
* **Recovering a no-show.** Checking in a booking that was already marked as a no-show, which can undo the no-show fee.
* **Cancelling a booking that has already been checked in.** This voids the attendance record and zeroes the instructor's pay for it.
* **Rewriting the entrance method after the visit was recorded and paid for.**
By default those three land only on Admin, owner, and 1Club support, because Manager holds Check-ins at Club scope. You can delegate any of them to another role by granting it at Organization scope. You cannot take them away from an Admin.
### Assessments
Instructors' score cards on members after a training. See [Assessments](/members/assessments).
| Action | Allows |
| - | - |
| **View** | The cards on a session's roster and, at **Club** scope, a member's whole history on their profile. |
| **Create** | Score an attendee of a session. At **Club** scope, also add a card for a training that was never in 1Club. |
| **Edit** | Correct a card's scores and note. |
| **Delete** | Remove a card the member may already have received. **Club** scope only. |
| **Full access** | All four. |
Two things about scope are particular to this module:
* **Own means "a session I teach" for View and Create**: the user's own class, or a booking assigned to them. A member's whole history spans sessions an Own-scoped user does not teach, so the profile view needs **Club**. A card with no session has nothing to own, so adding one needs **Club** too.
* **Own means "a card I wrote" for Edit**, whatever the session. The desk, which scores club-wide, can still correct its own typo without being able to rewrite an instructor's card, and an instructor can correct their own.
Defaults: Manager holds all four at **Club**; Front Desk holds View and Create at **Club** and Edit at **Own**; Instructor holds View, Create and Edit at **Own**. Templates (the criteria and scale) are organization configuration and live under **Settings**, not here: any assessor can read them, and only a holder of Settings edits them.
### Staff
The instructor roster and admin access for your team.
| Action | Allows |
| - | - |
| **View** | The instructor list and detail, pay-rate policies, and time off. |
| **Full access** | Create, edit, delete, merge, and reorder instructors; manage pay-rate policies; manage time off and resolve its booking conflicts; **and manage team members and admin access** - invite, edit, change role, reset a password, and remove a user. |
Organization scope only - there is no Club-scoped version of this module.
Staff management is bounded by a role hierarchy regardless of the permission. See [Managing who has which role](#managing-who-has-which-role).
### Billing
Charges, invoices, payments, orders, plans, and revenue configuration.
| Action | Allows |
| - | - |
| **View** | Charges, invoices, payments, orders, plans, and packages. At **Own** scope it is the signed-in user's own earnings ledger, which is how an instructor sees their pay. |
| **Create** | Take a payment, create and amend an order, close or cancel an order, and create and send an invoice. |
| **Delete** | Void a charge, refund a charge or payment, and write off the outstanding balance when cancelling a season pass. At **Organization** scope, also delete a payment record. |
| **Full access** | Everything above plus billing configuration: tax rates, revenue accounts, payment methods, bill runs, bulk charging of saved cards, and wallet refunds. |
**Billing's configuration sits at Organization scope.** Full access at **Club** scope therefore behaves as View, Create, and Delete (voids and refunds) - it does not unlock tax rates, revenue accounts, payment methods, bulk charging, or deleting a payment record. Grant **Full access (Organization)** for those. Billing entities are organization settings, not part of this module.
Billing includes selling. A role with Billing: Create can ring up an order whether or not it also holds Point of sale, because the register and the **Sales** > **Orders** screen drive the same operations. See the Point of sale note below.
### Point of sale
Running the till. This module is layered on top of Billing rather than carved out of it, which makes it useful subtractively: you can grant "can work the register" without granting "can see the books".
| Action | Allows |
| - | - |
| **View** | Load the register: the sellable catalog and prices, the totals preview, drafts and open tabs, order and receipt lookup, the bookable timetable, and the customer picker. |
| **Create** | Transact: create, amend, execute, close, and cancel an order; record the tender; add a walk-in customer; and record a check-in. |
| **Full access** | Today this is exactly View plus Create. |
Four things to know before granting it:
* **Point of sale: View conveys member contact details and the organization's order history.** The register cannot attach a sale to anyone without the customer picker, so this is not a prices-only grant.
* **A register-only cashier still cannot do back office.** They cannot settle a customer's outstanding invoice, and they cannot reverse a sale that has already been fulfilled - only discard a draft cart.
* **To build a register-only cashier, grant Point of sale and revoke Billing.** Unchecking Point of sale on its own does not stop a role that still holds Billing: Create from selling.
* **A Point of sale grant widens timetable and roster reads to Club scope.** An Own-scoped instructor who is also given Point of sale will see the whole club's timetable and rosters. That follows from the grant, and it is a reason not to hand the till to instructors casually.
If your organization has the point of sale switched off in settings, the register refuses new sales regardless of permissions, while everything already on the books stays settleable.
### Marketing
The leads pipeline, promotions (under **Sales** > **Promotions**), automations, content, media, reviews, and the website builder.
| Action | Allows |
| - | - |
| **View** | See all of the above. |
| **Full access** | Create and change all of it, including publishing the website, rolling a release back, taking the site offline, and restoring an archived site. |
**Full access (Organization)** on Marketing also allows **deleting the website**, even though no Delete checkbox appears. Deletion is pinned to Organization scope so that a Manager's Club-scoped grant cannot reach it. Deleting is reversible for 30 days; after that the archive is purged.
### Analytics
Reports and insights.
| Action | Allows |
| - | - |
| **View** | At **Club** scope: every gym-wide report in **Analytics** (clubs, members, check-ins, bookings, classes, instructors, revenue, web traffic, and marketing), plus global search. At **Own** scope: the **My performance** bookings and classes reports, for users linked to an instructor record. The **My performance** earnings reports need Billing: **View (Own)** instead. |
| **Full access** | The same reads. Analytics has nothing to write. |
### Products
The product catalog: products, brands, and product categories.
| Action | Allows |
| - | - |
| **View** | Products, categories, and brands. |
| **Create** | Add a product, category, or brand. |
| **Edit** | Change any of the three, including reordering them. |
| **Delete** | Delete any of the three, including bulk delete. |
| **Full access** | All four. |
* **Stock is not part of this module.** Stock levels, ingredients, recipes, and deliveries sit on the same screens but belong to Inventory, so a catalog-only editor sees the product grid without the stock panel or recipe editor.
* **Any single product permission reveals Sales** > **Products.** The write actions do not imply View, so a role granted only Create sees the section with Products as its only tab.
* **A write action is useless without View.** Every catalog screen loads through View, so the editor ticks View for you when you check a write action.
* The register reads the catalog through Point of sale: View, so a cashier prices a sale without holding a product permission.
### Inventory
Stock, ingredients, recipes, suppliers, and deliveries.
| Action | Allows |
| - | - |
| **View** | Stock levels, stock movements, ingredients, recipes, suppliers, delivery receipts, and product availability. |
| **Full access** | Receive and adjust stock, manage ingredients and suppliers, edit recipes, and create, scan, and edit delivery receipts. |
### Rentals
Lending gear out for a session and getting it back.
| Action | Allows |
| - | - |
| **View** | The rentals list, the counts, and what a given member still has out. |
| **Create** | Check gear out. |
| **Edit** | Take gear back in. |
| **Delete** | Write off gear that is not coming back. |
| **Full access** | All four. |
This split is deliberate: the desk can lend and take back without being able to write off missing gear.
### Cash
The cash drawer, cash movements, and expenses.
| Action | Allows |
| - | - |
| **View** | The cash ledger, the drawer, the overview, and expense categories. |
| **Create** | Record a cash movement or expense, scan an expense receipt, and count the drawer. |
| **Full access** | Also reverse a ledger entry and manage expense categories. |
### Permissions
Administering role permissions - the editor described below.
This module is **locked to Admin**. It shows in the editor with an **Admin only** badge, an admin always holds it, and no other role can be granted it. That is what stops a role from widening its own access.
### Modules that are not in the editor
Three modules are enforced by 1Club but are not per-role customizable. They always resolve from the role defaults.
| Module | Covers | Why it is not customizable |
| - | - | - |
| **Operations** | Areas, opening hours, pricing sheets, events, the instructor roster, and time-off reads. | Not something organizations customize per role. Classes, Bookings, and Check-ins were split out of it precisely so they could be. |
| **Settings** | Organization settings and configuration: users, notification policies, integrations, API tokens, domains, and locales. | Too broad to hand out a piece at a time. In practice this is admin and owner only. |
| **Website** | Retired. The website builder is governed by Marketing. | Deprecated. |
## Default permissions by role
This is what each role starts with out of the box, before any customization. Admin holds Full access (Organization) on everything and is not shown. Member holds nothing.
| Module | Manager | Front Desk | Instructor |
| - | - | - | - |
| Profile | Full access (Own) | Full access (Own) | Full access (Own) |
| Notifications | Full access (Own) | Full access (Own) | Full access (Own) |
| Members | View, Create, Edit, Delete (Org) | View (Org) | - |
| Memberships | View, Create, Edit, Delete (Club) | View, Create (Club) | - |
| Member directory | - | - | View (Own) |
| Messages | Full access (Club) | Full access (Club) | View, Create (Own) |
| Classes | Full access (Club) | View (Club) | View (Own) |
| Bookings | Full access (Club) | View, Create, Edit (Club) | Full access (Own) |
| Check-ins | Full access (Club) | View, Create (Club) | Full access (Own) |
| Assessments | View, Create, Edit, Delete (Club) | View, Create (Club), Edit (Own) | View, Create, Edit (Own) |
| Staff | Full access (Org) | - | - |
| Billing | View, Create, Delete (Club) | View, Create (Club) | View (Own) |
| Point of sale | View, Create (Club) | View, Create (Club) | - |
| Marketing | Full access (Club) | - | - |
| Analytics | Full access (Club) | - | View (Own) |
| Products | View (Club) | View (Club) | - |
| Inventory | Full access (Club) | View (Club) | - |
| Rentals | Full access (Club) | View, Create, Edit (Club) | - |
| Cash | Full access (Club) | View, Create (Club) | - |
| Permissions | Admin only | Admin only | Admin only |
Worth noticing in that table:
* **Front Desk can book, amend, and check in, but not cancel or undo.** It holds View, Create, and Edit on Bookings and View and Create on Check-ins, but no Delete. Grant Delete if your desk needs to cancel bookings or undo check-ins.
* **Front Desk takes money but does not hand it back.** Voids and refunds are Billing: **Delete**, which Manager holds and Front Desk does not.
* **Front Desk has no analytics by default.** Instructors get **My performance** through their own-scope grants.
* **Front Desk reads members but does not amend them**, and signs a member up to a plan without being able to extend or delete that membership.
* **Instructors read their timetable but do not write it.** A class is scheduled *for* an instructor, so Classes is View only, while the bookings and attendance on that timetable are theirs to run in full.
* **Manager runs the gym but does not configure it.** Settings is not one of the modules the editor can grant, so organization configuration stays with Admin and owner.
* **Neither desk role maintains the product catalog** - both read it. Catalog writes were admin-only before Products became its own module, and the defaults preserve that.
## The Permissions page
**Settings** > **Organization** > **Permissions** is where an admin changes what each role can do. It is the whole permission model in one screen, and every checkbox on it maps to a real check in the product.
Open it from **Settings**, in the **Organization** section. Only users holding the Permissions module reach it, which in practice means Admin and owner.
### Reading the page
The page is two columns.
**On the left, the five roles.** Each non-admin role carries a badge: **Default** if it still matches the built-in bundle, **Customized** if your organization has changed it. Select a role to load it.
**On the right, that role's permissions**, as one collapsible section per module. A collapsed section still tells you what the role holds there, on the right of its header - for example `View, Create` or `Full access` or **No access** - so you can read the whole role without opening anything.
A search box above the list filters by module name, action name, and the words in a module's description, and it also matches a pasted permission string like `members.delete`. Matches open automatically. Press `Escape` or click the clear icon to reset. The search survives switching roles, which is the point: comparing the same permission between Front Desk and Instructor is the usual reason to search.
Selecting **Admin** shows the whole grid ticked and read-only. The Admin role cannot be customized, by design - it is the role that always has a way back in.
### Changing a permission
Open a module and you get one row per action. Each row is a checkbox and, where more than one scope is meaningful, a scope dropdown.
Select it on the left. Changes apply to that role, and therefore to everyone holding it.
Scroll or search. Open the section.
Tick **View**, **Create**, **Edit**, **Delete**, or **Full access**. A newly checked action starts at **Club** scope where the module offers it, so you never widen a role to the whole organization by accident.
Use the dropdown next to the action. Organization, Club, or Own - only the scopes that mean something for that module and action are listed.
A save bar appears as soon as anything is different. It saves every role you edited, not just the one on screen.
### Rules the editor enforces for you
The editor prevents the combinations that would produce a role that half-works.
* **Checking a write action also checks View.** A role with Edit but not View passes the write check and then fails on every list and detail screen it needs to aim that edit at. Unchecking View clears the write actions that depended on it.
* **Full access ticks and locks the actions it implies.** You cannot uncheck View while Full access is granting it. Uncheck Full access first.
* **Only real permissions are offered.** The action rows and scope options are derived from the checks the product actually performs, so there is no checkbox that quietly does nothing.
* **A role cannot be left with nothing.** Clearing every permission is indistinguishable from reverting the role, so the save is refused with a message pointing you at **Reset to default** instead.
* **The Permissions module cannot be granted to another role.** It renders with an **Admin only** badge and no working checkbox.
### When a change takes effect
Permission changes are not instant. They apply the next time an affected user's session refreshes, within about 15 minutes, or immediately if they sign out and back in. The page says so above the editor. If you are testing a change on your own account, sign out and back in rather than waiting.
### Resetting a role
**Reset to default** on a role deletes your organization's customization for that role and puts it back on the built-in bundle. It is enabled only when the role is actually customized, it asks for confirmation, and it warns you if you have unsaved edits it will also discard. There is no history to restore a customization from afterwards, so treat it as final.
### What the editor cannot do
* **Create a role.** The five names are fixed. Pick the closest one and tailor its permissions.
* **Customize Admin.** Admin always holds everything.
* **Customize Operations or Settings.** Those modules always resolve from the role defaults.
* **Take an override away from Admin.** The organization-scoped check-in overrides, for instance, can be delegated to other roles but never removed from Admin.
## Reading the Role permissions table
There is also a read-only view of the whole matrix, for when you are assigning a role rather than editing one. Open **Settings** > **Organization** > **Users**, invite or edit a user, and click the info icon next to the role picker.
It shows every role against every module, with the actions each role holds and a scope badge such as **(Own)** where the permission is narrower than the organization. It reflects **your organization's** permissions, including any customization, not the generic defaults. Roles with no permissions - Member - are left out, and so is the admin-only Permissions module.
If the table cannot load your organization's real sets it says so and falls back to the built-in defaults rather than showing nothing.
## Managing who has which role
* **Admins and owners** manage every user and can assign any role, including Admin and the owner flag.
* **Managers**, through staff management, can invite, edit, reset the password of, and remove users whose role is **below** Manager - Front Desk, Instructor, and Member - and can promote someone up to Manager. A Manager cannot modify themselves, another Manager, or an Admin, and can never grant the Admin role or account ownership.
* A role is assigned when a user is invited and can be changed later by editing them. Changing a role replaces the old one; the owner flag is separate and is preserved.
This hierarchy holds regardless of permissions, on both the screen and the server. Manage it all under **Settings** > **Organization** > **Users**. See [Users](/settings/admin-users).
## Worked examples
### Hiring a receptionist at a racket-sports venue
Invite them as **Front Desk**. Out of the box they look members up, sign them up to a plan, book courts, check people in, take payment at the desk, lend rackets, and answer the inbox. They cannot change organization settings, see staff pay, or maintain the product catalog.
Two adjustments are common. If your desk amends member records, grant Members: **Edit**. If your desk cancels bookings, grant Bookings: **Delete** - the default deliberately lets them book but not cancel.
### Onboarding a coach at a jiu-jitsu academy
Invite them as **Instructor**. They see their own timetable, run the bookings and attendance on it, see the members they teach as names and membership status, read their own earnings, and send their own messages. They cannot open organization billing or settings.
If you do not want instructors cancelling bookings on their own classes, remove Bookings: **Delete**. If you want them amending their own classes, grant Classes: **Edit (Own)** - it is off by default because a timetable is set *for* an instructor. Avoid granting them Point of sale: it widens their timetable and roster reads to the whole club.
### Promoting a studio lead to run a location
Give a trusted senior staffer **Manager**. They run members and memberships, the timetable, bookings, check-ins, marketing, analytics, inventory, rentals, cash, and the till, and they manage Front Desk and Instructor accounts themselves, so day-to-day team changes no longer need an admin. They take payments but do not configure billing, and they cannot reach organization settings.
### Building a register-only cashier
Start from **Front Desk**, then grant **Point of sale: View and Create** and **revoke Billing entirely**. Revoking Billing is the part that matters - leaving Billing: Create in place keeps them able to sell through the Orders screen, so unchecking Point of sale alone changes nothing. The result is a role that rings up sales but cannot see the books, settle an invoice, or reverse a fulfilled sale.
### Adding a co-owner
Give them **Admin** and mark them as **owner**. Owners bypass every permission check and are the only users who can delete the organization. Reserve this for the people who actually own the business.
## Tips and best practices
* **Assign the lowest role that fits, then add.** Start at Front Desk or Instructor and grant the one permission that is missing, rather than defaulting people to Admin.
* **Grant the narrowest action that does the job.** Prefer View, Create, Edit, or Delete over Full access when only one of them is needed. Full access absorbs anything added to that module later.
* **Be deliberate about Organization scope.** Club scope is the right default for staff. Organization is what unlocks the sensitive overrides described above.
* **Remember that Delete often means cancel.** On classes, bookings, and memberships, Delete is the destructive customer-facing action that triggers refunds and notifications. Withholding it is a real control.
* **Keep owners few.** Owners bypass every check and can delete the organization.
* **Read the role, not the label.** What a user can do is the sum of their permissions after customization, not the name of their role.
* **Check the Customized badge before debugging.** A role that behaves differently from this page's defaults has probably been customized in your organization.
## Troubleshooting
**Issue**: I changed a role's permissions but the user still has the old access.
**Cause**: Permission changes apply when the user's session refreshes.
**Fix**: Ask them to sign out and back in, or wait up to about 15 minutes.
**Issue**: A Front Desk user can look a member up but not edit them.
**Cause**: Front Desk holds Members: View by default, not Edit.
**Fix**: Grant Members: **Edit** (Organization scope, the only one Members offers) in **Settings** > **Organization** > **Permissions**.
**Issue**: A Front Desk user can book a member in but cannot cancel the booking.
**Cause**: Front Desk holds Bookings: View, Create, and Edit, not Delete.
**Fix**: Grant Bookings: **Delete** at Club scope, or route cancellations to a Manager.
**Issue**: I unchecked Point of sale but the role can still sell.
**Cause**: The register and the **Sales** > **Orders** screen drive the same operations, and the role still holds Billing: Create.
**Fix**: Revoke Billing from that role as well. See [Building a register-only cashier](#building-a-register-only-cashier).
**Issue**: I granted a write action and the screen loads empty or errors.
**Cause**: Write actions need the View on the same module to load the records they act on.
**Fix**: The editor ticks View for you when you check a write action. If the role was configured some other way, add View back.
**Issue**: I cannot uncheck View on a module.
**Cause**: Full access is granting it through the hierarchy, so the row is ticked and locked.
**Fix**: Uncheck **Full access** first, then pick the individual actions you want.
**Issue**: My save was refused with a message about keeping at least one permission.
**Cause**: You cleared every permission for the role, which is indistinguishable from reverting it.
**Fix**: Use **Reset to default** if that is what you meant, or leave at least one permission checked.
**Issue**: An Instructor cannot see organization billing or settings.
**Cause**: Instructor permissions are Own-scoped by design.
**Fix**: This is expected. If the person needs that access, make them a Manager rather than widening the Instructor role. Settings still requires Admin.
**Issue**: A Manager cannot change another Manager's or an Admin's role.
**Cause**: Managers only manage roles below Manager, and can never grant Admin or ownership.
**Fix**: Have an Admin or owner make the change.
**Issue**: I want a brand-new role with a custom name.
**Cause**: 1Club has five fixed roles. Custom named roles are not supported.
**Fix**: Pick the closest role and tailor its permissions in **Settings** > **Organization** > **Permissions**.
## Related
* [Users](/settings/admin-users) - Invite and manage organization users and assign roles.
* [Signing in](/reference/sign-in) - How staff authenticate into the admin.
* [Organizations and clubs](/reference/organization-clubs) - How organizations and clubs relate, and what Club scope means.
# Signing in
Source: https://docs.1club.ai/reference/sign-in
Sign in to 1club with Google, Apple, Facebook, or email and password, across the admin dashboard, member portal, marketplace, club website, and mobile app.
1club supports **Sign in with Google**, **Sign in with Apple**, and **Facebook login**, plus classic **email and password**, across all of its apps. Members, staff, and admins can use whichever method they prefer, and the same identity works on the web and in the mobile app.
## When this matters
Because an account is keyed to its email address, no matter which method created it, one person has one account everywhere: the admin dashboard, member portal, marketplace, club website, and mobile app. There is nothing to enable per member, and nothing to set up for Apple's Hide My Email.
Typical situations:
* **Getting new staff into the admin** - you invite a new hire from **Settings** > **Organization** > **Users**, and they sign in with their work Google account, so there is no password to create or share.
* **A member who wants to keep their email private** - a member books through the marketplace with **Sign in with Apple** and **Hide My Email**, and still gets confirmations, receipts, and reminders in their real inbox.
* **A social sign-up that later needs a password** - a member who joined with Facebook needs to sign in on a shared device without Facebook, so an admin sends them a password reset and they can then sign in either way with the same email.
See [Worked examples](#worked-examples) for each of these in more detail.
## Supported sign-in methods
* **Google** - one-tap or "Continue with Google". Uses your existing Google account, so there is no separate password to remember. Google One Tap can sign returning members in automatically.
* **Apple** - "Sign in with Apple". Uses your Apple ID and supports Apple's Hide My Email (private email relay).
* **Facebook** - "Continue with Facebook".
* **Email and password** - a traditional email and password login, with self-service password reset.
No matter which method you choose, an account is identified by its email address. Signing in with Google, Apple, or Facebook for the first time creates a 1club account (or links to an existing one with the same email) automatically. There is nothing to enable per member.
## Where each method works
App
Google
Apple
Facebook
Email & password
Admin dashboard (`admin.1club.ai`)
Yes
Yes
Yes
Yes
Member portal
Yes
Yes
Yes
Yes
Marketplace (`1club.me`)
Yes
Yes
Yes
Yes
Club website
Yes
Yes
Yes
Yes
Mobile app (iOS & Android)
Yes
Yes
Yes
Yes
Sign in with Google, Sign in with Apple, and Facebook login are available to
both staff signing into the admin dashboard and members signing into any
member-facing app.
## How sign-in works
All member-facing apps (portal, marketplace, club website, and mobile) sign you in through a single secure sign-in page. Choose a provider or enter your email, authenticate once, and you are returned to the app you started from, already signed in. The mobile app uses the same page in a secure in-app browser, so a member's account and history are identical on web and mobile.
The admin dashboard has its own sign-in page with the same Google, Apple, and Facebook options plus email and password.
## Sessions and staying signed in
* After you sign in, 1club keeps you signed in as you keep using the app and refreshes your session automatically in the background - you don't re-enter credentials on every visit.
* Sessions are per device and per app. Signing out on one device does not sign you out on the others.
* Sessions are protected with secure, HTTP-only cookies, so your credentials are never exposed to page scripts.
* Signing out ends the session on that device. If you think an account is compromised, change the password (or the provider account) and sign out everywhere.
## Hide My Email with Apple
When you sign in with Apple, you can choose to hide your email. Apple then gives 1club a private relay address (ending in `@privaterelay.appleid.com`) instead of your real one.
You still receive everything as normal. 1club sends your account and notification emails - booking confirmations, receipts, reminders, and verification emails - to that relay address, and Apple forwards them to your real inbox. There's nothing to set up, and you can change the choice anytime in your Apple ID settings.
Emails to members who hid their Apple email are sent from a 1club address so
Apple's relay accepts and forwards them, rather than from your club's own
sending domain. Replies still reach your club.
## Linking and switching methods
One email address is one 1club account, regardless of how you sign in. This means:
* If you first signed up with email and password, you can still choose "Continue with Google" or "Sign in with Apple" later, as long as the provider account uses the same email.
* If you signed up with Google, Apple, or Facebook and now want a password (for example, to sign in on a device without that provider), an org admin can start it for you from **Settings** > **Organization** > **Users** using the **Reset password** action. That sends you a link to set a password; after that, you can sign in with either method using the same email. See [Users](/settings/admin-users).
* Members can review their sign-in email on their profile. Staff can review and update theirs under [Profile](/settings/profile).
## Worked examples
### New staff member joining the admin
An admin invites the new hire from **Settings** > **Organization** > **Users**. The invitation email links them to sign-in. They click "Continue with Google" with their work Google account, and their admin access is ready - no password to create or share.
### Member who wants to keep their email private
A member books through the marketplace and chooses "Sign in with Apple", then picks "Hide My Email". They still get booking confirmations, receipts, and reminders in their real inbox, forwarded by Apple, without giving the club their real address.
### Social sign-up that later needs a password
A member created their account with Facebook, but wants to sign in on a shared kiosk that doesn't have Facebook. An admin uses **Reset password** on that user in **Settings** > **Organization** > **Users**; the member gets a link, sets a password, and can now sign in either way with the same email.
## Tips and best practices
* **Pick one primary method per person.** Any method works, but choosing one keeps sign-in predictable across devices.
* **Match the provider to the email.** Social sign-in only links to an existing account when the provider account uses the same email address.
* **Use Reset password to enable password login**, not to create a second account - the account stays the same; only the sign-in options grow.
* **Keep the sign-in email current.** Members and staff can review it on their profile; it is the key that ties every sign-in method to one account.
## Frequently asked questions
### Can members sign in with Google?
Yes. Members can use "Continue with Google" (including Google One Tap) on the member portal, the marketplace, your club website, and the mobile app.
### Does 1club support Sign in with Apple?
Yes. "Sign in with Apple" is available to members on every member-facing app and to staff on the admin dashboard, and it supports Apple's private email relay.
### Can I sign in with Facebook?
Yes. "Continue with Facebook" is available on every app - the member portal, the marketplace, your club website, the mobile app, and the admin dashboard.
### Can staff use Google, Apple, or Facebook to sign into the admin dashboard?
Yes. The admin dashboard offers Sign in with Google, Sign in with Apple, and Facebook login alongside email and password.
### Can I hide my email when I sign in with Apple?
Yes. Choose "Hide My Email" when Apple prompts you. 1club sends your notifications to the private relay address Apple provides, and Apple forwards them to your real inbox - so you still get booking confirmations, receipts, and reminders without sharing your real email.
### I signed up with Google - can I also use a password?
Yes. Ask an org admin to run **Reset password** for you from **Settings** > **Organization** > **Users**. You'll get a link to set a password, and after that you can sign in with either method using the same email.
### Do I need to create a separate account for the mobile app?
No. The mobile app uses the same sign-in page and the same account as the web apps. Sign in with any supported method and your memberships, bookings, and history are already there.
## Troubleshooting
Trouble signing in? See [Troubleshooting](/reference/troubleshooting#sign-in-and-account).
# Troubleshooting
Source: https://docs.1club.ai/reference/troubleshooting
Common issues you might hit using the 1Club admin - sign-in, permissions, bookings, check-ins, payments, organizations and clubs, exports - and what to check for each.
This page collects the issues we see most often in practice. For anything that isn't on this list, contact support from the **Help & updates** menu.
## Sign-in and account
### You can't sign in
Check, in order:
1. **The org URL is right**. Each organization has its own subdomain on the admin. If you're at the wrong subdomain, your credentials won't be recognized even when they're correct.
2. **The email is the one your invite went to**. If you've been invited under a different email than you usually use, sign in with that one.
3. **Your account is still active**. Ask an Admin in your org to open **Settings** > **Organization** > **Users** and confirm your row exists with status **Active**. If it shows **Invited** or **Expired**, the invitation never completed - have them resend it.
4. **You're not signed into a different org**. The organization switcher in the organization menu at the top of the left rail might have you in another organization where this user doesn't exist. See [Organizations and clubs](/reference/organization-clubs).
5. **You're using the sign-in method tied to your account**. Google, Apple, Facebook, and email/password all resolve to one account by email. If "Continue with Google" doesn't recognize you, confirm that Google account uses the same email as your 1club account. See [Signing in](/reference/sign-in).
### Password reset email never arrives
* Check spam/junk.
* Confirm the email is the one on file (have an Admin verify under **Settings** > **Organization** > **Users**).
* Try once more after 15 minutes - some mail providers delay first-time messages.
* If you signed up with a Google, Apple, or Facebook OAuth provider and never set a local password, password reset won't apply. Ask an Admin to use **Set password** from **Settings** > **Organization** > **Users** instead.
### Two-factor or session keeps logging you out
JWT tokens have a 7-day expiry. After they expire you'll be asked to sign in again - this is normal. If you're being signed out far more often than that, check whether you're using private/incognito mode (cookies don't persist) or whether your browser is clearing cookies on close.
## Permissions and access
### "Permission denied" or pages are missing from the menu
Each module is gated by the permissions your role grants. There are five roles - **Admin**, **Manager**, **Front Desk**, **Instructor**, **Member** - and an admin can change what each non-admin role grants. Pages you don't have access to are hidden from the navigation, not just disabled. Some sections also depend on a module being switched on, such as **Events** or the cash register. See [Roles and permissions](/reference/roles-permissions) for the full module-by-module matrix.
If you should have access and don't:
1. Ask an Admin to verify your role under **Settings** > **Organization** > **Users**.
2. Sign out and back in to refresh your role claims.
3. Check that you're in the right organization (organization switcher in the organization menu). Roles are granted per organization, so a role you hold in one org doesn't carry into another. See [Organizations and clubs](/reference/organization-clubs).
### Custom roles don't exist
You can't create a sixth named role or narrow the Admin role. You can change what Manager, Front Desk, and Instructor grant, module by module, in **Settings** > **Organization** > **Permissions**. See [Roles and permissions](/reference/roles-permissions#the-permissions-page).
## Organizations and clubs
### Members, bookings, or invoices you expect are missing
All data is scoped by organization. Records created in one organization are invisible in another, even when the same person administers both. Confirm the active organization with the switcher in the organization menu, then re-check. If a location's activity looks empty, confirm the club in the breadcrumb too - you may be on a different club, or an admin "All clubs" view that spreads the data across locations. See [Organizations and clubs](/reference/organization-clubs).
### A club is missing from the switcher
The club switcher is the first crumb of the breadcrumb. It lists every **Active** and **Hidden** club; a **Closed** club leaves it. Members only see **Active** clubs. Open **Settings** > **Clubs** > **Clubs**, open the club, and check its status. With only one club running, no switcher is shown at all - that's expected.
### You can't delete a club
A club that still has associated data (bookings, areas, memberships, and so on) is protected from deletion, and the admin blocks it with "Cannot delete club: it has associated data." Reassign or remove that data first, or set the club to **Closed** to retire it without deleting its history.
### Currency, time zone, or locale is wrong across the admin
These are organization-level settings, not per-club. Update them under **Settings** > **Organization** > **Details**; the change propagates to every page that reads org settings. See [Organization settings](/settings/organization).
## Home and analytics
### Widget or chart shows zero where you expect activity
Three usual culprits:
1. **Time zone**. Analytics use your organization's time zone (set under **Settings** > **Organization** > **Details**). A booking that's "today" in your local time may be yesterday in the org's time zone.
2. **Selected club**. Most reports follow the club in the breadcrumb. If it's set to one gym, bookings and revenue from other gyms are excluded.
3. **Permissions**. Front Desk has no analytics by default, and an Instructor sees only **My performance**, limited to their own data.
### Member Engagement dashboard is empty
Engagement scoring is disabled by default. Enable it under **Settings** > **Members** > [**Smart tagging**](/settings/smart-tagging) and let the background job run at least once before checking again.
### Revenue dashboard says "no data"
Check the date range and the club selected in the breadcrumb, then the **Billing entity** filter if your organization has more than one. See [Revenue analytics](/analytics/billing).
## Bookings and check-ins
### Walk-in won't check in
If the booking policy at this club has **Enforce payment** set to **At check-in** but Check-in Mode is **Strict**, you can't check in a person who doesn't have a contact, even if you're willing to take payment. Either create a lightweight contact for them, or switch the club's [Access control](/settings/access-control) to **Flexible** and let payment satisfy the requirement.
### Booking doesn't appear in the calendar
* The calendar filter has a date and (sometimes) an area/class filter. Confirm the booking falls inside both.
* Confirm the booking didn't fail to create. The bookings list under **Schedule** > **Bookings** is the source of truth.
* Verify your role permits viewing other people's bookings. Instructors with own-scope can only see their own.
* Confirm you're on the right club. A booking made against one club won't show while you're viewing another, or while an admin "All clubs" view spreads it among locations. See [Organizations and clubs](/reference/organization-clubs).
### An area is missing from the booking dialog or schedule
The schedule and booking flow only show **Active** areas. Open **Settings** > **Clubs** > **Areas** and check the area's status - Inactive and Maintenance areas are hidden. Also confirm its area type isn't hidden under **Settings** > **Bookings** > **Schedule Display**. See [Areas](/settings/areas).
### The booking price is zero or looks wrong
The line total is seeded from the area's **Price per hour** multiplied by the booked duration, before discounts. If the total is zero, the area has no hourly price set - open the area under **Settings** > **Clubs** > **Areas** and set one. If it's higher or lower than expected, check for a [promotion](/sales/promotions) applied at checkout or a [booking policy](/settings/booking-policies) that changes payment timing. See [Areas](/settings/areas).
### Check-in isn't consuming a membership or credit
Check-in runs through the same pipeline as booking, so a check-in only draws down a membership or package when the member actually has an entitlement that covers this activity. Confirm the member has an active [membership](/sales/memberships) or remaining package credits for the class or area, that the membership hasn't expired, and that the booking policy's **Enforce payment** setting matches how you intend to collect. If there's no entitlement, the system falls back to collecting payment per the policy.
## Payments
### Stripe charge declines
This is almost always a card issue (insufficient funds, blocked by issuer, wrong CVV). The error message from Stripe is shown verbatim - read it carefully. If you're testing in **test mode**, the org uses 1Club's platform Stripe account directly; in **live mode**, it uses the connected merchant account. Make sure you're in the mode you intended (visible under **Settings** > **Integrations** > **Stripe**).
### Refund button is greyed out
You can only refund a payment that's in the **Succeeded** state. Pending payments need to settle first; failed payments can't be refunded because nothing was captured. See [Payments](/sales/payments).
### You can't take card payments at all
If the card form won't load or every charge fails before it reaches the issuer, the organization's Stripe integration isn't connected for the mode you're in. A **live mode** org needs its own Stripe account connected via Stripe Connect; a **test mode** org uses 1Club's platform account. Check the connection and mode under **Settings** > **Integrations** > **Stripe**. See [Stripe](/settings/integrations/stripe).
## Exports
### Export rejected with "exceeds 500,000 row limit"
This is a hard cap. Narrow the date range (a single quarter is usually enough) and try again. For yearly accounting handoffs, run four quarterly exports.
### QuickBooks export creates duplicate customers
The `Display Name` in `invoices.csv` didn't match the customer name in QBO. Open `customers.csv` first, reconcile the names against existing QBO customers, then import invoices. See [Settings > Data Tools > Export](/settings/export) for the recommended QBO workflow.
## Performance
### Pages load slowly
* Close other tabs and browser windows that might be running heavy work.
* Clear your browser cache.
* Try a different browser - Chrome and Safari are the most-tested.
* For analytics, reduce the date range. A 12-month window with a heavy bucket count is slow by design.
If a single page is consistently slow across browsers and machines, report it to support with the page URL and roughly how long it takes.
## Getting help
The **Help & updates** panel in the left rail lets you email the 1Club team, ask 1club AI, or open the Help Center. When you contact support, include:
* The page URL where the issue happens.
* A screenshot or copy-paste of any error message.
* The browser and version.
* Your org slug.
* Steps to reproduce, if you can.
## Related
* [Roles and permissions](/reference/roles-permissions) - The full permission matrix.
* [Organizations and clubs](/reference/organization-clubs) - How org and club scoping decide what data you see.
* [Check-in models](/reference/check-in-models) - How door check-ins relate to bookings and payments.
* [Areas](/settings/areas) - Area status, pricing, and why an area might not show.
* [Stripe](/settings/integrations/stripe) - Payment connection and test/live mode.
* [Settings > Data Tools > Export](/settings/export) - Limits and behavior for CSV exports.
# Bill runs
Source: https://docs.1club.ai/sales/bill-runs
Sweep a period of unbilled charges into invoices in one batch, on demand or on a schedule, and understand how automated membership renewals charge and dun separately.
A bill run takes every billable charge in a period and groups it into invoices. Owners and admins run one on demand or on a schedule to produce invoice documents in one batch. That is the whole job. Bill runs do **not** charge anybody, send anything, or retry failed payments - they generate invoice documents and stop.
If you have been told "the bill run will collect money," that is wrong. Collection happens separately, through the [Payments](/sales/payments) flow, by sending an invoice payment link, or - for auto-renewing memberships - through the automated renewal engine described in [Automated membership renewals and dunning](#automated-membership-renewals-and-dunning). Do not confuse the two.
## When to use it
A bill run gives every member a dated invoice for what they owe or have paid, without you creating invoices one by one. You choose the period and filters, preview the result, and the run skips anything already invoiced.
Typical uses:
* **Monthly invoice documents** - a yoga studio schedules a bill run on the 1st so every member on a monthly plan has a dated invoice for their records, while the renewal engine does the charging.
* **One invoice for a corporate account** - a boxing gym whose ten fighters are sponsored by a company filters a run to those ten contacts and, with the `single` billing preference, sends the company one combined invoice.
* **A catch-up run** - a climbing gym whose scheduled run skipped a plan runs a one-off manual bill run for that period, filtered to the affected plan.
See [Worked examples](#worked-examples) for the details of each.
## What counts as billable
A charge is included in a bill run when all of the following are true:
* It belongs to the organization running the bill run.
* Its date falls inside the period you select.
* `isInvoiced` is `false` (it has not been swept into an invoice yet).
* Its `paymentStatus` is not `void`.
* Its charge type is one of the billable types: booking creation, membership creation, membership recurrence, or membership signup fee. Booking-cancellation reversals are excluded because they are settled, not owed.
Already-paid charges are still billable as long as they are not on an invoice yet. The bill run produces a receivable record for them, just one that opens as **paid**.
## One invoice per contact, or one for everyone
Your organization's **billing preference** decides how the bill run groups charges:
* `separate` (default): one invoice per contact, containing all of that contact's charges in the period.
* `single`: one combined invoice covering every billable charge in the period, addressed to the first contact in the set.
For most gyms `separate` is what you want. `single` is for B2B billing where a corporate account pays for many people in one document.
Every created invoice gets:
* A generated **invoice number** following your organization's numbering template (see [Invoices](/sales/invoices#document-numbering)).
* An **invoice date** set to the moment the run executed, in your organization's timezone.
* A **due date** 30 days after the invoice date.
* Line items pointing back to the original charges.
* A status derived from those charges (`paid`, `partially_paid`, `pending`, or `overdue` based on the due date).
After invoices are written, each included charge is marked `isInvoiced = true` so it won't be picked up again.
## Running a bill run manually
Bill runs live at **Sales** > **Finance** > **Bill Runs**, a tab next to **Invoices**, where you can also review every past run and the invoices it produced.
1. Go to **Sales** > **Finance** > **Bill Runs** and click **Bill Run**. The same button sits on the **Invoices** tab.
2. Give the run a **Name**. It defaults to ` Bill Run`.
3. Pick a **Period start** and **Period end**. Both default to the current month.
4. Optionally narrow the set under **Filters**: charge types, specific contacts, specific plans, and a minimum/maximum amount range.
5. Click **Preview** to see exactly which charges will be invoiced.
6. Review the summary cards - charge count, contact count, total amount, and the resulting invoice count - plus the per-charge-type breakdown chips and the full charge grid.
7. Click **Confirm** to generate the invoices. You land on the bill run details page listing every invoice it created.
The **Auto-charge invoices** toggle on the form is a UI placeholder. The field is accepted by the API but is not wired to anything in the current backend, so leave it off - confirming the run generates invoices only. To collect on what the run produces, use the [Charges](/sales/charges) batch-charge action or send payment links from the individual [invoices](/sales/invoices).
### Per-field guide
| Field | When to use it |
| - | - |
| **Name** | Label the run so you can find it later (for example "March 2026 memberships"). |
| **Period start / end** | Bound which charge dates are swept. Widen to catch a missed period; narrow to bill a single week. |
| **Charge types** | Bill only certain revenue, for example membership recurrence but not one-off bookings. |
| **Contacts** | Bill a specific set of members, for example only a corporate account's staff. |
| **Plans** | Restrict to memberships on chosen plans - useful when only some plans invoice monthly. |
| **Amount range** | Exclude trivial or unusually large charges from an automated sweep. |
## Scheduled bill runs
To run on a recurring schedule, go to **Settings** > **Billing** > **Automated Billing** and create a schedule.
Each schedule has:
* A **Schedule name** and **Description** (both required).
* A **Frequency** - daily, weekly, or monthly - plus a **Time** of day, and a **Day of week** or **Day of month** where relevant. These compile to a cron expression shown in plain language in the list.
* Optional **Filters** identical to the manual form.
When a schedule fires, it calls the same bill-run service the manual flow uses, with a default period (the current month) and the saved filters. You can also trigger a saved schedule on demand with **Run now**; that confirmation dialog spells out which contacts and charge types will be hit before it runs, and then takes you to the invoices the run produced.
A scheduled run executes silently in the background. It does not email members or notify admins on completion. To see results, open the **Bill Runs** tab - the new run appears there with its created invoices.
## Deleting a bill run
Deleting a bill run from its details page clears the `billRunId` on every invoice in it, then removes the bill run record. The invoices and their line items are kept. The original charges stay marked `isInvoiced = true`. If you want those charges to become billable again, delete the invoices too (which reverts them).
## Automated membership renewals and dunning
Recurring membership dues are **not** collected by bill runs. They are collected by a separate hourly engine that renews auto-renewing memberships and charges the member's stored card. Understanding this is the difference between "my members get charged automatically" (renewal engine) and "I generated invoices for the month" (bill run).
### How renewal charging works
Every hour, the renewal engine looks at each active, auto-renewing membership whose renewal is due within its advance window (`renewalDaysInAdvance`, 3 days by default) and:
1. Reconciles the membership's renewal date forward past any period already paid out of band (a manual payment, a sync-paid signup, or a Stripe webhook), so it never re-bills a covered period.
2. Ensures the upcoming period's recurrence charge exists, seeding it if missing.
3. Charges the owed charge against the contact's **default Stripe payment method** once it enters the advance window, then advances the renewal date to the period end on success.
Charging requires the organization to have active Stripe Connect. If the org is not on Stripe, the engine leaves the charge owed for manual collection and does not advance the chain. A genuinely zero-amount renewal is settled without a charge so the chain keeps moving.
The same hourly job also marks any `pending` charge whose date has passed as `overdue`.
### Failed-charge handling (dunning)
When a renewal charge fails - a declined card, no card on file, or a transient Stripe error - the engine:
* Records a `failed` payment row as a per-attempt audit trail (or flips the existing attempt's row to `failed`).
* Flips the charge's own `paymentStatus` to `failed`. This surfaces it in admin lists, feeds the automatic **Past Due** member tag, and excludes the row from the next hourly tick, so a failure is attempted once per renewal rather than retried every hour.
* Emits a payment-failed event, which notifies the member and is available as an [automation](/marketing/automations) trigger for your own follow-up sequences.
Two deliberate behaviors to know:
* **A failed renewal charge never deactivates the membership.** The member stays active and the billing chain still advances so the next period seeds normally. Cancelling a delinquent member is a staff decision, not an automatic one.
* **Recovery is automatic.** A later successful payment flips the charge from `failed` back to `paid` (or `partially_paid`), so the failed state is never sticky once the obligation is met.
There is no built-in multi-attempt retry cadence. Dunning here means "notify and tag once, then leave it for staff or an automation" - not an escalating retry schedule.
## Worked examples
**Yoga studio - monthly membership sweep.** Members on monthly plans auto-renew and are charged by the renewal engine as each renewal date arrives. Separately, you schedule a monthly bill run on the 1st so every member has a dated invoice document for their records. The bill run does not double-charge - it only wraps the already-billed charges into invoices.
**Boxing gym - corporate team billing.** A company sponsors ten fighters. Set the organization billing preference to `single` and filter a bill run to those ten contacts, producing one combined invoice you send to the company. Everyone else continues to invoice `separate`.
**Climbing gym - catch-up run.** You realize last week's scheduled run was misconfigured and skipped a plan. Run a one-off manual bill run for that period, filtered to the affected plan, preview to confirm the count, then confirm. Already-invoiced charges are skipped automatically.
**Pilates studio - failed card recovery.** A member's card expires and their monthly renewal fails. The engine tags them **Past Due**, records the failed attempt, and fires a payment-failed event that your automation turns into a "please update your card" email. When they add a new card and it charges, the charge flips back to `paid` and the tag clears on the next tagging pass. The membership was never interrupted.
## Tips & best practices
* Treat bill runs as invoice generation and the renewal engine as collection. If members need to be charged automatically, the lever is membership **auto-renew** plus Stripe Connect, not the bill run's auto-charge toggle.
* Always **Preview** before **Confirm**. Once you confirm, invoices are written and the charges are marked invoiced.
* Filter scheduled runs so they sweep only what you intend - an unfiltered monthly run picks up every billable charge type in the period.
* Turn the payment-failed event into an [automation](/marketing/automations) so members with a declined renewal are chased without manual work.
* If you miss a scheduled period, run a one-off manual catch-up run - there is no automatic backfill.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| Bill run confirmed but no money was collected | Bill runs only generate invoices; the auto-charge toggle does nothing. | Collect via [Payments](/sales/payments), invoice payment links, or rely on membership auto-renew. |
| A charge you expected isn't in the preview | It is voided, already invoiced, outside the period, or not a billable type. | Check its status and date; delete any invoice already holding it to make it billable again. |
| Members on auto-renew were never charged | The organization isn't on active Stripe Connect, or the member has no default card. | Complete Stripe Connect setup and confirm the contact has a saved default payment method. |
| A member's renewal keeps failing every day | It won't - a failed charge flips the charge to `failed` and is excluded from the next tick. | Fix the card, then the next successful payment clears the failed state; no daily re-charge occurs. |
| Deleting a bill run didn't make its charges billable again | Deleting a run keeps its invoices, which still hold the charges. | Delete the invoices too - that reverts the charges to billable. |
| A membership stayed active despite a declined renewal | By design - renewal failures never auto-deactivate. | Follow up manually or via automation, and cancel the membership if you choose to. |
## Related
* [Invoices](/sales/invoices)
* [Charges](/sales/charges)
* [Payments](/sales/payments)
* [Memberships](/sales/memberships)
* [Automations](/marketing/automations)
# Cash registry and expenses
Source: https://docs.1club.ai/sales/cash
Two things in one module - track the cash each location holds, and manage all your gym's expenses (cash and non-cash) against categories with receipts. Capture cash payments automatically and count the drawer to reconcile any over or short.
This module does two related jobs, so you can retire the spreadsheet your gym keeps for both:
* **Cash registry** - each location gets a cash drawer that always knows how much cash it should hold. Cash payments you take are added automatically, and you record floats, deposits, and pay-outs as they happen.
* **Expense management** - record everything your gym spends, whether or not it is paid in cash. Every expense is filed against a category with an optional receipt, so you get complete expense reporting alongside the cash view.
The two work together: a cash expense both reduces the drawer and counts toward your expense totals, while a card or bank-transfer expense counts toward expense totals without touching the drawer.
The module is opt-in. Until you turn it on, 1Club tracks neither a drawer balance nor expenses. Once it is on, front-desk staff record cash and expenses day to day, and managers correct entries, manage categories, and review the totals.
## When to use it
Turn the module on when you want the cash in each till and everything your gym spends in one record instead of a spreadsheet. Each location has its own drawer, so takings and expenses never mix across sites, and cash sales land in the drawer without anyone re-entering them.
Typical uses:
* **Petty cash and supplier bills** - a water delivery paid from the till is recorded as a cash expense and reduces cash on hand, while rent paid by bank transfer counts toward your expense totals without touching the drawer.
* **Floats and bank deposits** - the front desk records an opening float as **Cash in** at the start of the day and a bank deposit as **Cash out**.
* **Closing the till** - at the end of the day you count the drawer, and 1Club records any over or short so the books match the cash actually there.
See [A typical daily workflow](#a-typical-daily-workflow) for how these fit into a day.
## Turn on the cash module
Go to **Settings** > **Sales** > **Cash Registry & Expenses** (`/settings/operations/cash-registry`) and toggle the module on. The setting is organization-wide and controls whether the **Cash** tab appears in the **Sales** section. Once it is on, **Sales** > **Cash** has three tabs: **Overview**, **Expenses**, and **Ledger**.
The first time you enable it, 1Club stamps an activation moment and seeds a starter set of expense categories. Only cash taken from that moment on counts toward the drawer, so turning the feature on never sweeps historical sales into today's balance.
Viewing the drawer and ledger requires the `cash.read.club` permission.
Recording expenses, cash movements, and counts requires `cash.create.club`.
Editing an entry, reversing one, and managing categories all require
`cash.manage.club`. Turning the module on or off requires
`settings.manage.organization`.
Front desk staff can read the drawer and record cash day to day. Correcting an entry after the fact, whether by editing or reversing it, stays with managers, as does editing categories - a light segregation-of-duties boundary.
## The cash drawer
The drawer balance for a location is simple to reason about:
```
cash on hand = cash sales + cash in (floats, deposits) - cash out (expenses, pay-outs)
```
Only physical cash moves the drawer. A card or bank-transfer expense is recorded for reporting but leaves cash on hand untouched. You can see the current balance for each location on the **Sales** > **Cash** > **Overview** page.
## Automatic cash capture
When you take a cash payment for a class, membership, or product, 1Club adds it to that location's drawer automatically. You never re-enter a cash sale in the registry, so the money the drawer holds and the payments you recorded always agree.
Cash sales appear in the ledger as read-only rows alongside the entries you record by hand. A cash refund reduces the drawer only when you record handing the cash back as a cash-out entry; the original sale stays in the record.
## Expense management
The **Sales** > **Cash** > **Expenses** page is your gym's full expense record, not just the cash that left the till. Record every cost here - rent, instructor pay, supplies, utilities, a water delivery - regardless of how it was paid, and each one is filed against a category with an optional receipt for complete expense reporting.
Click **Add expense**, choose the location, enter the amount, pick a category, and optionally attach a receipt photo and a note.
* **Payment method** decides whether the drawer moves, so non-cash spend is a first-class part of the record. A **cash** expense (petty cash for supplies, a water delivery paid from the till) reduces cash on hand. A **card**, **bank transfer**, or **other** expense counts toward your expense totals but does not touch the drawer.
* **Category** groups the expense for reporting - rent, instructor pay, supplies, utilities, and so on. Categories are editable (see below).
* **Receipt** lets you keep a photo of the receipt against the entry.
Because both cash and non-cash costs live here, your expense reporting is complete regardless of how each one was paid, and the **Expenses by category** breakdown on the overview reflects all of them.
### Fix an expense you already recorded
Click the pencil on the expense's row and change what was wrong: the amount, the payment method, the date, the category, the supplier, the document number, the description, or the receipt. Save, and the expense report and the drawer both follow the corrected figure.
The one thing you cannot change is the location. A recorded movement belongs to one drawer, so to move an expense to another location, reverse it and record it again against the right one.
Changing the payment method moves money in or out of the drawer, because only a cash expense counts toward cash on hand. Switch a cash expense to card and the drawer gives the money back; discover that a card expense was really paid from the till and the drawer takes it.
## Move cash in and out
Use **Cash in** and **Cash out** for movements that are not sales or expenses:
* **Cash in** - an opening float at the start of the day, or topping up the drawer.
* **Cash out** - a bank deposit, or paying cash out of the till for something you are not categorizing as an expense.
Both adjust the drawer immediately.
## Expense categories
Categories are editable per organization. Manage them from **Sales** > **Cash** > **Expenses** with the **Categories** action:
* Add, rename, and reorder categories to match how your gym thinks about spending.
* A category that is still used by past expenses is **archived** rather than deleted, so historical reporting keeps its label. It disappears from the picker but its name survives on old entries.
* Re-creating a category with the same name restores the archived one instead of failing, so you can bring a category back.
## Count and reconcile the drawer
Counting is optional. When you want to check the till, open **Sales** > **Cash** > **Overview**, click **Count cash**, choose the location, and enter the amount you physically counted.
1Club compares your count to the expected balance and, if they differ, records a reconciliation entry for the over or short so the book balance matches reality. If the count matches, nothing is posted. Recent counts and their variances show on the overview.
A count trues the drawer up against the cash actually in it at that moment. If
you afterwards edit or reverse a cash entry from before the count, cash on hand
moves by the difference and stops matching what you counted. Count again after
correcting an older cash entry.
## The ledger
**Sales** > **Cash** > **Ledger** is the full history for a location: cash sales captured automatically and every entry you recorded, newest first. Nothing is ever deleted.
Two actions correct a row, and which one you want depends on what went wrong:
* **Edit** fixes an entry whose details were recorded wrongly - a mistyped amount, the wrong category, a receipt nobody attached. The row keeps its place in the history and the drawer follows the corrected figure.
* **Reverse** undoes an entry that should not be there at all - a duplicate, or spend that never happened. 1Club posts an equal and opposite **reversing entry** pointing back at the original, so both the mistake and the correction stay readable.
An entry can only be reversed once, and once a reversing entry exists neither half of the pair can be edited: a correction you can already read is not rewritten.
## The overview dashboard
**Sales** > **Cash** > **Overview** is the at-a-glance dashboard:
* **Cash on hand** for the selected location, or every location when you pick all clubs from the top bar.
* **Cash in vs cash out** over a chosen period (last 7, 30, or 90 days).
* **Expenses by category** for the period, so you can see where the money goes.
* **Recent reconciliations** with their over/short variance.
## How corrections keep the books honest
Only the entries a person typed in are editable, and only by a manager. Everything else in the registry is derived from something else, so you correct it at its source:
* A **cash sale** is read from the payment that produced it. Refund the payment; the sale is never edited here.
* A **cash count adjustment** is computed from the amount you counted. Count again, and 1Club posts a fresh adjustment.
* An **expense from a delivery** mirrors its goods receipt. Void the delivery and post a corrected one, and the expense follows.
* A **reversing entry**, and the entry it undoes, are a finished pair. Neither is editable.
Nothing is ever deleted, and the drawer balance is always the sum of the record you can see.
An edit records that the entry changed, but not what it used to say. When you
need the original figure preserved on the page - a disputed invoice, an amount
someone else will question - reverse the entry and record a corrected one
instead of editing it.
## A typical daily workflow
Enable it once in **Settings** > **Sales** > **Cash Registry & Expenses**.
Record a **Cash in** entry for the cash you put in the drawer at the start
of the day.
Ring up classes, memberships, and products. Cash payments are added to the
drawer automatically.
Add expenses for supplies, deliveries, and instructor pay, choosing the
payment method and a category, and attach receipts.
At close, count the cash and enter it. 1Club records any over or short so
the books match the till.
Check cash on hand, cash flow, and expenses by category over the period.
## Related
* [Point of sale](/pos/point-of-sale) - where the cash sales that fill the drawer are rung up
* [Payments](/sales/payments) - how cash and other payments are recorded
* [Inventory and stock](/sales/inventory) - another opt-in module you turn on the same way
* [Analytics overview](/analytics/overview) - organization-wide reporting
# Charges
Source: https://docs.1club.ai/sales/charges
What each payer owes for every booking, membership, signup fee, and product sale, and how charges relate to payments and invoices - charge saved cards or add payments in bulk, send myPOS links, refund what was paid, and void what was not.
A charge is what a payer owes for one thing: a booking, a membership period or signup fee, a product, a check-in, a cancellation charge, or a settlement. Every chargeable event in 1Club writes exactly one charge. Money that comes in is recorded separately as a [payment](/sales/payments), so one charge can be paid by several payments (a deposit then a balance), and one payment can pay several charges at once.
Charges were called **Transactions** in earlier versions of the admin and
member apps. The [Platform API](/api-reference/introduction) and the
[MCP tools](/mcp/tools) still call this record a transaction
(`/v1/platform/transactions`, `list_transactions`).
## When to use it
**Charges** lists everything owed across the organization, filterable by status, contact, membership, booking, revenue account, and date range. Each row shows what is still owed after succeeded payments, so you always know the real figure to collect. Owners and staff who handle billing use it to collect balances one by one or in bulk, wrap a charge into an invoice, void what should never have existed, and add an ad-hoc charge that has no booking or membership behind it.
Typical uses:
* **Charging a clinic roster after the session** - a padel gym selects every pending booking from its weekly clinic and charges each player's saved card in one batch.
* **Recording cash for a group of drop-ins** - a jiu-jitsu gym settles six open-mat visitors who paid cash at the desk with a single manual payment action.
* **Collecting from a member with no card on file** - a yoga studio emails a myPOS payment link for a membership started over the phone, and the charge is paid when the member pays.
* **Clearing charges nobody paid** - after a rained-off tournament, you void thirty unpaid entry charges in one go and refund the few members who had already paid.
See [Worked examples](#worked-examples) for the steps behind each of these.
## Charges, payments, invoices, and reversals
These are different records, and the distinction drives everything on this page:
* A **charge** is what is owed: it says a contact owes an amount for one thing. It carries a **Payment status** but never moves money on its own.
* A **payment** is money that came in: it says money was collected (or refunded), how, and when. One charge can be paid by several payments, and one payment can pay several charges.
* An **invoice** is a document issued over one or more charges, with a number and a due date. Not every charge is invoiced.
* A **reversal** is a negative charge that cancels all or part of another charge, for example when a booking is cancelled. It is different from a refund, which returns money from a payment, and from voiding, which closes an unpaid charge that should never have been owed.
So "recording a payment" and "the charge becoming paid" are two steps: you record a payment, and its outcome then drives the charge's status (see [How payment status flows](#how-payment-status-flows)). This is why a charge can sit at `partially_paid`, and why voiding a charge does not touch any money that was already collected against it.
The unresolved statuses (`pending`, `partially_paid`, `overdue`, and `failed`) are what the contact still owes. The part that has fallen due is their **Due now**; a charge that is not due yet, such as a booking whose visit has not started or a membership that starts later, counts as **Upcoming** instead (see [What counts as due now](/members/member-profiles#what-counts-as-due-now)). The closed statuses are `paid`, `settled`, `refunded`, `void`, and `cancelled`.
## Anatomy of a charge
Open the page at **Sales** > **Finance** > **Charges** (`/billing/charges` in the admin; the old `/billing/transactions` address redirects there). The grid pages on the server and is tab-filtered into **All**, **Pending**, **Overdue**, **Paid**, and **Void**, with a live count badge on each tab.
**Void has its own tab, and voided rows are excluded from All.** Voided charges are not owed and not revenue, so leaving them in the default view inflated every count and total you read off the page. They are still there for audit whenever you need them - just one tab across.
Each row carries:
* **Date** of the obligation.
* **Type** (`booking_creation`, `booking_cancellation`, `membership_creation`, `membership_recurrence`, `membership_signup_fee`, `membership_cancellation`, `product_sale`, `external_program_entry`, `checkin_creation`).
* **Description** describing what was billed.
* **Contact** the charge is against.
* **Amount**, **Tax amount**, **Total amount**, and **Currency** (the organization currency at the time of creation).
* **Payment status**: `pending` (shown as **Unpaid**), `paid`, `void`, `failed`, `settled`, `overdue`, `partially_paid`, `refunded`, or `cancelled`.
* **Remaining balance** - what is still owed after succeeded payments and any mid-flight membership discount are subtracted. This is the number the Add payment dialog defaults to, so the column and the dialog agree row for row.
* **Invoiced** flag. Once a charge has been wrapped into an invoice (manually or by a bill run), this is `true` and the obligation will not be picked up by future bill runs.
* Optional foreign keys to a **booking**, **membership**, **product**, **external program**, **tax rate**, and **revenue account**.
* Optional **parent / child** links, used to chain related entries together (for example a `booking_cancellation` reversal referencing the original `booking_creation` charge).
Zero-amount charges are auto-marked `paid` at creation, so a free booking still produces a charge but it never blocks anything.
## How charges get created
Most of the time you won't create charges by hand. They arrive when:
* Someone is **booked** into a class, an area, or with an instructor. The booking flow writes the charge at the price implied by the membership or the override.
* Someone **buys a plan**. You get `membership_creation` plus, when applicable, `membership_signup_fee`.
* A **recurring membership period rolls over**. The hourly `recurring-transactions` job writes the next `membership_recurrence` charge, and (when the plan requires payment upfront and a default card exists) attempts to charge it.
* A booking is **cancelled** with a refund or charge policy. You get a `booking_cancellation` reversal linked to the original charge.
* A **product** is sold through the Point of Sale.
### Creating one by hand
To record an ad-hoc charge that doesn't correspond to a booking or membership:
1. On **Sales** > **Finance** > **Charges**, click **Add** in the page header.
2. Pick the **Contact**, enter the **Amount**, and optionally a **Description** and **Revenue account**.
3. Save. The charge is created `pending` (unless the amount is zero, which auto-marks it `paid`).
## Row actions
Open a row's action menu (or use the details page) for the following. Which actions appear depends on the charge's status:
* **Add payment** - available while the charge is still collectible: `pending`, `overdue`, `partially_paid`, or `failed`. Opens the payment dialog, where you pick a payment method (manual or Stripe) and record the settlement. The default amount is the row's remaining balance.
* **Refund** - available once money has been collected. Returns it to the card, the member's wallet, or by hand. See [Refunds](/sales/payments#refunds).
* **Send myPOS payment link** - available when myPOS is connected for the organization AND the status is `pending`, `overdue`, or `partially_paid`. Emails the customer a secure link. See [Sending a myPOS payment link](#sending-a-mypos-payment-link).
* **Add invoice** - available when the charge hasn't been invoiced yet, has a contact, and isn't `void`. Wraps the single charge into a new one-line invoice and navigates to the invoice page.
* **Void** - available only while **nothing has been collected**: `pending`, `overdue`, or `failed`. Marks the charge `void` so it stops showing as owed and stops being picked up by bill runs. Voiding is a soft delete - the row remains for audit.
* **View** - opens the details page.
### Which action applies at which status
Collect and void are governed by two deliberately different lists. Void is the narrower one, because voiding a charge that took money would erase a real sale and strand the customer's funds.
| Status | Add payment | Void | Refund |
| - | - | - | - |
| `pending` | yes | yes | no |
| `overdue` | yes | yes | no |
| `failed` | yes | yes | no |
| `partially_paid` | yes | no | yes |
| `paid` | no | no | yes |
| `refunded` | no | no | no |
| `settled` | no | no | no |
| `cancelled` | no | no | no |
| `void` | no | no | no |
`failed` stays collectible on purpose: a charge attempt that failed leaves the balance owed, and retrying it or taking cash instead is exactly what you do next.
`settled` rows are closed and reconciled. Some of them are reversals carrying a negative amount, so they are kept out of the collectible list to stop anyone recording a payment against a reversal.
## Batch actions
Select rows with the checkboxes in the grid to unlock the header batch actions. All three are best-effort: a per-row failure is reported in the results and does not block the other rows.
Every batch action reports why each skipped row was skipped, using a consistent set of reasons: **Charge not found**, **Belongs to another organization**, **Already paid**, **Void**, **No remaining balance**, **Already voided**, **Paid - refund it instead of voiding**, and **Closed - nothing owed**.
### Charge saved card (Stripe)
**Charge saved card** calls Stripe for every selected charge's contact, charging their **default saved payment method**. A confirmation dialog first warns that this will charge the default payment method on file for each customer.
For each selected row the batch:
* **Skips** rows that are not collectible, rows with no contact, rows that already have a `pending`/`processing` payment in flight, and rows where the contact has no default payment method on file.
* **Charges the outstanding balance**, not the gross amount. A partially paid charge is collected only for what remains, so a member who has already paid half is not billed the full amount again.
* Uses an idempotency key built from the charge ID and the current date (`batch-charge-txn--`), so rerunning the batch on the same day won't double-charge.
When it finishes, a **Batch Payment Results** dialog summarizes how many succeeded, failed, and were skipped, with the reason for each failure (no payment method, charge declined, and so on).
Batch **Charge saved card** only works for customers who have a saved Stripe card. For
customers without one, use **Send myPOS payment link** (so they pay on their
own device) or collect at the desk and use **Add payment**.
### Add payment
**Add payment** opens the payment dialog for every eligible selected charge. The dialog shows how many charges across how many contacts, the **Total to record**, and how many selected rows will be skipped (already paid or void). What it records depends on the selection:
* **One contact, one currency** - a single payment for the combined remaining balance. The amount is fixed to that total, and you pick any payment method the contact can pay with, such as cash, a saved card, or their wallet.
* **Several contacts or currencies** - one payment per charge, each for that charge's own remaining balance, because a payment has one payer and one currency. Only manual payment methods (cash, bank transfer, etc.) are offered.
1. Select the charges and click **Add payment**.
2. Check the summary and the **Total to record**.
3. Pick the **Payment method**. A manual method also asks for the **Payment date**.
4. Click **Save**.
Rows are skipped and counted in the result when the charge is not found, belongs to another organization, is already paid, is void or otherwise closed, or has no remaining balance. To charge several customers' saved cards at once, use **Charge saved card** instead.
### Void
**Void** closes out every selected charge that has taken no money. The confirmation is explicit about the consequences:
> Void selected charges? Voided charges are excluded from revenue and bill runs, and this cannot be undone.
Rows that have been paid are **skipped, not voided**, and the dialog tells you before you confirm:
> selected charges will be skipped: they have been paid, so they need a refund instead of a void.
A **Void results** dialog then lists what happened per row. Use this for a batch of charges that should never have existed - a duplicated import, a clinic that was cancelled before anyone paid. For anything already collected, refund it instead.
## Sending a myPOS payment link
When your organization has [myPOS](/settings/integrations/mypos) connected, you can email a customer a secure link to pay a charge on their own device instead of charging a card yourself. This is ideal when there's no card on file and the customer isn't at the desk.
The **Send myPOS payment link** row action appears only when myPOS is active and the charge status is `pending`, `overdue`, or `partially_paid`. Closed states (`paid`, `void`, `refunded`, `cancelled`, `failed`, `settled`) don't offer it.
1. From the charge's action menu, choose **Send myPOS payment link**.
2. The dialog shows the reference (the description, or `#`), the amount, and the contact's name and email.
3. Click **Send link**. 1Club generates a link and emails it to the contact's address on file.
Behind the scenes, sending is a two-step call: the server first generates a stateless link (a JWT-backed `/pay/` URL on your member-facing site) and then emails it. The customer opens the link on any device and pays through myPOS hosted checkout; the myPOS notification (IPN) marks the charge paid. The link is **valid for 7 days**.
For the link to generate, the charge must be payable (not paid or void), have a positive total amount, and resolve a currency from the charge or the organization. For the email to send, the contact must have an email address on file.
## Charge details
The **Charge Details** page opens at `/billing/charges/`. Beyond the same fields shown in the grid, the details page exposes:
* A **payments** list: each payment linked to this charge with amount, status, and date, each linking to the payment.
* The full tax breakdown when a tax rate applied (rate name, percentage, description).
* **Recurrence info** for `membership_recurrence` charges: auto-renew flag, billing frequency, and the period start/end dates.
* The linked **booking** or **membership** as a clickable card.
* A **Related charges** card showing the parent and any children, so you can trace a cancellation reversal back to the original booking charge or follow a chain of recurring periods.
## How payment status flows
When a payment is recorded, its outcome determines the charge's new status:
| Payment outcome | Charge status |
| - | - |
| `succeeded` | `paid` |
| `failed` | `pending` |
| `cancelled` | `pending` |
| `refunded` | `refunded` |
| `partially_refunded` | `partially_paid` |
A successful payment also flips any pending booking on that charge to `confirmed`.
The hourly job that processes recurring memberships also runs a sweep that flips any `pending` charge with a date in the past to `overdue` (start of UTC day). `overdue` is treated as a flavor of `pending` when invoice status is derived.
## Worked examples
### Batch-charging a padel clinic roster
You run a weekly paid padel clinic. Everyone who booked has a card on file, and you charge the group after the session.
1. On **Sales** > **Finance** > **Charges**, open the **Pending** tab and search for the clinic by description (or filter by the class).
2. Select all the roster's `booking_creation` rows with the checkboxes.
3. Click **Charge saved card** and confirm.
4. Read the **Batch Payment Results**: successes flip to `paid`, and any "No payment method on file" or "charge declined" rows tell you exactly who to follow up with.
If you rerun the batch later the same day to retry the failures, the idempotency key prevents double-charging anyone who already succeeded.
### Recording cash for a jiu-jitsu drop-in cohort
A group of visitors dropped in for an open-mat jiu-jitsu session and paid cash at the desk. Their bookings created `pending` charges.
1. Select the drop-in charges in the grid.
2. Click **Add payment**.
3. Confirm the summary (for example "6 charges across 6 contacts" and "Total to record: 90.00"), pick **Cash** as the **Payment method**, set today's **Payment date**, and click **Save**.
Because the drop-ins are different contacts, each eligible row gets its own payment for its full remaining balance, in one action. Any already-paid rows in your selection are safely skipped and counted.
### Sending a myPOS link for a yoga membership balance
A member started a Pilates/Yoga plan over the phone but has no card on file. Their `membership_creation` charge is `pending`.
1. Open the charge's action menu and choose **Send myPOS payment link**.
2. Confirm the amount and the member's email, then click **Send link**.
3. The member gets an email, taps the link on their phone, and pays through myPOS. The charge is marked paid automatically when myPOS confirms - no further action at the desk.
### Voiding an erroneous charge
A front-desk error created a duplicate signup fee for a new climbing member.
1. Open the charge. If nothing was collected, **Void** is offered in the row action menu; if money was taken, it is not, and **Refund** is the correct action instead.
2. Choose **Void** and confirm.
3. The charge moves to `void`: it stops counting as owed, is excluded from bill runs and revenue, and drops out of the **All** tab. Find it later under the **Void** tab - the row stays in the ledger for audit.
### Clearing a batch of charges nobody paid
A rained-off tournament left thirty unpaid entry charges on the ledger.
1. Filter to the **Pending** tab and select the affected rows.
2. Click **Void** in the header and read the confirmation: any row that has been paid is listed as skipped, needing a refund instead.
3. Confirm. The **Void results** dialog shows what was voided and what was skipped, so you can refund the handful of members who had already paid.
## Tips & best practices
* **Filter before you batch.** Use the **Pending** and **Overdue** tabs plus search to select exactly the right rows. Batch actions act on your whole selection.
* **Pick the right collection tool.** Card on file -> **Charge saved card**. Customer not present, no card -> **Send myPOS payment link**. Paid in person -> **Add payment**.
* **Trust the remaining balance column.** It already nets out succeeded payments and mid-flight membership discounts, so it is the true figure to collect - not the gross total.
* **Retry failures the same day freely.** The batch charge idempotency key is scoped to the charge and the date, so a same-day rerun only charges rows that haven't succeeded yet.
* **Void what was never paid, refund what was.** Voiding preserves the audit trail and is only offered while nothing has been collected. The moment money has moved, **Refund** is the only correct action - and 1Club will not let you void your way around it.
* **Read totals off a tab, not off All.** Void rows are excluded from **All** so the counts and totals you see are real. If a figure looks short, check whether you are expecting voided charges to be in it.
* **Reconcile through invoices when a customer needs a document.** Use **Add invoice** on a single row, or a [bill run](/sales/bill-runs) to sweep many pending charges at once.
## Troubleshooting
**Issue**: Batch **Charge saved card** skipped a customer with "No payment method on file."
**Cause**: That contact has no default saved Stripe card. Batch charge can only use a stored default method.
**Fix**: Ask the customer to add a card, use **Send myPOS payment link** so they pay on their own device, or collect at the desk and use **Add payment**.
**Issue**: A batch charge row failed with "charge declined."
**Cause**: Stripe declined the card (insufficient funds, expired, etc.).
**Fix**: Follow up with the customer to update their card, then rerun the batch (same-day reruns won't double-charge the ones that succeeded).
**Issue**: The **Send myPOS payment link** action isn't showing on a charge.
**Cause**: Either myPOS isn't connected for the organization, or the charge isn't in a payable state (`pending`, `overdue`, or `partially_paid`).
**Fix**: Confirm myPOS is connected under [**Settings** > **Integrations** > **myPOS**](/settings/integrations/mypos). If the charge is `paid`, `void`, `refunded`, or `cancelled`, there is nothing to collect.
**Issue**: Sending a myPOS link failed with an email error.
**Cause**: The contact has no email address on file.
**Fix**: Add an email to the contact's profile, then resend. To generate a link, the charge also needs a positive amount and a resolvable currency.
**Issue**: **Add payment** shows no payment methods to pick for a selection.
**Cause**: The selection spans several contacts or currencies, so each charge gets its own manual payment, and no manual-channel payment method (cash, bank transfer, etc.) is configured. To charge saved cards across several customers, use **Charge saved card**.
**Fix**: Configure a manual payment method in settings, then retry.
**Issue**: **Void** isn't offered on a charge I want to write off.
**Cause**: Money has been collected against it. Voiding a charge that took money would erase a real sale and leave the customer's funds stranded, so it is blocked once the status is `partially_paid` or `paid`.
**Fix**: Use **Refund** instead and choose where the money goes. See [Refunds](/sales/payments#refunds).
**Issue**: A bulk void skipped some of my selected rows.
**Cause**: Those rows have been paid. The confirmation dialog lists them before you commit: "they have been paid, so they need a refund instead of a void."
**Fix**: Void the unpaid rows, then refund the paid ones individually.
**Issue**: My **All** tab total is lower than I expected.
**Cause**: Voided charges are deliberately excluded from **All**, because they are neither owed nor revenue.
**Fix**: Check the **Void** tab if you need to see them. If a figure genuinely looks wrong, confirm you are not expecting voided charges to be counted.
**Issue**: **Add payment** isn't available on a `settled` charge.
**Cause**: `settled` rows are closed and reconciled, and some are reversals with a negative amount. They are kept out of the collectible list so nobody can record a payment against a reversal.
**Fix**: If money is genuinely owed, add it as a new charge.
**Issue**: A `cancelled` charge still appears to be owed somewhere.
**Cause**: The member-facing outstanding charges query treats anything not `paid` as payable, so a `cancelled` row can surface until it's explicitly excluded.
**Fix**: Void the row if it should stop counting as owed, or ignore it in outstanding views.
## Related
* [Payments](/sales/payments) - the money that came in to pay charges.
* [Invoices](/sales/invoices) - how to issue a sendable document over charges.
* [Bill runs](/sales/bill-runs) - sweeping pending charges into invoices in bulk.
* [myPOS](/settings/integrations/mypos) - connect myPOS hosted checkout to enable payment links.
* [Booking policies](/settings/booking-policies) - decides when a booking's charge is created and when it must be paid.
# Deliveries and invoice scanning
Source: https://docs.1club.ai/sales/deliveries
Book incoming stock as a supplier delivery. Attach the invoice, let 1Club read the line items, review them, and post once so stock rises and the expense is recorded with the invoice attached.
A delivery records stock arriving from a supplier. You attach the supplier invoice, 1Club reads the supplier, the invoice number, the date and every line item off it, and you review the result before anything is committed. Posting the delivery does both halves of the job at once: stock rises at the location, and an expense lands in the cash ledger with the invoice attached to it. Managers who receive stock create and post deliveries; front-desk staff can view them.
Deliveries are part of [Inventory](/sales/inventory), so they appear only when inventory is turned on for your organization.
## When to use it
Use a delivery whenever goods arrive, instead of adjusting stock item by item and typing the expense in separately. Supplier, invoice number, date and every line live on one record, and because stock and the expense are written together, the two can never disagree. Scanning is optional: adding a delivery by hand uses the same grid, starting from an empty table.
Typical uses:
* **A drinks order for the cafe fridge** - you scan the invoice for a case of 24 bottles billed as one case, set **Per pack** to 24, and 24 bottles go into stock while the expense is recorded at the gross total, VAT included.
* **A supplier you order from every week** - you match an unfamiliar line to your catalogue by hand once, and the next invoice from that supplier arrives already matched.
* **An invoice with a new item and a delivery charge** - you create the new product straight from its line and mark the delivery charge **Expense only**, so it counts towards the expense without booking stock.
## Create a delivery
Go to **Sales** > **Products** > **Deliveries** and select **New delivery**. Deliveries require the `inventory.manage.club` permission; reading them requires `inventory.read.club`.
The delivery opens as a **draft**. Nothing you do to a draft affects stock or money, so you can leave it half-finished and come back to it.
## Scan a supplier invoice
Drop the invoice onto the panel at the top of the delivery, or select the panel to pick a file. Photos and PDFs are both accepted, up to 15 MB.
1Club reads the document and fills in:
* the supplier, the invoice number and the invoice date
* every line item, with its quantity, unit, unit cost and VAT rate
* the invoice totals, which are checked against the sum of the lines
Scanning only ever fills in a draft. If the read fails or the document is unreadable, the delivery is left exactly as it was and you can type the lines in yourself.
Check every line against the document before posting. Scanning is an
assistant, not an authority - a smudged or unusual invoice can be misread, and
what you post is what your stock and books will say.
If the line totals do not add up to the invoice total, 1Club tells you so rather than silently trusting either figure.
## Review the lines
Each line has an item name as printed on the invoice, an optional supplier code, the catalogue item it is matched to, a type, and the quantities and costs.
### Matching
1Club matches each line to a product or ingredient in this order:
1. **Barcode** - an exact match on the product barcode.
2. **Article number** - an exact match on the product SKU.
3. **A mapping this supplier taught us** - how you matched the same item on a previous invoice.
4. **Name** - a close match on the item name.
A weak name match is deliberately left unmatched. A wrong automatic match would book stock against the wrong product without anyone noticing, which is worse than an empty cell.
Whenever you match a line by hand, 1Club remembers it against that supplier's item code and printed name. The next invoice from the same supplier arrives already matched.
### Lines you cannot match
If an item is not in your catalogue yet, select the plus beside the line to create the product from it. The name and cost are carried over from the invoice, and you set the sale price. The new product is attached to the line straight away.
If a line is not stock at all - a delivery charge, a service fee - change its type to **Expense only**. It stays on the delivery and counts towards the expense, but no stock is booked for it.
Every stock line must be matched, created or marked expense only before the delivery can post. This is deliberate: a line left unmatched would otherwise post an expense while quietly adding no stock.
### Pack sizes
**Per pack** is how many individual items are in one billed unit. A case of 24 bottles billed as one case is quantity 1, per pack 24, and 1Club books 24 bottles into stock.
Leave it at 1 when the invoice bills individual items.
### Costs and VAT
**Unit cost** is the net price per billed unit, excluding VAT, and **VAT %** is the rate on that line.
The two sides of the delivery use different figures, which is intentional:
* The **expense** is the gross total, VAT included, because that is what you paid.
* The **stock cost** is the net cost per individual unit, because that is what the goods cost you.
## Post the delivery
Select **Execute**. In a single step 1Club:
* writes a receive movement for every stock line at the delivery's location
* updates each product's cost price from what you just paid
* records one expense in the [cash registry](/sales/cash) for the gross total, with the invoice attached and filed under the chosen category
If the cash registry is turned off for your organization, the stock is still booked and 1Club tells you no expense was recorded.
Once posted, the delivery becomes a read-only record. Its lines, costs and totals cannot be edited.
## Void a delivery
Select **Void** on a posted delivery. 1Club writes reversing stock movements and reverses the linked expense, leaving the original record intact for the audit trail.
Voiding is how you correct a posted delivery. Fix whatever was wrong on a new delivery rather than trying to edit the old one.
## Suppliers
Suppliers are created from the delivery itself: open the supplier picker and choose **Add supplier**. A supplier holds its VAT number, company ID, contact details, and the payment method and expense category to default new deliveries to.
Scanning an invoice from an unknown supplier creates that supplier automatically from the name printed on the document.
Suppliers are archived rather than deleted, so posted deliveries keep pointing at whoever supplied them.
## Permissions
| Action | Permission |
| - | - |
| View deliveries and suppliers | `inventory.read.club` |
| Create, scan, post and void | `inventory.manage.club` |
| Choose an expense category | `cash.read.club` |
| Turn inventory on or off | `settings.manage.organization` |
Deliveries write into the cash ledger, so the endpoints behind them are unavailable entirely while inventory is turned off, not merely hidden.
# Inventory and stock
Source: https://docs.1club.ai/sales/inventory
Track stock levels per location, record receive, sale, adjustment, and waste movements, and build cafe or bar recipes from ingredients that decrement automatically when a product sells.
Inventory lets your gym track physical stock behind the point of sale: bottled drinks and merchandise in a pro-shop, and the raw ingredients that make up cafe or bar items. When a product sells, stock decrements automatically, so on-hand counts stay accurate without manual bookkeeping. Managers receive, adjust, and write off stock and build recipes; front-desk staff can see stock levels and sell as normal.
Inventory is an opt-in feature. Until you turn it on, products sell without any stock tracking.
## When to use it
Turn inventory on when you sell physical goods and want to know what is left at each location without counting by hand after every sale. Every change is recorded in a ledger you can audit, and each location keeps its own count.
Typical uses:
* **A pro-shop with bottled drinks and merchandise** - each bottle of water or branded t-shirt you ring up takes one unit off that location's count.
* **A cafe that makes drinks to order** - a latte recipe of 18 g of coffee beans and 150 ml of milk draws down both ingredients on each sale, and if you have milk for 40 lattes but beans for only 12, 1Club shows 12 available.
* **Keeping counts honest** - you record a spilled or spoiled item as waste, correct the count after a physical stock-take, and you review write-offs in the movements report.
See [A typical pro-shop and cafe workflow](#a-typical-pro-shop-and-cafe-workflow) for how to set this up end to end.
## Two kinds of stock: products and ingredients
Every stock record points at exactly one of two things:
* **Products** - packaged items you buy and resell as-is, like a bottle of water or a branded t-shirt. Products are always counted in whole units (`each`).
* **Ingredients** - raw or prepped items that go into something you sell, like coffee beans, milk, or a burger patty. Ingredients are used to build cafe and bar recipes.
Stock is held separately for each location (your organization's clubs). The same ingredient can have a different quantity on hand at each site.
## Turn on inventory
Go to **Settings** > **Sales** > **Inventory Management** (`/settings/operations/inventory`) and toggle inventory on. The setting is organization-wide and controls whether the **Stock**, **Ingredients**, and **Deliveries** tabs appear under **Sales** > **Products**, and whether stock panels and the **Recipe** action appear on your products.
Enabling inventory changes the product detail page: a stock panel appears and,
for products you build from ingredients, a **Recipe** action becomes
available. With inventory off, neither shows.
Reading stock requires the `inventory.read.club` permission; receiving, adjusting, and editing recipes require `inventory.manage.club`. Turning the feature on or off requires `settings.manage.organization`.
## Units of measure
Each ingredient has a base unit of measure that is fixed when you create it and cannot be changed afterwards. There are three base units:
| Base unit | Use for | Dimension |
| - | - | - |
| `g` | Anything weighed (beans, meat, powder) | Mass |
| `ml` | Anything poured (milk, syrup, spirits) | Volume |
| `each` | Anything counted (cups, bottles, patties) | Count |
When you receive or adjust stock you can enter quantities in a friendlier unit and 1Club converts to the base unit for you: grams accept `g` or `kg`, milliliters accept `ml` or `l`, and counted items accept `each`. Products, which are always counted, only accept `each`.
Conversions stay within the same dimension. You cannot convert a weight into a volume or a count - milliliters never become grams. Pick the base unit that matches how you actually measure the ingredient.
## Stock levels
The stock page at **Sales** > **Products** > **Stock** (`/billing/products/stock`) lists the current quantity on hand for every product and ingredient, at every location you can access. Each row shows whether it is a product or an ingredient, its name, the location, and the on-hand quantity in its base unit.
Click a row to open the underlying product or ingredient. Click **Add stock** to record a delivery, or use the row action to adjust a count.
## Stock movements
Every change to stock is written to an immutable ledger as a movement. A movement records the item, the location, a signed quantity in the base unit, the type of change, and an optional reason. There are four movement types:
* **Receive** - stock coming in, for example a delivery from a supplier. Quantity is positive and increases what is on hand. A reason is optional.
* **Sale** - stock going out because an order was fulfilled. Quantity is negative and is recorded automatically (see below). You never enter a sale by hand.
* **Adjustment** - a manual correction, for example after a stock count finds more or fewer units than expected. The quantity is signed, so you can add or remove, and a reason is required.
* **Waste** - stock written off, for example a spilled or spoiled item. Recorded through the same flow as an adjustment, with a required reason.
### Receiving stock
Click **Add stock** on the stock page, pick the product or ingredient, choose the location, and enter the quantity and unit. 1Club creates a `receive` movement and increases the on-hand level.
This is the quick way to book a single item. For a supplier delivery, record it as a [delivery](/sales/deliveries) instead: attach the invoice, review the lines it reads off the document, and post once so the stock and the expense are written together.
### Adjusting and writing off stock
Use the row action on the stock page to open the adjustment dialog. Enter a signed quantity (positive to add, negative to remove), pick **Adjustment** or **Waste** as the type, and give a reason. The reason is mandatory so the ledger stays auditable.
## How a sale decrements stock
When an order that contains a product is fulfilled, 1Club consumes stock for each line automatically, inside the same transaction that records the order. What gets consumed depends on how the product is set up:
* **A product with a recipe** (a cafe or bar item) is exploded into its ingredients, and each ingredient's stock is decremented by the amount the recipe calls for, multiplied by the quantity sold. A single informational `sale` movement is also recorded against the product itself, so the product's history reads "sold N" rather than a scattered list of ingredient draws.
* **A product with no recipe but a direct stock level** (packaged resale, like bottled water) has its own count decremented by one per unit sold.
* **A product that is neither stocked nor recipe-backed** consumes nothing - selling a service or a non-tracked good is a no-op for inventory.
Consumption is idempotent per order line, so re-processing an order never double-counts. If a sale would take stock below zero, it is still recorded and the on-hand level goes negative; 1Club logs a warning rather than blocking the sale, so the point of sale is never held up by a stock discrepancy.
## Recipes and ingredients for cafe and bar
A recipe defines what a sellable product is made of. Build one when a product is not resold as-is but assembled from ingredients - a latte, a cocktail, a protein shake.
On the product detail page, click **Recipe** to add ingredient lines. Each line names an ingredient, a quantity, and a unit, expressed as the amount needed for one unit of the product. For example, a latte recipe might be 18 g of coffee beans and 150 ml of milk.
Ingredients live at **Sales** > **Products** > **Ingredients** (`/billing/products/ingredients`). From there you can create ingredients, set each one's base unit, and open an ingredient to manage its stock. An ingredient cannot be deleted while any recipe still uses it.
### Sub-recipes
An ingredient can itself have a recipe, letting you model prepped components. A "house syrup" ingredient can be built from sugar and water, and any drink recipe that calls for house syrup will explode all the way down to sugar and water when it sells. Recipes can nest several levels deep, and 1Club guards against cycles and runaway depth.
## Availability
For a product built from ingredients, 1Club computes how many whole units you could sell right now at each location. It expands the recipe to its leaf ingredients and takes the lowest number the on-hand stock of any single ingredient allows. If you have milk for 40 lattes but beans for only 12, availability is 12.
The product detail page shows this availability alongside the product's recent movements, scoped to the location you have selected. Products with a direct stock level show that count; products that are neither stocked nor recipe-backed show as not tracked.
## Inventory movements report
The analytics report at **Analytics** > **Inventory** > **Movements** (`/analytics/inventory/movements`) is the full stock ledger across your organization. It lists every movement newest first, with its date, type, whether it targeted a product or an ingredient, the item name, the location, the signed quantity, a link to the originating order for sales, and the reason for manual changes.
Filter by movement type to focus on one kind of change - for example show only **waste** to review write-offs, or only **receive** to check deliveries.
## A typical pro-shop and cafe workflow
Enable the feature in **Settings** > **Sales** > **Inventory Management**.
Create ingredients for cafe and bar items (beans, milk, syrup) with the
right base unit, and make sure resale products like bottled water exist as
products.
For each cafe or bar product, open it and add a recipe listing the
ingredients and quantities it consumes per unit.
On the stock page, click **Add stock** to record what you have on hand at
each location.
Ring up products at the point of sale. Sales decrement ingredient or product
stock automatically.
Record waste as it happens, and use adjustments after a physical count to
reconcile on-hand levels. Review the movements report to audit the ledger.
## Related
* [Deliveries](/sales/deliveries) - booking supplier deliveries, with invoice scanning and the matching expense
* [Products](/sales/products) - the catalog items you sell and attach recipes to
* [Point of sale](/pos/point-of-sale) - where sales that decrement stock are rung up
* [Sales overview](/sales/overview) - how products, orders, and revenue fit together
* [Analytics overview](/analytics/overview) - where the inventory movements report lives
# Invoices
Source: https://docs.1club.ai/sales/invoices
Create, send, download, and settle invoice documents that group a contact's outstanding charges under one number, due date, and PDF - including pro-formas and converting them to invoices.
An invoice in 1Club is a receivable document that groups one or more [charges](/sales/charges) under a single number, with an issue date, a due date, line items, and a downloadable PDF. It is the artifact you send to a member or a corporate account when you want a formal record of what is owed. Owners and admins create and edit invoices, and the member or company receives them by email or as a PDF.
Invoices do not move money on their own. Money moves through [payments](/sales/payments), which the invoice flow can trigger but does not replace. Deleting or editing an invoice never touches a settled payment.
There are two kinds of document, chosen by **Document type**:
* An **Invoice** is the tax document. It counts towards your revenue and fiscal reporting.
* A **Pro-forma** is a request for payment issued before the invoice exists. It is not a tax document, it is excluded from fiscal reporting, and it numbers in its own series. See [Pro-formas](#pro-formas).
## When to use it
Use an invoice when someone needs a numbered document to pay against, or when you need a formal record of what was owed and paid. Numbering, currency, and the PDF layout all follow your organization's billing settings, so every document you send looks consistent, and every settled invoice has a real payment record behind it.
Typical uses:
* **One monthly document for a company** - a padel gym invoices a local employer once a month for its weekly staff-league court, billed to the company's **Account** so the PDF shows its legal name and VAT number.
* **A deposit before the revenue is yours** - the same gym sends a company a **Pro-forma** for a summer-league deposit, records the transfer when it lands, and then issues the tax invoice from it.
* **A package paid by bank transfer** - a Pilates studio invoices a 10-class reformer package, emails it with the PDF, and records the payment when the transfer clears.
* **Moving off a spreadsheet** - a jiu-jitsu gym imports last year's already-collected dues as invoices and records each payment on a manual method, dated when the money arrived.
See [Worked examples](#worked-examples) for the details of each.
## How invoices get created
Four paths:
1. **Manual single invoice.** Open a [charge](/sales/charges) and use its invoice action. The charge is wrapped in a one-line invoice and marked `isInvoiced = true` so a bill run won't sweep it again.
2. **Manual full-page create.** From **Sales** > **Finance** > **Invoices** click **Add** and choose **Invoice**, then pick a contact, optionally attach a billing account, and add line items.
3. **From a pro-forma.** Choose **Pro-forma** from the **Add** menu instead, then use **Issue invoice** on it later. See [Pro-formas](#pro-formas).
4. **Bill run.** A [bill run](/sales/bill-runs) sweeps every billable charge in a period and groups it into invoices by contact. This is the right tool when you have many charges to invoice at once. Bill runs only produce invoices, never pro-formas.
A new document gets a generated number from the template for its **own** document type, a `currency` inherited from the organization, and (for bill runs) a due date 30 days out. Manual documents use the invoice date and due date you enter.
### Creating a manual invoice
1. Go to **Sales** > **Finance** > **Invoices** and click **Add**, then choose **Invoice** as the document type.
2. Choose the **Contact** the invoice is billed to.
3. Optionally choose a billing **Account** (a household or company). The account must belong to your organization, and the contact you picked must be the account's primary contact or one of its members, otherwise the save is rejected.
4. Set the **Invoice date** and **Due date**.
5. Add **Line items**: each needs a description, quantity, and unit price. The total is the sum of the line items unless you enter an amount directly.
6. Save. The invoice number is generated automatically.
When a line item references a charge, saving the invoice flips that charge to `isInvoiced = true` and recalculates the invoice status from the linked charges.
## The invoices grid
**Sales** > **Finance** > **Invoices** shows every invoice for your organization with tabs **All**, **Pending**, **Paid**, and **Overdue**. Each row carries the invoice number, contact, billing account (when the invoice is billed to a household or company), invoice date, due date, total amount, the bill run ID if any, and status. Search matches invoice number, contact name, and contact email.
Row actions:
* **View** opens the invoice details.
* **Pay** opens the payment dialog (shown only for `pending` invoices).
* **Delete** removes the invoice, deletes its line items, and reverts every linked charge back to `isInvoiced = false` so the obligation can be re-invoiced.
Select multiple rows to reveal the **Delete** bulk action, which uses the same revert-linked-charges behavior for every selected invoice.
## Invoice details
The details page shows:
* **Invoice details** card with the invoice date, due date, total, status, and the bill run reference when present.
* **Account** and **Contact** cards on the right.
* **Line items**, each with description, quantity, unit price, total, and a link to the source charge.
* **Billing information** listing related bookings and memberships, each linking to the underlying record.
* **Amount breakdown** with subtotal, tax, and total.
* **Payment information** when the invoice has been associated with a Stripe payment, including the payment ID, a **View in Stripe Connect** link, and a **Refresh payment status** button that re-queries Stripe and updates the invoice if the state has changed.
The header carries these actions, conditional on status:
* **Add payment** (pending only): record a payment against the invoice. Uses the same dialog as the [Payments](/sales/payments) flow.
* **Send** (any status except paid or cancelled): emails the document to the contact with its PDF attached, optionally including a payment link. See [Sending a document](#sending-a-document).
* **Issue invoice** (pro-formas only, and only once): creates the tax invoice from this pro-forma. See [Pro-formas](#pro-formas).
* **Download**: fetch the invoice PDF.
* **Edit**: change invoice fields or line items.
* **Delete**: same behavior as the grid action.
## Invoice operations reference
| Operation | Where | What it does |
| - | - | - |
| **Add payment** | Invoice header / grid **Pay** | Records a payment (cash, transfer, terminal, or Stripe) against the invoice's charges and recalculates status. |
| **Send** | Invoice header **Send** | Emails the document with its PDF attached, optionally including a payment link. |
| **Issue invoice** | Pro-forma header | Issues a new invoice from a pro-forma, taking the next number in the invoice series. |
| **Download** | Invoice header **Download** | Streams a branded PDF named `invoice-.pdf`. |
| **Charge stored card** | API (`POST /v1/invoices/:id/charge`) | Charges a saved Stripe payment method for the invoice's outstanding balance. Not a button on the invoice page. |
| **Delete / batch delete** | Grid and header | Removes the document(s) and reverts linked charges to billable. |
There is no status-only **Mark as paid**. Closing a document goes through the
payment flow, so every settled invoice has a real payment record behind it
that you can reconcile and refund. For invoices already settled outside 1Club
(a historical migration, for example), record a payment on a **manual** method
such as Cash or Bank transfer, dated when the money actually arrived.
### Charging a stored payment method
There is no "Charge stored card" button on the invoice details page. In the admin UI you collect manually with **Add payment** (record what you took outside Stripe) or **Send** (email a payment link). Charging a saved card directly is available programmatically through the charge endpoint and is what the automated membership renewal engine uses under the hood - see [Bill runs](/sales/bill-runs).
When a stored card is charged, the amount is capped to the sum of the invoice's unpaid, non-void line items, not the raw invoice total. So if a future-period line was voided (for example after a membership pause), the charge collects only what is genuinely outstanding. On success, the linked charges flip to `paid`, any pending bookings on them become `confirmed`, and the invoice's Stripe integration data is updated.
### Sending a document
**Send** emails the document to the contact with its PDF attached. The dialog offers:
* **Send to** - the billing email on file. If the contact has none, the dialog says **No billing email on file** and the send is blocked.
* **Message** - an optional personal note added to the email.
* **Include the payment link** - attaches a hosted payment link so the customer can pay from the email.
Sending does not depend on Stripe. You can email an invoice or a pro-forma with its PDF whether or not a payment provider is connected, and whether or not you include a link - useful when the customer pays by bank transfer. The document records when it was last sent; if the send fails, that stamp is left untouched so you can retry safely.
### Downloading the PDF
**Download** renders the invoice as a PDF on demand. The document includes your organization's logo and address, the bill-to party (the billing account's legal name and VAT number when an account is attached, otherwise the contact), the invoice number and status, the line items with a tax column, the subtotal/tax/total breakdown, any notes, and a payment link when one exists. Nothing is stored; the PDF is generated fresh each time from the current invoice data.
### Document numbering
Each document type has its **own** template and its **own** counter, set per company under **Settings** > **Billing** > **General**. Most gyms have one company, so there is one pair of templates to fill in; if you trade through several, each keeps its own series and its own templates. See [Billing entities](/settings/billing-entities).
* **Invoice Numbering Template** - default `INV-[id]`.
* **Pro-forma Numbering Template** - for example `PRO-[id]`.
Placeholders are the same in both:
* `[id]` inserts the sequential counter for that series (1, 2, 3, ...). This is the placeholder the UI documents.
* `[year]`, `[month]`, and `[orgId]` are also supported, so a template like `INV-[year]-[id]` produces `INV-2026-42`.
The two series are independent, so a pro-forma never takes a number out of the invoice series. Issuing invoice 42 and pro-forma 7 on the same day is normal, and converting a pro-forma consumes the next **invoice** number, leaving the pro-forma series untouched.
Each counter belongs to the company issuing the document and is assigned under a lock scoped to that series, so numbers stay unique and gapless for that company even when two staff members create documents at the same moment. Two companies number independently, so both can issue a document numbered 1 - give each a distinct prefix.
## Invoice statuses
Invoice status is normally derived from the linked charges:
| Linked charges | Invoice status |
| - | - |
| All `paid` | `paid` |
| Some `paid` or `partially_paid` | `partially_paid` |
| Otherwise, due date in the future (or none) | `pending` |
| Otherwise, due date in the past | `overdue` |
Charges in the `overdue` state count as pending for this derivation. `cancelled` is sticky - once an invoice is cancelled it stays that way. Some Stripe webhook paths set `failed` directly when a charge fails, so you may briefly see `failed` before the next status recalculation.
### Overdue and due-soon views
Two dedicated lookups back reminders and dashboards:
* **Overdue** returns invoices whose stored status is still `pending` and whose due date is in the past.
* **Due soon** returns `pending` invoices whose due date falls within the next N days (7 by default).
Both are stricter than the grid's **Overdue** tab, which simply filters on the stored `overdue` status. An invoice only lands in the grid's Overdue tab after status recalculation flips it; the overdue lookup catches pending invoices the moment their due date passes.
## Pro-formas
A pro-forma is a request for payment you issue **before** the tax invoice exists. Use one when the customer needs a document to pay against but you should not book the revenue yet: a corporate client whose finance team pays on a document, a deposit against a tournament, or a quote the customer has not committed to.
A pro-forma looks and behaves like an invoice - same line items, same contact and billing account, same branded PDF, same **Send** - with three deliberate differences:
* It is **not a tax document**, so it is excluded from fiscal reporting.
* It **numbers in its own series**, so it never takes a number out of the invoice series. See [Document numbering](#document-numbering).
* It can be **converted** to an invoice, once, with **Issue invoice**.
### Creating a pro-forma
1. Go to **Sales** > **Finance** > **Invoices** and click **Add**.
2. Choose **Pro-forma** as the document type.
3. Fill in the contact, dates, and line items exactly as you would for an invoice.
4. Save. The number comes from your pro-forma template.
To see existing pro-formas, switch the **Document type** control in the grid toolbar. The grid shows one type at a time, so invoices and pro-formas never mix in the same list or in the tab counts.
### Issuing an invoice from a pro-forma
When the customer commits, open the pro-forma and click **Issue invoice**. 1Club confirms:
> This issues a new invoice from this pro-forma, taking the next number in the invoice series. The pro-forma is kept as sent.
What happens:
* A **new invoice** is created, copying the pro-forma's contact, amounts, line items, and notes, and taking the next number in the invoice series.
* The pro-forma is **kept**, linked to the invoice it produced. It is not deleted, so the document you already sent the customer stays valid and auditable.
* The link is one-way and permanent. A pro-forma can be converted **once**; the action is not offered again afterwards.
Neither side of a conversion can be deleted. Once a pro-forma has produced an
invoice, deleting either document is refused - the pair is the audit trail for
how the revenue came to be recognised. If the whole thing was a mistake, void
or credit the invoice through the normal flow instead.
### When to use which
| Situation | Document |
| - | - |
| Member owes you for a booking or membership | Invoice |
| Corporate client's finance team needs a document to pay | Pro-forma, then **Issue invoice** when payment is agreed |
| Deposit or advance before a tournament | Pro-forma |
| Quote the customer has not committed to | Pro-forma |
| Bulk monthly billing across many members | Invoice, via a [bill run](/sales/bill-runs) |
## Collecting on an invoice
You have two options from the invoice detail page:
* **Send** the document with a payment link included. The contact pays through a hosted payment page. On success, the linked charges flip to `paid`, any pending bookings on those charges become `confirmed`, and the invoice's payment data is updated.
* **Add payment** to record what you collected outside the provider (cash, bank transfer, manual card terminal). This creates a payment row, links it to the invoice's charges, and updates status.
The invoice's totals are derived from its line items, which point at charge IDs. Editing the underlying charge prices after issuing the invoice will not retroactively change the invoice total.
### You cannot collect more than the document owes
A payment is checked against what is **still** owed, not against the document total, so a second payment cannot overshoot. If a colleague has already taken 400 on a 1,000 invoice, the most you can add is 600.
This also holds when two staff members collect at the same moment. The balance is claimed the instant one of them starts, so the second attempt is refused with the balance that is genuinely left rather than both succeeding and putting 2,000 against a 1,000 document. An attempt that is started and abandoned (the browser is closed mid-payment, for example) releases its claim after a few minutes, so a stalled attempt never locks a document permanently.
## Worked examples
**Padel club - corporate account, one document.** A local employer books a weekly padel court for its staff league. You attach every court charge to the company's billing **Account**, then send one invoice per month. Because the invoice is billed to the account, the PDF shows the company's legal name and VAT number as the bill-to party, and the staff member's name appears as a subline.
**Jiu-jitsu gym - historical migration.** You are moving off a spreadsheet and importing last year's already-collected membership dues. Create the invoices, then record a payment on each with a **manual** method (Cash or Bank transfer) dated when the money actually arrived. They land in the **Paid** tab with a real payment record behind them, and no phantom card charges are created.
**Padel club - corporate deposit up front.** A company wants to reserve four courts for a summer league and their finance team needs a document before releasing funds. Issue a **Pro-forma** for the deposit and **Send** it with the PDF attached. When the transfer lands, record the payment, then use **Issue invoice** to produce the tax invoice for their books. The pro-forma stays on file as the document they originally paid against.
**Pilates studio - reformer package sold on account.** A member buys a 10-class reformer package but wants to pay by bank transfer. Create a single manual invoice with one line item for the package, email it with **Send** or hand them the **Download** PDF, and when the transfer clears use **Add payment** to record it. The package charge flips to `paid` and the invoice moves to the **Paid** tab.
**Climbing gym - chasing late payers.** At month end you pull the due-soon list to nudge members whose dues are due this week, and the overdue list to follow up on ones that have already lapsed. Members who paid by link show up as `paid` automatically; the rest stay `pending` until you record a payment.
## Tips & best practices
* Every settled invoice needs a payment record. There is no status-only shortcut, so for money collected outside 1Club record it on a manual method rather than looking for a **Mark as paid** button.
* Attach a billing **Account** whenever a company or household is the payer, so the PDF bills the right legal entity and VAT number.
* Set both numbering templates before you issue your first document. Including `[year]` keeps numbering readable across financial years, and the two series are independent so you can format them differently.
* Reach for a **pro-forma** when the customer needs a document but the revenue is not yours yet. Converting later is one click and keeps both documents.
* To re-invoice an obligation, delete the invoice rather than editing it. Deletion reverts the linked charges to billable; editing prices does not change what the charge owes.
* Use the **Refresh payment status** button on the invoice details page if a member says they paid a Stripe link but the invoice still reads `pending`.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| Invoice save rejected with "Invoice contact must belong to the selected account" | The chosen contact is neither the account's primary contact nor a member. | Pick a contact tied to that account, or leave the account blank. |
| A charge won't appear in a new bill run | It is already on an invoice (`isInvoiced = true`). | Delete the invoice holding it to revert it to billable. |
| Invoice still shows `pending` after the member paid a link | Status recalculation or the Stripe webhook hasn't landed yet. | Open the invoice and click **Refresh payment status**. |
| Editing charge prices didn't change the invoice total | Totals are frozen from the line items at issue time. | Delete and re-create the invoice, or edit the invoice line items directly. |
| A pending invoice isn't in the **Overdue** tab yet, though its due date passed | The tab filters on the stored `overdue` status, which flips on recalculation. | Use the overdue lookup, which catches pending invoices with a past due date immediately. |
| Two invoices almost got the same number | Concurrent creation. | No action needed - numbering retries on collision and stays unique for the issuing company. |
| I can't find a pro-forma I created | The grid shows one document type at a time. | Switch the **Document type** control in the toolbar to **Pro-forma**. |
| **Issue invoice** is missing on a pro-forma | It has already been converted. A pro-forma converts once. | Open the invoice it produced; the two are linked. |
| Deleting a pro-forma or invoice is refused | The document is one side of a conversion, which cannot be broken. | Leave both on file. To reverse the revenue, credit or void the invoice instead. |
| I'm looking for **Mark as paid** and it isn't there | It was removed - settling goes through the payment flow. | Record a payment on a manual method (Cash, Bank transfer) for money taken outside 1Club. |
| A pro-forma appears in a revenue figure | It should not. Pro-formas are excluded from fiscal reporting. | Check whether the number you are reading is the converted invoice rather than the pro-forma. |
## Related
* [Charges](/sales/charges)
* [Payments](/sales/payments)
* [Bill runs](/sales/bill-runs)
* [Export](/settings/export) - bundle invoices for QuickBooks.
# Memberships
Source: https://docs.1club.ai/sales/memberships
The org-wide membership grid. Sell, pause, resume, renew, and cancel memberships, and track how members consume their usage allowance.
A membership is an instance of a [plan](/sales/plans) assigned to a specific contact. The plan defines price, billing frequency, scope, and use limits; the membership snapshots those values at the moment of sale and tracks that member's usage and billing schedule from then on. The front desk sells memberships, managers pause, renew, and cancel them, and members use them to book and check in.
## When to use it
The Memberships page is where you manage the full lifecycle of every membership in the org, and see at a glance how many sessions each capped one has left. Editing the plan later does not rewrite memberships already sold, so each member keeps the terms they bought. The same contact can hold more than one membership at the same time (for example a monthly main plan plus a 10-class add-on pack).
Typical uses:
* **An unlimited monthly plan** - a yoga studio's members book as many classes as they like, and each renewal charges the card on file.
* **A class pack that counts down** - a reformer studio sells a 10-class pack, each booking takes one use, and a cancelled booking gives it back.
* **Court hours** - a padel gym sells 20 court hours a month, and a 90-minute booking takes 1.5 hours off the allowance.
* **A hold for an injury or a holiday** - a jiu-jitsu member tears a ligament, so you pause the membership and they lose none of the time they paid for.
See [Worked examples](#worked-examples) for how each of these is set up.
A [season pass](/schedule/season-passes) appears here too, sold from the schedule rather than from this page. It behaves differently in three places, each noted below: its allowance, its cancel dialog, and its renewal.
A membership and a booking share the same payment, pricing, and usage-consumption code. A membership consumed at check-in behaves exactly as it does when consumed through a booking - there is no separate "membership check-in" logic. See [Check-in models](/reference/check-in-models).
## Opening the page
The Memberships page lists every membership in the org, regardless of status, with status tabs, a billing-frequency filter, and search by member or plan.
Go to **Sales** > **Memberships** (`/billing/plans/memberships`). Memberships sits between **Promotions** and **Orders** in the Sales section, next to the [Plans](/sales/plans) they are sold from. Old links to `/members/memberships` still open this page. You need permission to view memberships to see the tab.
## Tabs and counts
Tabs filter by status, with server-side counts in each badge:
* **All** - everything.
* **Active** - the default tab. Currently valid memberships.
* **Paused** - billing and access on hold.
* **Pending** - start date is in the future.
* **Used** - all available sessions consumed but the membership has not been cancelled.
* **Cancelled** - cancelled either immediately or at end of period.
* **Expired** - past the end date without renewal.
A membership whose renewal charge is declined can be moved to an internal "pending payment" state by the billing engine until the payment settles. It is not one of the tabs above; it reappears as **Active** once the outstanding charge is paid.
The frequency filter in the drawer narrows by billing frequency: **recurring** (any recurring cadence), **one-time**, **weekly**, **monthly**, **quarterly**, **annually**, or **custom**.
## Grid columns
Each row shows:
* **Contact** - the member who holds the membership.
* **Plan** - the plan the membership was sold under.
* **Price** - the snapshotted price, with the billing frequency underneath.
* **Auto renew** - Yes/No (shown as "-" for one-time memberships).
* **Usage** - the allowance bar (see [How usage is counted](#how-usage-is-counted)). Shows **Unlimited** when the plan has no use cap.
* **Period** - the current period's start and end dates ("Ongoing" for recurring memberships with no fixed end).
* **Status** - the status chip.
* **Payment status** - whether the membership's charges are paid, pending, or overdue.
Click a row to open the membership detail page. The row action menu exposes **View**, **Edit**, and the lifecycle actions that apply to the current status.
## Adding a membership
Click **Add Membership**. The dialog lets you:
1. **Pick the contact.** On an existing membership this field is locked - a membership can't be reassigned to a different member.
2. **Pick the plan.** Fields auto-populate from the plan: price, billing frequency, signup fee, max uses, and scope (which classes, areas, or instructors the membership covers).
3. **Adjust the start date.** The end date and renewal date are computed from the billing frequency, but you can override them.
4. **Set auto-renew.** Only available for recurring frequencies; defaults from the plan.
5. **Apply a discount (optional).** Enter a promotion code or pick a discount in the price section. The discount lowers the saved price, so recurring renewals inherit the reduced amount going forward. Only discount-type promotions apply here, not wallet vouchers.
6. **Take payment (optional).** If the plan requires payment upfront, the payment section opens automatically. You can still save without a payment: the dialog notes that the plan is normally paid when sold, and the amount stays as an outstanding balance to collect from the member's profile. The signup fee is charged as a separate charge and is not reduced by a discount.
### Editing a membership
Editing an existing membership uses the same dialog. Because a plan can be edited after a membership was sold, the edit form also exposes the snapshot fields - **renewal date**, **billing frequency**, **max uses**, **uses remaining**, **session unit**, and **scope** - so you can repair a stale snapshot directly on the membership without touching the plan.
To enter or leave the paused state, use the **Pause** and **Resume** actions, not the edit dialog. Pausing has side effects (voiding scheduled charges, shifting the renewal clock) that the generic edit form deliberately does not perform.
## The lifecycle
Memberships move through the states below. The actions available on each row reflect the current status.
### Pause and resume
Use **Pause** (available only from **Active**) to put a membership on hold. Billing stops, access is blocked, and any pending future recurrence charge is voided so the member is not billed during the freeze. The renewal clock stops too.
The pause dialog offers:
* **Schedule an auto-resume date** - toggle it on to set a **Resume On** date; the membership resumes automatically on that date. Leave it off for an indefinite pause that requires manual resumption.
* **Reason (optional)** - a free-text note (e.g. vacation, injury, medical leave), up to 500 characters.
When you **Resume** (available only from **Paused**), the end date and renewal date shift forward by the pause duration so the member recovers exactly the time they paid for. The resume dialog previews the new dates before you confirm. Resuming does not itself create a new charge; the billing engine seeds the next renewal charge from the shifted renewal date once it falls due.
### Renew
**Renew** appends a new billing period. It is available for active, pending, used, and expired memberships, but not for paused, cancelled, or one-time ones.
* If the membership is still within its current period, the new period stacks on top of the existing end/renewal date.
* If it has expired or its end date is in the past, the new period starts today.
The renew dialog previews the resulting **New billing period** dates and the price. You can renew now and leave the charge pending for later settlement. If the plan requires payment upfront, the dialog notes it, and without a payment the new period stays as an outstanding balance. One-time memberships show a warning and cannot be renewed - create a new membership instead.
### Cancel
**Cancel** is available for any membership that is not already cancelled or expired. What the dialog offers depends on auto-renew:
* **Auto-renewing memberships** get a choice of two modes:
* **Cancel at end of period** - the membership stays active until the current period ends, then will not renew. The row shows "Cancelling on ". This is the default.
* **Cancel immediately** - the status flips to cancelled, the end date is set to today, and access is revoked at end of day.
* **Non-renewing memberships** (including one-time packs) are cancelled immediately - there is no future period to cancel against.
* **Season passes** open their own dialog instead. Ending one has to release the courts and close the pass together, so the generic cancel is refused for them. See [Cancelling a season pass](/schedule/season-passes#cancelling-a-season-pass).
Either mode voids any pending future recurrence charges so the member is not billed again. A membership already scheduled to cancel at period end can be flipped to immediate cancellation from the same dialog.
### Delete
Deletion is only available to admins with the delete permission, and only when the membership has **no** charges. To remove a membership that has been paid against, cancel it instead so financial history is preserved. Attempting to delete a membership with charges is blocked with a clear error.
## How usage is counted
A membership tracks usage only when the plan sets a use cap (**Max Uses**). Uncapped memberships show **Unlimited** and are never debited. The usage bar reads as consumed / total in the plan's **session unit**:
* **Bookings** - each session counts as one unit.
* **Hours** - each session debits its duration in hours, so a 90-minute court booking debits 1.5 hours.
When and how a use is debited:
* **Booked in advance** - the use is debited when the booking is created. Cancelling the booking restores the use (never above the max), and if the membership had flipped to **Used**, restoring a use flips it back to **Active**.
* **Consumed at check-in** - a member checked in directly against the membership (no prior booking) debits a use at check-in. When the last use is consumed the membership flips to **Used**.
* **No double-debit** - a check-in linked to a booking does not debit again, because the booking already paid for the session.
A [season pass](/schedule/season-passes) reads its allowance the other way round. The sale spends the season's hours up front on the weeks it books, so a freshly sold pass shows its allowance fully committed here (`27 / 27 hours`), and `0 of 27 hours left` on member-facing surfaces. It rests there for the whole season, is never marked **Used** at zero, and the counter rises only when a cancelled week credits a makeup hour back. Its two real ends are the season's last day and cancelling the pass.
An exhausted or no-longer-active membership still honors bookings that were created against it while it was active. The booking debited a use at booking time, so check-in for that specific booking goes through even if the membership has since hit zero uses or expired. See [Attendance](/schedule/attendance) for the walk-in versus pre-booked distinction.
## Membership scope
Memberships can be scoped to specific **classes**, **areas**, or **instructors** (the `validFor` snapshot). The scope is set on the plan and copied to the membership at sale. When a booking is being created, only memberships whose scope covers the chosen class, area, or instructor are offered as a payment option.
If the plan restricts bookable hours (for example an off-peak plan valid 20:00-23:00 on weekdays), a booking that falls outside that window - or straddles its boundary - is not covered by the membership.
You can edit the scope on an existing membership if the plan's snapshot is stale.
## Auto-renew
Recurring memberships can auto-renew. When the period ends, a renewal charge is generated and collected from the member's saved payment method. Auto-renew is only available for active, used, or pending memberships, and defaults to the plan's default-auto-renewal setting. If a renewal charge is declined, the membership is deactivated until the payment settles, then reactivates and resumes its billing cycle from the failed period.
A [season pass](/schedule/season-passes) never auto-renews. It is paid once for a fixed period, and the next season is a fresh sale at the new season's prices.
## Worked examples
### Unlimited yoga membership (Pilates/Yoga)
A studio sells an "Unlimited Monthly Yoga" plan.
* **Billing frequency** - monthly, **auto-renew** on.
* **Max Uses** - empty, so the usage column shows **Unlimited** and no session is ever debited.
* **Scope** - classes, limited to the yoga class type.
The member books as many yoga classes as they like each month. Each renewal charges the card on file. If they go on holiday, **Pause** with an auto-resume date freezes billing and pushes the renewal date out by the freeze length.
### 10-class pilates reformer pack (Pilates/Yoga)
A reformer studio sells a "10-Class Reformer Pack".
* **Billing frequency** - one-time, **auto-renew** off.
* **Max Uses** - 10, **session unit** Bookings.
* **Scope** - classes, limited to the reformer class type.
Each reformer booking debits one use; the usage bar counts down 0/10 to 10/10. When the tenth session is consumed the membership moves to the **Used** tab. Because it is one-time, **Renew** is not offered - sell the member a fresh pack. If a booking is cancelled, the use is returned and a **Used** pack flips back to **Active**.
### Racket-club court hours (Racket sports)
A padel club sells "20 Court Hours Monthly".
* **Billing frequency** - monthly, **auto-renew** on.
* **Max Uses** - 20, **session unit** Hours.
* **Scope** - areas, limited to the court area type.
A 90-minute court booking debits 1.5 hours from the allowance, a 60-minute booking debits 1. When the member runs out of hours before the month ends, the membership shows **Used**; renewing (or the auto-renewal at period end) resets the allowance for the new period.
### Membership hold for an injured athlete (Martial arts)
A jiu-jitsu gym runs an "Unlimited Monthly" plan. A member tears a ligament and needs six weeks off.
* Open the member's membership and click **Pause**.
* Enter a **Reason** of "injury" and either schedule the resume date six weeks out or leave it indefinite.
* Billing stops and the scheduled renewal charge is voided. On resume, the renewal date shifts forward by the exact pause length, so the member loses none of the time they paid for.
## Tips & best practices
* **Let the plan carry the defaults.** Configure price, use limits, scope, and default auto-renewal on the [plan](/sales/plans) so every sale snapshots consistent terms. Only override on the membership for genuine exceptions.
* **Prefer cancel over delete.** Delete is blocked once any charge exists, and cancelling preserves the financial trail. Reserve delete for memberships created in error before any charge.
* **Use pause, not cancel, for temporary absences.** Pause preserves paid-for time and the member's history; cancelling and re-selling does not.
* **Schedule the resume date when you know it.** A scheduled auto-resume keeps a paused membership showing a coherent renewal date and reactivates without staff action.
* **Watch the Used tab for one-time packs.** Members on the **Used** tab have consumed their allowance and (for one-time packs) need a new membership, not a renewal.
## Troubleshooting
**Issue**: A membership I sold shows an outstanding balance.
**Cause**: The plan has "require payment upfront" on, and the membership was saved without a payment. That rule binds members buying for themselves; staff can sell on credit, and the amount stays owed.
**Fix**: Collect it from the member's profile, or next time add a payment method in the payment section of the Add Membership dialog.
**Issue**: I can't find the **Renew** action on a membership.
**Cause**: Renew is hidden for paused, cancelled, and one-time memberships.
**Fix**: Resume a paused membership first. For a one-time pack, create a new membership. For cancelled memberships, sell a fresh one.
**Issue**: A member's booking went through even though their pack shows zero uses.
**Cause**: The booking was created while the membership still had a use; the use was debited at booking time.
**Fix**: This is expected. The pre-booked session is already paid for and check-in proceeds. New bookings are blocked once the allowance is exhausted.
**Issue**: I paused a membership but the member was still charged.
**Cause**: The charge was raised before the pause, or the membership was edited rather than paused.
**Fix**: Always use the **Pause** action - it voids pending recurrence charges. If a charge already settled, handle it through the refund flow in [Charges](/sales/charges).
**Issue**: The **Delete** action is missing or fails.
**Cause**: Either you lack the delete permission, or the membership has charges.
**Fix**: Cancel the membership instead to retain its financial records. Only charge-free memberships can be hard-deleted.
**Issue**: A membership isn't offered as a payment option when booking a class.
**Cause**: The membership's scope doesn't cover that class, area, or instructor - or the booking falls outside the plan's allowed hours.
**Fix**: Open the membership, check the scope section, and widen it if the snapshot is stale. Confirm the booking time falls inside any plan operating-hours window.
## Related
* [Plans](/sales/plans) - the plan templates memberships are built from.
* [Member profile](/members/member-profiles) - per-member memberships, on the member's Memberships tab.
* [Attendance](/schedule/attendance) - check-ins, usage counting, and the pre-booked-versus-walk-in distinction.
* [Charges](/sales/charges) - the charges memberships generate, and how they are paid.
* [Check-in models](/reference/check-in-models) - how memberships satisfy payment and access at the door.
# Orders
Source: https://docs.1club.ai/sales/orders
Understand orders as multi-line purchase containers - what an order holds, its draft, completed, and cancelled lifecycle, how you complete or cancel one, and how orders relate to charges, invoices, and payments.
An order is a single purchase container that groups one or more line items a member is buying together - memberships, products, court and class bookings, event tickets, and packages. Each order carries its own status, an aggregate payment status, and links out to the bookings, memberships, and charges it creates when it is completed. Orders come from the front desk at the point of sale, from members checking out in the portal, and from booking flows; owners, managers, and front-desk staff then review, complete, and cancel them.
## When to use it
The **Orders** tab is the record of every basket your organization has assembled. It shows what a member bought together on one receipt, whether the money is in, and lets you finish a draft or reverse a finished purchase with everything it created.
Typical uses:
* **A court plus a racket rental** - a padel member books an hour on court and rents a racket at the desk, paid by card in one order, with each line linked to the booking and charge it produced.
* **A drop-in who changes their mind** - a jiu-jitsu visitor pays cash for a mat session and a bottle of water, then decides not to train, so you cancel the completed order to void its charges and refund the cash from the payment record.
* **A membership with a joining fee** - a Pilates client joins a monthly reformer plan, and the order carries the membership and its signup fee as separate lines.
See [Worked examples](#worked-examples) for more.
## What an order contains
An order is the parent; its **line items** are the children. Each line has a type that decides what completing the order creates:
* **Booking** - A court, class, instructor, or event-ticket reservation. Becomes a booking record with its own charge.
* **Membership** - A plan purchase. Becomes a membership, and its signup fee (when the plan charges one) is carried as a separate **membership signup fee** line.
* **Product** - A retail or food-and-drink item sold by quantity at a unit price (for example a drink, towel, or racket rental).
* **Package** - A bundle sold as one item; it is expanded into its component membership and product lines at order-create time.
* **Discount** - A negative line written when a promotion is applied at completion, so the line-item total matches what the member actually pays.
An order also carries a **member** (the contact it belongs to), an optional **club**, and an optional order-level **note** (for example kitchen instructions or a buzzer/pager number for a food order). A product-only order can be anonymous, but any order containing a booking or membership line must have a member attached.
## How orders relate to charges, invoices, and payments
An order is the top of the money trail:
* **Order -> charges** - Completing an order creates one charge per billable line (a booking charge, a membership charge, a product charge). The order's payment status is the roll-up of these charges' statuses. See [Charges](/sales/charges).
* **Charges -> invoices** - Charges can be grouped onto an invoice for the member. The invoice is the document; the order is the basket that produced the underlying charges. See [Invoices](/sales/invoices).
* **Charges -> payments** - A payment is recorded against one or more charges to settle them. Completing an order records a payment against the order's charges with the method you selected. See [Payments](/sales/payments).
The order detail page surfaces these links directly. Each line's **Linked to** column deep-links to the **booking**, **membership**, and **charge** it produced, and when an order has been synced to an external system (for example a POS for fiscalisation) an **Integrations** card shows the linked system and sync time.
## The order list
Use **Sales** > **Orders** to review, complete, and cancel every purchase your organization has taken.
Open **Sales** > **Orders**. The list shows every order with these columns:
* **#** - The order number.
* **Member** - The contact the order belongs to (blank for anonymous product-only sales).
* **Status** - Draft, Completed, or Cancelled.
* **Payment status** - The rolled-up payment state across the order's charges (hidden while the order is still a draft with no live charges).
* **Items** - How many line items the order holds.
* **Total** - The order total, shown tax-exclusive in the admin grid.
* **Created at** - When the order was opened.
Tabs across the top filter by status: **All**, **Draft**, **Completed**, and **Cancelled**, each with a live count. Sort defaults to newest first. The row action menu offers **View** and, unless the order is already cancelled, **Cancel**. The **Point of sale** button in the top right jumps to the POS to start a new sale.
Orders are created from the point of sale, from member checkout in the portal, or from a booking flow - not from an "Add order" button on this tab. The Orders tab is where you review and resolve those orders after they exist.
## Order status and the lifecycle
There are exactly three order statuses:
* **Draft** - The basket is assembled but not yet paid or fulfilled. Nothing has been booked or charged. Draft orders can still be edited (their line items, member, club, and note replaced).
* **Completed** - The order has been executed: its lines are now real bookings, memberships, and charges, and payment has been recorded (or left pending for pay-on-arrival).
* **Cancelled** - The order has been voided. For a draft this is just a status change; for a completed order it reverses everything the order created.
The allowed transitions are:
* **Draft -> Completed** when you complete (execute) the order.
* **Draft -> Cancelled** when you discard a draft.
* **Completed -> Cancelled** when you reverse a finished order.
A cancelled order is terminal - it cannot be re-opened, and attempting to cancel it again is rejected.
## Payment status vs order status
The **payment status** is separate from the order status and is rolled up from the order's charges:
* **Paid** - Every live charge on the order is paid.
* **Partially paid** - Some charges are paid (or one is marked partially paid) while others are still outstanding.
* **Refunded** - At least one charge has been refunded.
* **Pending** - Charges exist but none are settled yet.
* **Hidden** - No live charges yet (a draft, or a fully voided order), so the chip is not shown rather than reading "pending" for something that was never billed.
Voided charges are ignored in this roll-up, so a voided line does not drag an otherwise-paid order into a misleading state.
## Completing a draft order
Completing (executing) a draft is what turns intent into records.
1. Open the draft from **Sales** > **Orders**.
2. Click **Complete order** in the top right.
3. In the **Complete order** dialog, pick a **payment method** (required when the order total is greater than zero). Add wallet credit as a secondary tender if you want a split - see [Payments](/sales/payments).
4. Confirm. The order is executed: each line becomes a booking, membership, or product charge, and payment is recorded against the resulting charges with the method you chose.
The in-app help reads: "Add a payment method and complete the order to create bookings, memberships, and charges." A zero-total order (for example a booking fully covered by a membership) completes without a payment method.
If you choose pay-on-arrival, the booking is confirmed but its charge is left **pending** - no wallet debit and no card charge. Collect payment on site later. The order still moves to **Completed**; only its payment status stays open.
## Cancelling an order
1. Open the order and click **Cancel**, or use the **Cancel** row action in the list.
2. Confirm in the **Cancel order** dialog ("Are you sure you want to cancel this order? This action cannot be undone.").
What cancelling does depends on the order's state:
* **Draft** - The status flips to **Cancelled**. Nothing else happens because nothing was created.
* **Completed** - Each booking line is cancelled through the standard booking-cancellation flow, each membership line is cancelled immediately, and every related charge is voided. Then the order status becomes **Cancelled**. This reversal is idempotent, so an already-cancelled booking or already-voided charge is skipped rather than erroring.
Cancelling a completed order does not by itself hand cash back. A booking that was paid may generate a wallet-credit refund through the cancellation flow (subject to your cancellation policy), and voiding the charge stops it counting toward revenue. To reverse a specific card or cash payment, use the refund action on that payment - see [Payments](/sales/payments).
## Editing a draft
Only **draft** orders can be edited. Editing replaces the order's line items and, optionally, its member, club, and note. Once an order is completed it is immutable - you cancel and rebuild rather than edit. Attempting to edit a non-draft order is rejected with "Only draft orders can be updated".
## Worked examples
### Padel court plus racket rental in one sale
A member books an hour on court 2 and rents a racket at the desk.
* The order holds two lines: a **booking** (the court) and a **product** (racket rental).
* Complete it with **Card** as the payment method.
* Completion creates a booking charge and a product charge; the order payment status becomes **Paid**.
* The booking line's **Linked to** column points at the new booking and its charge.
### Jiu-jitsu drop-in taken at the front desk
A visitor pays for a single mat session and grabs a bottle of water.
* Built at the point of sale as a **product**-only or booking-plus-product order.
* Take **Cash**; the order completes and its charges are marked paid.
* If they later decide not to train, cancel the completed order to void the charges, then refund the cash from the payment record.
### Pilates membership with a signup fee
A new client joins on a monthly reformer plan that charges a joining fee.
* The order carries a **membership** line plus a **membership signup fee** line.
* Complete it with the client's chosen method. Completion creates the membership and its charges.
* The **Linked to** column takes you to the new membership under [Memberships](/sales/memberships).
### Climbing day pass sold with a promotion
A walk-in buys a day pass and you apply a discount code at checkout.
* The order holds the **booking** (or membership) line; applying the promotion at completion writes a negative **discount** line so the order total matches what the climber pays.
* The discount line references the promotion it came from. See [Promotions](/sales/promotions).
### Class pack sold as a package
A yoga studio sells a 10-class pass.
* The order holds a **package** line, which is expanded into its component lines when the order is created.
* Complete it as one basket; the components fulfil together. See [Packages](/sales/packages).
## Tips & best practices
* **Read status as two facts, not one.** "Completed" says the order was fulfilled; the payment-status chip says whether the money is in. A completed order can still be **Pending** if you took it pay-on-arrival.
* **Clear your Draft tab.** Drafts are abandoned or in-progress baskets. Complete the real ones and discard the stale ones so the tab reflects live work.
* **Cancel, do not edit, a finished order.** Completed orders are immutable by design. If a purchase is wrong after completion, cancel it (which reverses the bookings, memberships, and charges) and build a fresh order.
* **Use the note field for fulfilment context.** Kitchen instructions or a pager number on a food-and-drink order ride along on the order and flow to a connected POS.
* **Follow the Linked to trail for disputes.** When a member queries a charge, open the order and jump straight to the booking, membership, or charge from the item grid rather than searching each list.
## Troubleshooting
**Issue**: The **Complete order** button is missing.
**Solution**: It only shows for **draft** orders. A completed or cancelled order has nothing left to execute.
**Issue**: Completing the order fails with a message about a required payment method.
**Solution**: The order total is greater than zero, so the **Complete order** dialog needs a payment method before it can record payment. Pick one (or apply wallet credit / pay-on-arrival). A zero-total order completes without one.
**Issue**: I cannot edit an order - it says only draft orders can be updated.
**Solution**: The order is already completed or cancelled. Cancel it and build a new draft instead of editing.
**Issue**: A completed order shows **Pending** payment status.
**Solution**: The order was taken pay-on-arrival, so its charges are still open. Record the payment against those charges to move it to **Paid** - see [Payments](/sales/payments).
**Issue**: I cancelled a completed order but the member still expects their money back.
**Solution**: Cancelling voids the charges and may credit wallet through the cancellation flow, but it does not automatically reverse a specific card or cash payment. Refund the payment from its record on the [Payments](/sales/payments) tab.
**Issue**: The payment-status chip is missing on a draft.
**Solution**: That is expected. A draft has no live charges yet, so there is nothing to roll up and the chip stays hidden until the order is completed.
## Related
* [Payments](/sales/payments) - Record payments, take split payments, and issue refunds against an order's charges.
* [Charges](/sales/charges) - The individual charges an order creates.
* [Invoices](/sales/invoices) - Group charges into a member invoice.
* [Packages](/sales/packages) - Bundles that expand into order line items.
* [Plans](/sales/plans) - Membership plans sold as membership lines.
* [Memberships](/sales/memberships) - The memberships an order creates.
* [Bookings](/schedule/bookings) - The bookings an order creates.
* [Promotions](/sales/promotions) - Discounts applied to an order at completion.
# Sales overview
Source: https://docs.1club.ai/sales/overview
How the Sales section is organized - Plans, Products, Promotions, Memberships, Orders, Finance, and Cash - and how plans, orders, charges, invoices, and payments fit together.
**Sales** is where you set up what your gym sells and follow the money it brings in. It replaces the old **Revenue** menu and also holds **Promotions** (formerly under **Marketing**) and **Memberships** (formerly under **Members**). Owners and managers set up the catalog here, and front-desk staff and bookkeepers use it to review orders, chase what is owed, and reconcile the till.
The register itself is a separate rail item, [Point of Sale](/pos/point-of-sale). Sales is where you manage what the register sells and review what it took.
## The Sales section
Go to **Sales** in the left rail (`/sales`). It opens on the first tab you can access, usually **Plans**. Each tab appears only when your role has permission for it.
| Tab | What it holds | When to use it |
| - | - | - |
| [Plans](/sales/plans) | Membership and pass templates | Set up pricing tiers, recurrence rules, and usage limits. |
| [Products](/sales/products) | The catalog of sellable items, with sub-tabs for packages, stock, and rentals | Define retail and add-on items sold at the point of sale and on signup forms. |
| [Promotions](/sales/promotions) | Vouchers and discounts | Run trials, intro offers, and promo codes. |
| [Memberships](/sales/memberships) | Every membership sold from your plans | Pause, renew, cancel, and track usage. |
| [Orders](/sales/orders) | Every basket bought at the desk, in the portal, or through a booking | Review, complete, or cancel a purchase and everything it created. |
| **Finance** | Money owed and collected, as tabs (see below) | Audit charges, collect payments, issue invoices, and manage wallets. |
| [Cash](/sales/cash) | The cash drawer and expenses | Count the till and record what your gym spends. Only shown when the cash module is on. |
### Products sub-tabs
**Sales** > **Products** groups the catalog with the pages that sell and stock it:
* **Products** - the [catalog](/sales/products) itself.
* **Packages** - fixed-price [bundles](/sales/packages) of plans and products.
* **Stock** and **Ingredients** - on-hand counts and recipe ingredients, when [inventory](/sales/inventory) is on.
* **Deliveries** - supplier [deliveries](/sales/deliveries) with invoice scanning, when inventory is on.
* **Rentals** - [gear lent out](/sales/rentals) and still to come back, when rentals are on or gear is still out.
### Finance
**Sales** > **Finance** (`/finance`) gathers everything about money that has moved into one tab, with its own sub-tabs. It opens on **Charges**.
| Sub-tab | What it lists |
| - | - |
| [Charges](/sales/charges) | Everything owed: what someone owes you, one row per thing sold |
| [Payments](/sales/payments) | Every payment: how money came in, and who recorded it |
| [Invoices](/sales/invoices) | Numbered invoice and pro-forma documents |
| [Bill Runs](/sales/bill-runs) | Batches that sweep a period of charges into invoices |
| [Wallet](/sales/wallet) | Each member's store-credit balance |
| **Wallet movements** | Every credit and debit across all wallets |
### Cash
**Sales** > **Cash** appears once you turn on the cash module under **Settings** > **Sales** > **Cash Registry & Expenses**. It has three sub-tabs: **Overview** (cash on hand and expenses by category), **Expenses**, and **Ledger**. See [Cash registry and expenses](/sales/cash).
## The money model
Sales is built on three concepts that are deliberately kept separate. Once you have the model in your head, every Finance screen lines up.
* A **charge** is what someone owes you for one thing: a booking, a membership period or signup fee, a product, a check-in, a cancellation charge, or a settlement. Charges were called transactions in earlier versions of 1Club.
* A **payment** is money that came in. One charge can be paid by several payments (a deposit, then the rest at the desk), and one payment can pay several charges (a basket at checkout).
* An **invoice** is a document issued over one or more charges for a contact, with a number and a due date. Not every charge is invoiced, and invoices don't move money on their own.
A charge's `paymentStatus` tells you whether it is still owed. `pending` (shown as **Unpaid**), `partially_paid`, `overdue`, and `failed` are unresolved: the ones that have fallen due make up the contact's **Due now**, and the rest show as **Upcoming** (see [What counts as due now](/members/member-profiles#what-counts-as-due-now)). `paid`, `settled`, `refunded`, `void`, and `cancelled` are closed.
Three ways to take something back are kept apart too. A **refund** returns money from a payment. **Voiding** closes an unpaid charge that should never have been owed. A **reversal** is a negative charge that cancels all or part of another, for example when a booking is cancelled, so revenue reports stay balanced.
This separation is why you can record a payment without ever issuing an invoice (for example, taking cash at the front desk for a single booking), and why an invoice can contain charges from several different bookings or memberships.
## How a charge usually flows
The typical flow for a paid activity is:
1. A booking, membership, or signup fee is created. 1Club writes a **charge** with `paymentStatus = pending` (or `paid` if the amount is zero).
2. Either the system or an admin collects payment. That can happen up front (Stripe Payment Element in the member portal, or an admin recording a manual payment), at check-in if the booking policy says so, or later from an invoice.
3. When the payment succeeds, 1Club creates a **payment** row, links it to the charge(s), and flips the charge's status to `paid`. Pending bookings tied to that charge are confirmed.
Invoices are optional. They get created when:
* You manually invoice a charge from its details page.
* You run a [bill run](/sales/bill-runs), which sweeps every pending non-void charge for a period and groups them into invoices.
* A scheduled bill run fires on a cron (configured under **Settings** > **Billing** > **Automated Billing**).
## Recurring memberships
For recurring plans, an hourly job (`recurring-transactions`) does three things:
* Creates the next `membership_recurrence` charge when a billing period rolls over.
* For memberships flagged **Require payment upfront**, it attempts to charge the contact's default Stripe payment method when the recurrence becomes due. If no default method is on file or the card payment fails, the charge stays `pending`.
* Marks any `pending` charge whose due date is in the past as `overdue`.
It does not send invoices and it does not retry failed charges. Retries are manual: open the charge or invoice and use **Charge saved card** or **Add payment**. See [Plans](/sales/plans) for how a plan's fields drive this behavior.
## Settings for Sales
Setup that you do once lives under **Settings**, in two categories:
* **Settings** > **Sales** - **Point of Sale**, **Inventory Management**, **Rentals**, **Cash Registry & Expenses**, and **Payment terminals**. The switches here turn the matching tabs and register features on, and **Payment terminals** connects the card readers the register takes payment on.
* **Settings** > **Billing** - **General** (currency, [billing entities](/settings/billing-entities), tax rates, revenue accounts, [payment methods](/settings/payment-methods), [external programs](/settings/external-programs)), [**Pricing Rules**](/settings/pricing-rules), [**Seasons**](/settings/seasons), and **Automated Billing** (scheduled [bill runs](/sales/bill-runs#scheduled-bill-runs)).
## Related
* [Point of sale](/pos/point-of-sale) - the register where in-person sales are rung up
* [Plans](/sales/plans)
* [Products](/sales/products)
* [Orders](/sales/orders)
* [Charges](/sales/charges)
* [Payments](/sales/payments)
* [Invoices](/sales/invoices)
* [Payment methods](/settings/payment-methods)
* [Export](/settings/export) - CSV and QuickBooks bundle for accounting handoff.
# Packages
Source: https://docs.1club.ai/sales/packages
Bundle plans and products into a single fixed-price offer you can sell on the POS and signup forms, with revenue split back to each component.
A package bundles one or more plans and products into a single fixed-price offer. Instead of selling a membership and a starter product separately, you wrap them into one package at one price, and 1Club splits the revenue back out to the underlying items so each is still recorded against its own account. You build packages in the admin, the front desk sells them on the point of sale, and members buy them on signup forms.
## When to use it
Use a package to sell different things together at one price, typically to get a new member joined and equipped in one purchase. A package can mix any [plans](/sales/plans) and [products](/sales/products), each with its own quantity, and its price is independent of the components' list prices. Each component still records its own share, so pro-shop revenue stays separate from membership revenue.
Typical uses:
* **A padel starter pack** - a padel gym bundles its monthly padel plan with a starter kit, priced below the combined list price, so new members join and walk away kitted out.
* **A beginner day at a climbing gym** - a day pass plus a chalk bag and a guidebook, sold at one price from the **Packages** tab on the point of sale.
* **A martial-arts trial with a gi** - a trial month and a uniform attached to the signup form, so prospects buy the whole bundle when they register online.
* **A term paid up front** - one class-pack plan at **Qty 3**, priced below three individual packs.
See [Worked examples](#worked-examples) for how to set up each of these.
## How the revenue split works
When a package is sold, 1Club expands it into its component memberships and product sales. The fixed package price (times the quantity sold) is allocated across those components in proportion to each component's list price, using largest-remainder rounding so the parts sum back to the package price exactly. Each component is then recorded against its own revenue account, exactly as if it had been sold individually at the allocated amount.
Tax follows the same path as the rest of your catalog. The allocated amounts run through the normal per-component tax pipeline, so:
* With **tax-inclusive** rates, the customer pays the package price and tax is carved out of each component's share.
* With **tax-exclusive** rates, tax is added on top of each component (the customer pays the package price plus tax).
Because the split is proportional to list prices, a bundle priced below the sum of its parts passes that discount through to every component proportionally.
## The list page
You manage packages under **Sales** > **Products** > **Packages** in the admin (`/billing/products/packages`).
Packages are shown in a data grid with columns for **Name**, **Price**, **Items** (the component count), **Visibility**, and **Status** (Active or Inactive), plus a row menu to edit or delete. Click a row to open the package dialog.
## Creating or editing a package
1. Go to **Sales** > **Products** > **Packages**.
2. Click **Add package** to open the package dialog.
3. Enter a **Name** and the **Package price** - the single fixed price charged for the whole bundle.
4. Under **Items in this package**, use **Add Plan** and **Add Product** to add components, and set a **Qty** for each. A package must contain at least one item.
5. Optionally set **Visibility**, **Active**, a **Description**, an **Image**, and a **Color**.
6. Save.
### Per-field reference
* **Name** - required.
* **Package price** - the single fixed price for the whole bundle. This is what the customer is charged, regardless of the sum of the components' list prices.
* **Items in this package** - the plans and products included. Add a plan or a product row, choose the item, and set its **Qty**. Only active plans and products can be selected, and a product set to **Rented out** cannot be part of a package (see [Equipment rentals](/sales/rentals)). The item pickers show each option's list price for reference.
* **Visibility** - **Public**, **Member only**, or **Private** (admin-only).
* **Active** - inactive packages are hidden from the POS and order forms.
* **Description**, **Image** (up to five), **Color** - display fields shown on the package and public surfaces.
Packages are categorized and priced at the package level; the components keep their own revenue accounts. There's no separate tax field on a package - tax is applied per component at sale time using your organization's tax settings (see [How the revenue split works](#how-the-revenue-split-works)).
## Where packages are sold
### Point of sale
Packages appear in their own **Packages** tab on the point of sale. Adding a package to the cart records it as a package line that expands into its component plans and products on completion.
### Forms
Attaching packages to a [form](/marketing/forms) turns it into an **order form** where members can add or remove packages during signup. You can also attach optional [products](/sales/products) as add-ons alongside, with a running order summary as the member builds their order.
## Worked examples
### Racket club starter pack
You want new members to join and walk away kitted out.
* Create a package *Padel starter* containing your *Monthly padel* plan (Qty 1) and a *Padel starter kit* product (Qty 1).
* Set the **Package price** below the combined list price to make the bundle attractive.
* The membership and the kit each record their own share, so pro-shop revenue stays separated from membership revenue.
### Climbing day + starter kit
* Bundle a *Day pass* plan with *Chalk bag* and *Guidebook* products into a *Beginner day* package at one price.
* Sell it on the POS Packages tab; the pass and the products expand as separate lines on completion.
* Shoes and harnesses you lend out are [rental products](/sales/rentals), which cannot go in a package. Lend them on the same order instead.
### Martial arts trial with a gi
* Create a *Trial month + gi* package pairing a starter membership plan with a *Uniform (gi)* product.
* Attach it to your signup form so prospects can buy the whole trial bundle when they register online.
### Multi-month pass block
* Create a package containing a single class-pack plan with **Qty 3** for members who want a term's worth up front, priced below three individual packs.
## Tips & best practices
* **Price the bundle, not the parts.** The package price is what the customer pays; the split to components is automatic. Set the price for the offer you want to make.
* **Keep components active.** A package that references an inactive plan or product can't be sold - the sale expansion validates that every component is still active.
* **Use packages for onboarding.** A "join + starter kit" or "trial + gear" bundle is the cleanest way to combine a membership with retail in one sale.
* **Reach for the right tool.** Use a package to sell different things together at a fixed price; use a [promotion](/sales/promotions) to discount a single plan or product.
## Troubleshooting
**Issue**: A package won't save.
**Solution**: A package must contain at least one item. Add at least one plan or product row before saving.
**Issue**: A plan or product isn't in the item picker.
**Solution**: Only **active** plans and products can be added to a package. Activate the item first, then add it.
**Issue**: Selling a package fails or is blocked.
**Solution**: The sale expansion re-checks that every component plan and product is still active. If a component was deactivated after the package was built, reactivate it (or edit the package to remove it).
**Issue**: A package doesn't appear on the POS or signup form.
**Solution**: Confirm the package is **Active** and its **Visibility** allows it in that context. On signup forms, the package also has to be attached to the form.
**Issue**: The component revenue amounts don't look round.
**Solution**: That's expected. The fixed price is split proportionally to list prices with largest-remainder rounding, so individual component amounts can be uneven while summing exactly to the package price.
## Related
* [Plans](/sales/plans) - the membership and pass templates you bundle into packages.
* [Products](/sales/products) - the retail and add-on items you bundle in.
* [Charges](/sales/charges) - how package sales are recorded and split.
* [Forms](/marketing/forms) - attach packages to build an order form.
# Payments
Source: https://docs.1club.ai/sales/payments
Track, record, and refund payments in 1Club - payment status and method, split a booking's bill across its players, apply wallet credit, and refund to the card, the wallet, or cash.
The **Payments** tab is the record of every payment your organization has taken - manual payments logged by staff, card payments captured by a provider, and wallet credit applied at checkout. Use it to see what has been collected, which method was used, who recorded it, and to issue refunds. A payment pays one or more [charges](/sales/charges) (what is owed); the payment carries an amount, a method, a status, and a link to the charges it paid. Front-desk staff record the money they take at the counter, and owners and admins review payments and issue refunds.
## When to use it
Payments are created when a member pays online, when a stored card is charged automatically, or when a staff member records a manual payment at the front desk. You do not have to settle a balance in one go: take part now, combine a method with wallet credit, or give each player on a booking their own share. When money has to go back, you choose whether it returns to the card, the member's wallet, or by hand.
Typical uses:
* **A deposit now, the rest on arrival** - a padel member pays half for a court at the desk and settles the remainder when they arrive.
* **A four-ball paying their own quarters** - four members split a 60.00 court booking, each paying their own share, with a member on a membership covered at no charge.
* **A monthly fee partly from the wallet** - a boxing member with 10.00 of wallet credit owes 40.00, so you apply the credit and charge 30.00 to the card at the point of sale.
* **A cancelled tournament refunded to the card** - you refund a 40.00 card entry back to the card, or return it instantly as wallet credit if the member will book again.
See [Worked examples](#worked-examples) for the details of each.
## The payment list
Use **Sales** > **Finance** > **Payments** to review payments, filter by status, and drill into a single payment. A payment's details show its amount, method, status, the linked charges, the **Created by** admin, and any provider references (for example a Stripe payment intent).
Open **Sales** > **Finance** > **Payments**. The grid shows these columns:
* **Date** - When the payment was recorded.
* **Contact** - The member the payment is for.
* **Amount** - The payment amount and currency.
* **Payment method** - The method used (for example Cash, Card, or a card provider).
* **Status** - Succeeded, pending, failed, refunded, partially refunded, cancelled, or processing.
* **Created by** - The admin who recorded the payment.
Tabs across the top filter by status: **All**, **Succeeded**, **Pending**, and **Failed**, each with a count. Click a row to open the payment. Where you have the right permission, the row action menu offers **Delete** for eligible payments (see [Deleting a payment](#deleting-a-payment)).
The **Created by** column shows the admin who recorded the payment. For payments captured automatically by a provider (online checkout, automatic membership renewals), the column is empty - those are system-recorded. For manually recorded payments (front desk, phone, bank transfer logged by staff), it shows the admin's name.
The payments grid has no column for linked charges. To see the charges a
payment paid, open the payment details. Every charge is listed under
[Charges](/sales/charges).
## Payment status
A payment moves through these states:
* **Pending** - Recorded but not yet settled (for example a manual payment awaiting confirmation, or an online payment mid-flow).
* **Processing** - The provider is capturing the payment.
* **Succeeded** - The money is in. This is the only state a payment can be refunded from.
* **Failed** - The attempt did not go through.
* **Cancelled** - The payment was called off before it settled.
* **Refunded** - The full amount has been refunded.
* **Partially refunded** - Part of the amount has been refunded.
## Recording a payment
Most manual payments are recorded as part of a flow rather than from a blank form - completing an order, settling an invoice, or taking money at the point of sale all record a payment behind the scenes. When you record one, 1Club needs:
* **Amount** - How much is being paid.
* **Payment method** - Which configured method the money came in on (see [Payment methods](/settings/payment-methods)).
* **What it settles** - A charge, several charges, or an invoice.
For example, completing a draft order opens the **Complete order** dialog, where you pick a payment method; 1Club then records a payment against each of the order's unpaid charges with that method. See [Orders](/sales/orders).
Payments handled entirely by a provider's own checkout (for example an online
card payment via the hosted flow) are captured by that provider and cannot be
re-recorded as manual payments. Log manual payments for money you collect
yourself - cash, a card terminal, or a bank transfer.
### Payment methods used
Each payment records the **method** it came in on. Methods belong to a channel:
* **Manual** - Methods you take and log yourself, such as cash, an in-person card terminal, or a bank transfer. You define these under [Payment methods](/settings/payment-methods).
* **Card provider** - Online card payments captured through the connected provider's checkout.
* **myPOS** - Payments taken through a myPOS terminal or link. See [myPOS](/settings/integrations/mypos).
* **Wallet** - The member's stored wallet balance, applied as credit (see [Wallet](/sales/wallet)).
The method shown on a payment tells you where the money physically came from, which matters when you reconcile the till or a bank statement.
## Partial and split payments
You do not have to settle a balance in one payment.
* **Partial payment (one charge)** - Record less than the full amount against a single charge. The charge moves to **partially paid** and keeps an outstanding balance you can collect later.
* **Settling several charges at once** - You can record one payment against multiple charges, but only when the payment covers all of them in full. A single lump sum cannot be split partially across several charges, so a multi-charge payment must equal the sum of their outstanding balances.
* **Dividing a booking between its players** - Give each player on a booking their own share and their own way of paying it. See [Splitting a booking across its players](#splitting-a-booking-across-its-players).
### Splitting a booking across its players
Four people book a padel court and each want to pay their own quarter. Rather than one person paying and chasing the others, split the bill so each player owns their own charge.
On the booking's detail page, use **Split payment** (or **Edit split** if one already exists). The dialog explains:
> Divide between the players. Each one can pay their own way. Shares are shown before tax.
For each player you set:
* **Share** - their slice of the price, before tax.
* **Pays with** - how they are covering it: paying directly, a membership, or an external program. Until they choose, the row reads **Not chosen yet**.
A player covered by a membership or program shows as **Covered** and is charged nothing - their entitlement is used instead, exactly as it would be on a booking of their own.
#### Even and custom splits
* An **even** split divides the price equally and re-divides it whenever the roster changes. Add a fifth player and all five shares rebalance, rather than leaving a gap.
* A **custom** split takes the amounts you enter. Because the shares **are** the price in that case, changing them reprices the booking, and 1Club tells you so before you save: "This changes the booking price from to ." That is intended - charging the four players more or less than the court's list rate is a legitimate thing to do at the desk.
Two rules are enforced on save: no share may be negative, and everyone holding a share needs a payment method.
#### Collecting each share
Each player's share becomes their own charge, so you collect them independently from the players list on the booking, or from [Charges](/sales/charges) like any other charge. A player who has settled is marked as such even while others still owe.
This also drives entry: the check-in gate asks whether **this player** has paid their own share, so three players who have paid are admitted while the fourth is still outstanding.
#### Re-splitting after money has changed hands
You can edit a split after players have paid. Anything already collected is returned as wallet credit, not discarded:
> Anything already paid goes back to that player's wallet, so they can settle their new share from it.
Memberships and program entries consumed by the old split are given back before the new one is applied, so re-splitting twice never quietly drains a member's allowance.
### Splitting across a method and wallet credit
You can combine a primary method with wallet credit in one checkout:
* **Member portal** - Wallet credit is applied automatically. The order deducts the wallet balance (up to the amount due) and the member pays only the remainder by card or on arrival. The breakdown shows a **Wallet** line (for example `Wallet: -10.00`) above the total, and the **Pay** button reflects the post-wallet amount.
* **Admin point of sale** - When you take a payment with a primary method (card, cash, and so on), you can also apply wallet credit as a secondary payment via the **Also apply wallet credit ( available)** checkbox and an **Amount from wallet** field. The split is shown as "Wallet + via primary method". The wallet toggle is hidden when the wallet already covers the full amount (you can then pay entirely from the wallet).
The amount drawn from a wallet is always capped at the member's balance and the outstanding amount.
## Refunds
Refunds are issued from the **charge**, not from the payment, and you choose where the money goes. Open the charge in [Charges](/sales/charges) and use **Refund**.
### Issuing a refund
1. Open the charge from **Sales** > **Finance** > **Charges**.
2. Click **Refund**. The **Refund charge** dialog opens.
3. Answer **Where should the money go?** (see the destinations below). Each option shows its own ceiling, because they differ.
4. Set **Amount to refund**. It defaults to everything still refundable, and cannot exceed the ceiling for the destination you picked.
5. Optionally add a **Reason**.
6. Click **Issue refund**.
Refunds require the **billing manage** permission at the organization level.
### Choosing a destination
| Destination | What it does |
| - | - |
| **Back to the card** | A real reversal through the payment provider, back to the card the member paid with. Drawn from your Stripe balance. |
| **Wallet credit** | Added to the member's wallet to spend with you later. No money leaves your account. |
| **Cash** | Handed back at the desk. Records the reversal against the payment. |
| **Manual** | Settled outside the platform, for example a bank transfer you make yourself. |
Only options the original payment supports are offered. **Back to the card** appears only for payments taken through Stripe: money collected through myPOS, DatecsPay, or Hype cannot be reversed through those providers from 1Club yet, so return it as **Wallet credit** or by hand at the till.
The ceilings differ per destination because they are calculated per payment. A charge settled partly by card and partly from the wallet can return only the card portion to the card.
### Card refunds are not instant
When you refund to the card, 1Club tells you:
> The refund is sent to Stripe now and usually reaches the member's statement within 5-10 days. It is drawn from your Stripe balance.
Some card refunds settle asynchronously, so you may see a refund reported as **awaiting confirmation**:
> Refund sent to Stripe - awaiting confirmation. It is not back with the member yet.
That is a normal intermediate state, not a failure. The amount is confirmed once the provider settles it, and the charge's status updates then. A partial confirmation reads as "X refunded, Y sent to Stripe and awaiting confirmation" - treat only the confirmed part as money the member has back.
Refunding to **Wallet credit** is immediate and never has a pending state,
because no money leaves your account. It is the right choice when the member
is standing in front of you and will book again.
### Partial refunds
Enter any amount up to the ceiling. Each refund is recorded individually with its amount, destination, reason, and who issued it, so a charge refunded in three instalments has three records rather than one status flag. You can keep refunding until the refundable balance is exhausted.
### Nothing to refund
If no money was ever collected on the charge, **Refund** tells you so:
> Nothing was collected on this charge, so there is nothing to refund. Void it instead if it was raised by mistake.
See [Voiding a charge](/sales/charges#row-actions) for that path.
### Automatic wallet-credit refunds
Some flows refund a member automatically by crediting their **wallet** rather than reversing the original tender, without asking you to pick a destination:
* A booking is **cancelled** under a cancellation policy. The refund follows the tiered rules on the matching policy - see [Booking policies](/settings/booking-policies).
* An **order** is cancelled.
* A member's **entrance method changes** at check-in (they paid directly, then a membership or program is used to admit them instead). 1Club marks the original payment refunded and credits the amount to their wallet.
* A booking's **split is edited**. Anything already paid goes back to each player's wallet so they can settle their new share from it - see [Splitting a booking across its players](#splitting-a-booking-across-its-players).
Cancellation refunds always go to wallet credit. To return that money to the
member's card instead, refund the charge from the billing page and choose
**Back to the card**.
## Payment links
When you send a member a link to pay an outstanding amount, the link opens a **branded payment launcher** on your organization's member-facing site. The launcher shows your organization's logo and theme colours, the amount due, the reference, and the date, with a **Continue to payment** button that hands off to the payment provider (myPOS) to complete the payment.
The launcher resolves to your member-facing site automatically - it uses your verified custom domain, then your published website, then the member portal, then the network, so members land on a branded page rather than a generic one.
The launcher also reflects the link's state, for example:
* "This payment link has expired"
* "This payment has already been completed"
* "Thank you, your payment was received"
* "Payment cancelled"
Payment links for charges (such as a booking or membership charge) show
the charge's reference. Only genuine invoices are labelled "Invoice #".
## Deleting a payment
**Only manually recorded payments can be deleted.** That means every method on the **manual** channel - Cash, an in-person card terminal, a bank transfer, or whatever else you have defined. Those rows are just a record of money taken at the counter, so removing the record is the whole correction.
Everything else must be refunded instead, because the money sits somewhere a deleted row cannot reach:
* **Card provider, myPOS, DatecsPay, Hype** - the provider is holding the funds.
* **Wallet** - the member's wallet was debited.
Attempting it tells you:
> Only manually recorded payments can be deleted. This one was collected through a payment provider or the wallet, so refund it instead.
A manual payment may settle **several** charges at once - a multi-line invoice, a booking split across a court and an instructor, a point-of-sale order. Deleting it recalculates every affected charge back to paid, partially paid, or pending based on what remains, so you do not have to unpick them yourself.
Deletion requires the **billing delete** permission at the organization level.
A payment recorded on a manual method that nonetheless carries a provider
reference is also refused. This catches a Cash or Terminal row that actually
went through a provider, where the funds genuinely are with that provider
despite the method chosen.
## Worked examples
### Padel court paid half now, half on arrival
A member books a court and pays half at the desk, settling the rest when they arrive.
* Record a **partial payment** for half against the booking charge; it moves to **partially paid**.
* On arrival, record a second payment for the remainder to bring it to **paid**.
### Boxing gym monthly fee split across card and wallet
A member has 10.00 of wallet credit and owes 40.00 for the month.
* At the point of sale, take the primary method (card) and tick **Also apply wallet credit (10.00 available)**.
* The split shows "Wallet 10.00 + 30.00 via primary method"; the card is charged 30.00.
### Cancelled climbing session refunded to wallet
A climber cancels a booked session within the free-cancellation window.
* The booking is cancelled and, under the cancellation policy, the amount is credited to the climber's **wallet** automatically.
* The original payment is reflected as refunded; the wallet balance is available for their next visit.
### Front-desk cash payment recorded in error
Staff logged a cash payment against the wrong member.
* Cash is a **manual** method, so **delete** the payment. Every charge it touched recalculates automatically, even if it settled several at once.
* Record the payment again against the right member.
* Had it been a card payment, deletion would be refused - **refund** it and re-take it instead.
### Padel four-ball, each player paying their own quarter
Four members book a court for 60.00 and each wants to pay separately.
* On the booking, click **Split payment**. An even split gives each player 15.00.
* One player is on a membership: set their **Pays with** to that membership. They show as **Covered** and are charged nothing, and the other three still owe 15.00 each.
* Collect each share from the players list as they pay. The two who have paid can check in while the third is still outstanding.
### Cancelled tournament entry returned to the card
A member paid 40.00 by card for a tournament that you then cancel outright.
* Open the entry charge in **Sales** > **Finance** > **Charges** and click **Refund**.
* Choose **Back to the card**, leave the amount at the full 40.00, and add a reason.
* The refund is sent to Stripe and may read as awaiting confirmation for a few days. Tell the member it typically lands on their statement within 5-10 days.
* Had you wanted to keep the money with the gym, **Wallet credit** would have returned it instantly as credit instead.
## Tips & best practices
* **Refund, do not delete, anything a provider or the wallet touched.** Only manual-channel payments can be deleted. Refund the rest so the money actually goes back and the audit trail stays intact.
* **Pick the refund destination deliberately.** **Wallet credit** is instant and keeps the money with you; **Back to the card** is the right answer when the member is not coming back. Do not use wallet credit to avoid a card refund the member asked for.
* **Read Created by to tell manual from automatic.** A blank **Created by** means the provider captured it; a name means a staff member logged it. This is your first check when reconciling the till.
* **Match the method to the money.** Record the payment on the method it actually came in on (cash, terminal, transfer) so your Payments tab reconciles against your bank and till.
* **Use partial payments for deposits.** Take a deposit as a partial payment and collect the balance later rather than forcing payment in full up front.
* **Split the bill rather than nominating a payer.** On a group booking, give each player their own share so each is collected and admitted independently.
* **Send a payment link for outstanding balances.** Rather than chasing a member for card details, send the branded payment link and let them pay on your own site.
## Troubleshooting
**Issue**: I cannot find a **Refund** action on the payment.
**Solution**: Refunds are issued from the charge, not the payment. Open the charge in **Sales** > **Finance** > **Charges** and use **Refund** there. You also need the **billing manage** permission at the organization level.
**Issue**: **Back to the card** is not offered as a destination.
**Solution**: The payment was not taken through Stripe. Money collected through myPOS, DatecsPay, or Hype cannot be reversed through those providers from 1Club yet - return it as **Wallet credit**, or hand it back at the till and record it as **Cash**.
**Issue**: A refund says it is awaiting confirmation and the member says they have not received it.
**Solution**: That is expected. Card refunds can settle asynchronously and typically reach the statement in 5-10 days. The amount is not back with the member until it is confirmed. Do not issue a second refund for the same money.
**Issue**: **Refund** says there is nothing to refund.
**Solution**: No money was ever collected on that charge. If it should never have been charged, void it instead - see [Charges](/sales/charges).
**Issue**: The **Delete** action is refused on a payment.
**Solution**: Only manually recorded payments (the **manual** channel: cash, terminal, bank transfer) can be deleted. Provider-collected and wallet payments must be refunded. A manual payment carrying a provider reference is also refused.
**Issue**: A multi-charge payment is rejected.
**Solution**: A single payment can settle several charges only when it covers them all in full. Record separate payments, or record a partial payment against each charge individually.
**Issue**: The wallet toggle is missing at the point of sale.
**Solution**: The wallet already covers the full amount due, so the secondary-tender toggle is hidden - you can pay entirely from the wallet. It reappears when the wallet only partly covers the balance.
**Issue**: One player on a split booking cannot check in although the booking is paid.
**Solution**: Entry is judged per player, not per booking. Check that player's own share on the booking's players list - the others having paid does not settle theirs.
## Related
* [Orders](/sales/orders) - Complete an order to record payment against its charges.
* [Charges](/sales/charges) - The charges a payment settles.
* [Invoices](/sales/invoices) - Invoices generate payments when paid.
* [Payment methods](/settings/payment-methods) - Configure which payment methods your organization accepts.
* [myPOS](/settings/integrations/mypos) - The provider behind payment links and terminal payments.
* [Booking policies](/settings/booking-policies) - Decide when payment is collected, and the tiered rules for cancellation refunds.
# Plans
Source: https://docs.1club.ai/sales/plans
Membership and pass templates in 1Club - price, billing frequency, usage limits, what they grant access to, signup fees, wallet credit, visibility, and revenue account.
A plan is the template you sell to members. It defines the price, the billing cadence, the usage allowance, what the plan grants access to, and a few visibility and accounting fields. When someone is enrolled in a plan they get a [membership](/sales/memberships) record; the plan itself just describes the offer. Owners and admins set plans up, the front desk sells them, and members buy them online.
## When to use it
Create a plan for every membership or pass you sell. One plan can be recurring or a one-time pass, cap how often it can be used, limit which classes, courts, or instructors it covers, add a signup fee or starter wallet credit, and be shown publicly, to members only, or kept private for staff-assisted signups. Trials and intro prices attach to a plan as [promotions](/sales/promotions).
Typical uses:
* **An unlimited monthly membership** - a yoga studio sells an all-access monthly pass that auto-renews, with an optional off-peak version that only works before 4pm.
* **A class pack that expires** - a Pilates studio sells 10 reformer classes valid for 60 days after purchase.
* **Court hours per month** - a padel gym sells a monthly membership with eight court hours, counted per hour rather than per booking.
* **A drop-in day pass** - a one-day pass for visitors who check in at the front desk rather than booking.
See [Worked examples](#worked-examples) for the settings behind each of these.
## How plans turn into charges
When a contact buys a plan you get a membership and, immediately, a charge:
* **One-time plans**: one charge at the plan's price, plus a separate `membership_signup_fee` charge when the signup fee is set.
* **Recurring plans**: a `membership_creation` charge for the current period, with the next period scheduled. An hourly job creates a fresh `membership_recurrence` charge when each period rolls over.
For plans that have a stored Stripe payment method on file and **Require payment upfront** on, the recurrence job also attempts to charge immediately. If the card payment fails or no default method is on file, the charge is left `pending` and you collect through the [Invoices](/sales/invoices) or [Charges](/sales/charges) flow. See the [Sales overview](/sales/overview) for the full charge lifecycle.
## The plans list
Plans live under **Sales** > **Plans** in the admin (`/billing/plans`). **Plans** is the first tab of **Sales**, so the section opens here. The page opens on the **Active** tab; switch to **Inactive** to see archived offers.
**Sales** > **Plans** shows plans as a sortable list. Each row displays:
* A **color dot** (the plan's color).
* **Name**.
* **Price**, with the **signup fee** shown beneath it when one is set.
* A **category** chip (when the plan has a category).
* A **frequency** chip (for `custom` plans it reads "N days/weeks/months").
Drag any row by its handle to set the global plan order (see [Plan order](#plan-order)). The row menu has **Edit** and **Delete**.
Above the list, four cards summarize **Total Plans**, **Active Plans**, **Average Price**, and **Recurring Plans** (anything with a billing type other than one-time). A **frequency filter** at the top narrows the list to a specific billing cadence, and the **Active** / **Inactive** tabs carry a count badge each.
The page header also has **Categories** (manage plan categories) and **Add Plan**.
## Creating or editing a plan
Click **Add Plan**, or open a row's menu and choose **Edit**. The plan dialog is organized into five sections.
### General
* **Plan Name** - public-facing. Required.
* **Gym** - which gym the plan belongs to. Only shown when your organization has more than one gym; choose **All** to make it organization-wide.
* **Category** - an optional grouping (see [Plan categories](#plan-categories)). Pick an existing one or type a new name to create it inline.
* **Visibility** - `Public`, `Member only`, or `Private`. Controls which sales surfaces the plan appears on.
* **Description** - public-facing free text.
* **Active** - whether the plan can be sold. Toggling a plan inactive does not affect existing memberships on it; they keep running until they expire or are cancelled.
You can translate **Name** and **Description** into your other locales with the translate button on the section header.
### Pricing
* **Price** (labeled **Price per Period** for recurring plans) - the amount charged each billing period, or the one-off price for a one-time plan.
* **Type** - the billing frequency: **One-time**, **Weekly**, **Monthly**, **Quarterly**, **Annually**, or **Custom**. Choosing **Custom** reveals a **Period** number and a **Unit** of `Days`, `Weeks`, or `Months` (for example, `10 weeks`).
* **Signup Fee** - a one-time fee charged when joining. Added to the first charge. Optional.
* **Wallet Credit** - credit added to the member's wallet when the membership is activated. Use it for "get X credit when you join" offers. Optional.
* **Revenue Account** - the accounting code that charges for this plan are tagged with. Leave as **None (uses default)** to fall back to the organization default.
* **Default Auto Renewal** - whether new memberships on this plan are flagged to auto-renew at the end of each period. Members can flip this on their own membership later. When on, a **Renewal Lead Time (Days)** field appears: how many days before the renewal date 1Club attempts the auto-renewal charge. This is also the cancellation cut-off. Defaults to 3, maximum 60.
* **Require payment upfront** - when on (the default), 1Club attempts to charge the member's stored default payment method as each period becomes due. When off, the charge is not attempted automatically and you collect through invoices or charges instead.
### Validity and access
* **Sessions** - the usage cap (the plan's max uses). For recurring plans this is uses per billing period (for example, `10/month`); for one-time plans it is the total uses for the pass. Leave blank for unlimited.
* **Session limit is per** - whether each use counts as one **Booking** or as the booking's duration in **Hours**. Hidden for generic plans, where a use is always one unit. Only shown once the plan has a scope.
* **Validity (Days)** (one-time plans only) - how long the pass remains valid after it is issued.
* **Valid for** - what the plan grants access to. Turn on **Apply to all bookings** to cover everything, or toggle individual scopes - **Classes**, **Areas**, **Instructors** - and, within each, pick specific class types, area types, or instructor types (leave a scope's list empty to mean "all of that scope").
* **Household Allowance** - the number of additional household members the plan covers, used in account-level billing. `0` means individual only.
Leaving **Valid for** completely empty (no scope selected) creates a **generic plan**. Generic plans cannot be used to access bookings. They only work for direct check-ins and they still affect dynamic booking pricing. If you want a membership that members book classes or courts with, pick at least one scope (or turn on **Apply to all bookings**).
### Presentation
* **Features** - a free-text bullet list shown on the plan detail card. Add each feature and press Enter.
* **Sports** - tags the plan with one or more sports so it surfaces under sport-specific filtering.
* **Plan Images** - a drag-to-reorder gallery (up to 5) shown on the member-facing sales pages.
* **Color** - the accent color used for the plan's dot in the list and on member-facing surfaces.
### Operating hours
Set per-day open and close times, or fall back to the default. A plan's operating hours define the time windows during which its membership can be used, which is how you model off-peak or time-limited memberships. Leave this empty for a membership that works whenever the gym is open.
## Plan details
Click a plan's name to open its details page. It shows the same fields plus two stat cards at the top: **Active Memberships** (a count of memberships on the plan currently in `active` status) and **Total Revenue** (the sum of the prices of those active memberships).
If the plan has images, a sortable gallery sits at the top; drag to reorder and the change saves as you drop. The right column shows the assigned **Revenue Account**, or a "None (uses default)" hint when none is set. The details page also lists the [promotions](#trials-free-days-and-intro-offers) targeting the plan.
## Trials, free days, and intro offers
Plans don't have a built-in "trial days" field. Instead, trials and intro pricing are modelled as [promotions](/sales/promotions) attached to the plan. This keeps the same offer machinery (eligibility, caps, audience, analytics) behind every kind of incentive.
The plan-details page has a **Promotions** section listing everything currently targeting this plan. **Add promotion** opens the editor pre-scoped to this plan; from there you pick the shape:
* **Free trial** - a discount with **Auto-apply** on and **Value** of 100%. New customers buying this plan get it free the first time, no code. The first-time-buyer rule prevents repeat redemptions.
* **First-month intro** - same idea with a partial discount (e.g. 50% or a fixed amount off the first period). For recurring plans this rides through every renewal once applied, so prefer a one-shot code if you only want it on the creation charge.
* **Coded promotion** - leave **Auto-apply** off and give the discount a code to share via email or partners. Members or staff enter the code at checkout.
See the [Promotions](/sales/promotions) page for the full set of controls (schedule, redemption caps, audience targeting, analytics).
## Plan order
Plans have a single global sort order that controls how they appear everywhere - the public plans listing, the booking-confirmation upsell suggestions, and the admin Plans list. On **Sales** > **Plans**, drag rows to reorder them; the order is saved and applied across all of those surfaces. If another admin reorders at the same time, the later save is rejected and the list reloads to the latest order.
Booking policies no longer keep their own upsell order. When a policy upsells plans, they display in the global plan order set here. See [Booking policies](/settings/booking-policies).
## Plan categories
You can group plans into categories (for example, "Adults", "Juniors", "Day passes"). Categories are optional - uncategorized plans simply appear ungrouped.
* **Manage categories** - On **Sales** > **Plans**, click **Categories** to add, rename, reorder, or delete categories. Deleting a category clears it from any plans that used it (the plans are not deleted).
* **Assign a category** - Set a plan's **Category** in the plan dialog. You can pick an existing category or type a new name to create one inline.
* **On your website** - Categories show as a small tag on plan cards and on the plan details page. When a Plans section has more than one category, members get a pill filter (with an **All** option) to narrow the list, and you can scope which categories a section shows from the website editor's **Categories** field.
## Deleting a plan
Delete is permanent and clears the plan record. If active memberships still reference it you'll need to migrate or cancel them first. Inactive plans you've stopped selling but want to keep for historical reporting should stay on the **Inactive** tab rather than being deleted.
## Worked examples
### Unlimited yoga membership (recurring, all classes)
You run a yoga studio and want an all-access monthly pass.
* **Type**: Monthly. **Price per Period**: your monthly rate.
* **Sessions**: leave blank (unlimited).
* **Valid for**: toggle **Classes** and leave the class-type list empty so it covers all classes (or turn on **Apply to all bookings**).
* **Sports**: yoga. **Default Auto Renewal**: on, so members roll over each month.
* Optionally set **Operating hours** to model an off-peak version that only works before 4pm.
### 10-class pack (one-time pass)
A punch card that expires.
* **Type**: One-time. **Price**: the pack price.
* **Sessions**: 10, with **Session limit is per** set to **Bookings**.
* **Validity (Days)**: 60, so the pack expires two months after purchase.
* **Valid for**: **Classes**, scoped to your reformer or mat class types.
### Racket monthly membership (court access, per-hour usage)
A padel or tennis membership that includes a set number of court hours.
* **Type**: Monthly. **Sessions**: 8, with **Session limit is per** set to **Hours** (eight court hours per month).
* **Valid for**: **Areas**, scoped to the *Padel court* area type (see [Areas](/settings/areas)).
* **Signup Fee**: a one-off joining fee. **Wallet Credit**: optional starter credit for extra bookings.
### Family membership (household allowance)
One membership that covers a household.
* **Type**: Monthly or Annually. **Household Allowance**: 3 (the primary member plus three additional household members).
* **Valid for**: **Apply to all bookings**, or scope to the class and court types the family should reach.
* **Category**: "Family" so it groups on your website.
### Martial arts unlimited with intro offer
An unlimited jiu-jitsu or muay-thai membership with a trial.
* **Type**: Monthly, **Sessions** blank, **Valid for** the martial-arts class types.
* On the plan details page, **Add promotion** and create a **Free trial** (auto-apply, 100%) so new members get their first month free, or a **First-month intro** at 50% off.
### Day pass (generic check-in plan)
A single-visit pass for drop-ins who check in at the front desk rather than booking.
* **Type**: One-time, **Validity (Days)**: 1.
* Leave **Valid for** empty to create a generic plan - it won't grant booking access but works for direct check-ins and still feeds dynamic booking pricing.
## Tips & best practices
* **Pick a scope unless you truly want generic.** An empty **Valid for** makes a generic plan that can't book anything. For a bookable membership, toggle a scope or use **Apply to all bookings**.
* **Choose the session unit deliberately.** Class packs usually count per **Booking**; court memberships that sell "hours per month" count per **Hour**.
* **Model trials as promotions, not prices.** Keep the plan price at its true value and attach a free-trial or intro promotion so analytics and eligibility stay consistent.
* **Prefer Inactive over Delete.** Deactivating stops new sales while existing memberships keep running; deleting is permanent and blocked while memberships reference the plan.
* **Set the global order once.** Drag rows on the Plans list to control how plans appear on your website, in upsells, and in the admin all at once.
* **Use categories for large catalogs.** Grouping into "Adults", "Juniors", "Day passes" gives members a pill filter on the website.
## Troubleshooting
**Issue**: Members can't book anything with a membership on this plan.
**Cause**: The plan has no **Valid for** scope, so it is a generic plan.
**Fix**: Edit the plan and toggle at least one scope (Classes, Areas, or Instructors), or turn on **Apply to all bookings**.
**Issue**: The **Session limit is per** field isn't showing.
**Cause**: It only appears once the plan has a scope; generic plans always count one unit per use.
**Fix**: Select a **Valid for** scope first, then choose **Bookings** or **Hours**.
**Issue**: A recurring plan's renewal charge never runs.
**Cause**: **Require payment upfront** is off, or the member has no default Stripe payment method on file.
**Fix**: Turn on **Require payment upfront** for automatic charging, and make sure the member has a saved default method. Otherwise the recurrence stays `pending` and you collect it from [Charges](/sales/charges) or [Invoices](/sales/invoices).
**Issue**: You can't delete a plan.
**Cause**: Active memberships still reference it.
**Fix**: Migrate or cancel those memberships first, or set the plan to **Inactive** to stop selling it while keeping the history.
**Issue**: A custom-period plan won't save.
**Cause**: **Custom** billing requires both a **Period** number and a **Unit**.
**Fix**: Fill in both fields (for example, `10` and `Weeks`).
## Related
* [Sales overview](/sales/overview) - how plans, charges, invoices, and payments fit together.
* [Memberships](/sales/memberships) - the per-contact record produced when someone buys a plan.
* [Charges](/sales/charges) - the records created when someone is billed for a plan.
* [Invoices](/sales/invoices) - documents that group outstanding charges.
* [Promotions](/sales/promotions) - vouchers, discount codes, and auto-apply trials that attach to a plan.
* [Areas](/settings/areas) - the area types a court or space plan can be scoped to.
# Products
Source: https://docs.1club.ai/sales/products
Build your catalog of sellable items - retail, add-ons, and rentals - with categories, brands, price, revenue account, visibility, and optional stock tracking.
Products are the sellable items in your catalog that aren't memberships: pro-shop retail, consumable add-ons, and gear rentals. Memberships and passes live in [Plans](/sales/plans) instead. Each product has a name, a price, a revenue account for accounting, a visibility level, and can be grouped by category and brand. You maintain the catalog in the admin, the front desk rings products up at the point of sale, and members can add them to their order on signup forms.
## When to use it
Add a product for anything you charge for that isn't a membership or pass. Products are what you ring up at the point of sale, bundle into packages, and offer as add-ons on signup order forms, and each one's revenue account keeps retail, rentals, and equipment separate in your accounting. Stock tracking is optional.
Typical uses:
* **A pro shop at a padel gym** - grips, strings, and branded tees grouped in a *Pro shop* category, scanned by barcode at the desk, and booked to a retail revenue account.
* **Drinks and towels at checkout** - a sports drink and a towel rental on the POS and as add-ons on the signup order form, with the fridge count tracked as stock.
* **Gear rental at a climbing gym** - shoes and harnesses marked as rentals, lent out alongside a day pass in one order and tracked until they come back. See [Equipment rentals](/sales/rentals).
See [Worked examples](#worked-examples) for how to set up each of these.
## The list page
You manage products under **Sales** > **Products** in the admin (`/billing/products`). The **Products** tab has its own sub-tabs, each shown only when you have access to it:
| Sub-tab | What it holds | Shown when |
| - | - | - |
| **Products** | The catalog of sellable items, described on this page | Always |
| [**Packages**](/sales/packages) | Fixed-price bundles of plans and products | Always |
| [**Stock**](/sales/inventory) | On-hand quantities per location | Inventory is on |
| [**Ingredients**](/sales/inventory#recipes-and-ingredients-for-cafe-and-bar) | Raw items that recipes draw down | Inventory is on |
| [**Deliveries**](/sales/deliveries) | Supplier deliveries and invoice scanning | Inventory is on |
| [**Rentals**](/sales/rentals) | Gear that is lent out and still to come back | Rentals is on, or gear is still out |
Products are shown in a data grid with columns for **Name**, **Price**, **Visibility**, and **Status** (Active or Inactive), plus a row menu to edit or delete. Click any row to open the product's detail page.
The page header has three actions:
* **Add Product** - opens the product dialog.
* **Categories** - opens the category manager (see [Categories and brands](#categories-and-brands)).
* **Brands** - opens the brand manager.
## Creating or editing a product
Click **Add Product** (or edit an existing row) to open the product dialog.
The dialog asks for:
* **Product Name** - required.
* **Club** - which club the product belongs to. For single-club organizations the lone club is selected automatically; multi-club organizations pick one, or leave it unset to make the product available across clubs.
* **Category** - the product category, used to group products on the POS. Type to search existing categories, or type a new name to create one inline.
* **Brand** - an optional brand. Type to search, or type a new name to create one inline.
* **Barcode** - an optional code (up to 64 characters) for scan-to-sell at the point of sale.
* **Price** - the sell price in your organization's currency.
* **Revenue Account** - the accounting code the sale is booked against. Leave it as **None (uses default)** to fall back to your organization's default account, or pick a specific account (shown as `code - name`).
* **Visibility** - **Public** (visible to anyone), **Member only** (visible after sign-in), or **Private** (admin-only, e.g. staff-only items).
* **Active** - inactive products are hidden from the POS and can't be added to packages or order forms, but existing sales are preserved.
* **Description** - free text shown on the product's detail page and public surfaces.
* **Features** - a tag list of short selling points.
* **Images** - up to five images; the first is used as the primary image.
* **Color** - a display color for the product.
* **Sale or rental** - shown when rentals are on. Set **Fulfilment** to **Rented out** to lend the product instead of selling it. See [Equipment rentals](/sales/rentals).
Products do not carry their own tax rate. Tax is applied at the point of sale using your organization's tax settings, and the product's **Revenue Account** determines which accounting code the sale (and its tax) lands on. Set up your accounts and tax rates once, then just pick the right account per product.
### The product detail page
Click a product row to open its detail page. It shows a **Product Details** card (price, description, features, revenue account, and status/visibility chips) and an **Images** gallery you can drag to reorder. Use the header buttons to **Edit** or **Delete** the product. If inventory management is on, the page also shows a **Stock** section and a **Recipe** action (see [Inventory and stock](#inventory-and-stock)).
### Categories and brands
Both categories and brands are managed from the Products page header:
* **Categories** group products on the point of sale and are sortable; drag to set the order the groups appear in. Renaming is inline. Deleting a category clears it from any products that used it (the products remain).
* **Brands** are an optional label. Deleting a brand clears it from its products.
You can also create a category or brand on the fly from the product dialog by typing a new name in the picker.
## Where products are sold
### Point of sale
Products are the first tab on the **point of sale**. They're grouped into sections by category, so your POS layout mirrors the category order you set. Uncategorized products fall into their own group. If a product has a barcode, staff can scan it to add it to the cart.
### Packages
Products can be bundled with plans into a fixed-price [package](/sales/packages). When a package sells, its fixed price is split proportionally across the component plans and products, so each product still records revenue against its own revenue account.
### Signup order forms
On a [form](/marketing/forms), you can attach products as **add-on products** - optional items a member can add to their order during signup. Attaching packages turns the signup form into an order form; add-on products then appear alongside as optional extras.
## Inventory and stock
Stock tracking is optional and off by default. Turn it on under **Settings** > **Sales** > **Inventory Management** (which enables ingredient tracking, a stock ledger, and recipe-based consumption). Once enabled, each product's detail page gains a **Stock** section and a **Recipe** action.
* **Direct stock** - Simple retail goods (a can of drink, a grip) are tracked directly: their on-hand count decrements on each sale.
* **Recipe-based** - A product that's assembled from ingredients (for example a mixed drink) can have a **Recipe** of ingredients; selling it consumes the ingredients instead.
* **Availability** - The Stock section shows total on-hand for the currently selected club (or the sum across clubs when "All clubs" is active), with a warning chip when it hits zero.
* **Movements ledger** - Every change is recorded as a movement (sale, adjustment, waste, receipt) with a quantity, type, and a link back to the order that caused it.
* **Not tracked** - A product with no recipe and no direct stock shows "This product is not tracked", meaning it sells without touching inventory.
Receiving and adjusting stock require the inventory manage permission; a signed adjustment requires a reason.
## Worked examples
### Pro shop for a racket club
You sell grips, overgrips, strings, and branded tees at the front desk.
* Create a **Category** called *Pro shop* and add each item as a product in it, with a **Brand** (Wilson, Head, Babolat) where relevant.
* Set a **Barcode** on each so the desk can scan them at the POS.
* Route them to a *Retail sales* **Revenue Account** so retail revenue is separated from membership revenue in reporting.
* If you enable inventory, track direct stock on strings and grips and receive stock when a delivery arrives.
### Drinks and towels as add-ons
You want members to grab a sports drink or rent a towel at checkout.
* Create products *Sports drink* and *Towel* in a *Cafe & add-ons* category, set **Member only** visibility if they're for members.
* They appear on the POS Products tab, and you can attach them as **add-on products** on your signup order form so new members can add them during signup.
* For the sports drink, enable direct stock so the fridge count stays accurate; for a mixed smoothie, define a **Recipe** so selling it draws down the ingredients.
### Gear rental at a climbing gym
You rent shoes and harnesses to drop-in climbers.
* Turn on rentals, then create products *Climbing shoes* and *Harness* at the rental price and set **Fulfilment** to **Rented out**.
* Lend them from the POS **Rentals** tab alongside a day pass in a single order. They stay on the rentals list until they are returned.
* Rental products cannot go in a package, so sell the day pass and the gear as separate lines rather than as a *Beginner day* package. See [Equipment rentals](/sales/rentals).
### Trial bundle for a martial arts gym
* Create a *Uniform (gi)* product and route it to an *Equipment* revenue account.
* Build a package that pairs a starter membership plan with the gi at a welcome price; the package split records the membership and the gi separately.
## Tips & best practices
* **Use categories deliberately.** They drive the POS layout, so name and order them the way you want staff to find things.
* **Pick the right revenue account per product.** This is what keeps retail, rentals, and equipment revenue separated in your accounting and analytics. Leave it as default only when you genuinely want the fallback.
* **Set barcodes for anything sold by scan.** It speeds up the POS and reduces mis-rings.
* **Prefer Inactive over Delete.** Deactivating hides a product from the POS, packages, and order forms while preserving its sales history.
* **Only enable inventory when you'll maintain it.** Stock tracking is most useful for fast-moving retail and consumables; leave slow or one-off items untracked.
## Troubleshooting
**Issue**: A product doesn't appear on the point of sale.
**Solution**: Confirm the product is **Active** and its **Visibility** isn't set to Private for a context where it shouldn't be hidden. Also check the club scope - a product tied to another club won't show for the currently selected club.
**Issue**: The product shows under the wrong group on the POS, or in an "uncategorized" group.
**Solution**: Set or change the product's **Category**. Products with no category fall into the ungrouped section. Reorder categories from the **Categories** manager to control group order.
**Issue**: I can't add a product to a package or an order form.
**Solution**: Only **Active** products can be bundled into packages or attached as add-on products. Reactivate the product first.
**Issue**: The Stock section says "This product is not tracked".
**Solution**: The product has neither direct stock nor a recipe. Receive stock to start direct tracking, or define a **Recipe** of ingredients if the product is assembled. If you don't see a Stock section at all, inventory management isn't enabled under **Settings** > **Sales** > **Inventory Management**.
**Issue**: Revenue for a product is landing in the wrong account.
**Solution**: Edit the product and set its **Revenue Account** explicitly instead of **None (uses default)**. Existing sales aren't re-booked; the change applies to future sales.
## Related
* [Packages](/sales/packages) - bundle products and plans into a single fixed-price offer.
* [Plans](/sales/plans) - membership and pass templates (the other half of your catalog).
* [Equipment rentals](/sales/rentals) - lend products out and track their return.
* [Charges](/sales/charges) - how product sales are recorded and split.
* [Forms](/marketing/forms) - attach products as add-ons to turn a form into an order form.
# Promotions
Source: https://docs.1club.ai/sales/promotions
Create vouchers and discounts with schedules, codes, audience targeting, and usage caps
Promotions are time-bound offers. They come in two flavours - **Vouchers** credit a member's wallet when the member redeems a code, and **Discounts** reduce the price of a booking or product order at checkout. Both share the same controls for scheduling, codes, redemption caps, audience targeting and scope. Owners and managers create promotions, staff can apply them on a member's behalf, and members redeem them in the portal or at checkout.
## When to use it
Promotions let you change a price for a purpose - winning new members, filling quiet hours, or bringing people back - without touching your everyday prices. Each one can be scheduled, capped, and tracked in promotion analytics.
Typical uses:
* **Build a trial or intro offer** - an auto-apply discount on a plan lands at checkout for first-time buyers with no code, and the plan card shows a badge like **Try free** or **Save 20%**.
* **Fill quiet court hours** - a padel gym gives a second hour free on 2-hour daytime court bookings, and the booking calendar advertises the offer to members.
* **Bring lapsed members back** - target a "lapsed" tag with a voucher code that adds credit to the member's wallet.
* **Reward group bookings** - a discount that applies only when someone books a class for three or more people.
See [Auto-apply](#auto-apply-trials-and-intro-offers) for trials and [Example: 2 hours for the price of 1 on daytime courts](#example-2-hours-for-the-price-of-1-on-daytime-courts) for the court offer.
## Vouchers vs. discounts
Promotions are always one of two types. Pick the type when you create the promotion; you cannot change it afterwards.
| | Voucher | Discount |
| - | - | - |
| **What it does** | Adds credit to the member's wallet | Reduces the price at checkout |
| **Who triggers it** | The member (or staff on their behalf) | Applied automatically or by entering a code at checkout |
| **Value format** | Fixed amount only | Percentage **or** fixed amount |
| **Where the code is entered** | Member portal: **Account** > **Add voucher** | Booking dialog or order checkout |
| **Can be scoped to a class/area/instructor?** | No - wallet credit is universal | Yes (see **Scope** below) |
| **Effect on the ledger** | Creates a wallet transaction | Reduces the booking/order total |
| **Auto-apply at checkout** | No | Yes - see [Auto-apply](#auto-apply-trials-and-intro-offers) |
| **Can target a specific plan?** | No | Yes - via **Plan** in the Valid for section |
## Creating a promotion
1. Go to **Sales** > **Promotions** (`/marketing/promotions`). Promotions moved here from **Marketing**, so old links still work.
2. Click **Add Promotion** and choose **Voucher** or **Discount** as the type.
3. Fill in:
* **Name** - shown to staff, on the member-facing receipt and, for an auto-apply discount, on your booking calendar beside the discounted price (for example "Buy 1 get 1"). Use the translate button next to the field to give it a name in every language your site supports.
* **Code** *(optional)* - what members type. Codes are normalised to UPPERCASE and must be unique within the organization. If you leave it blank, the promotion can still be applied by staff from the dashboard but won't have a code to share.
* **Description** - shown to members where the promotion appears.
* **Value** - for vouchers, the credit amount; for discounts, either a percentage (max 100) or a fixed amount, depending on **Discount format**.
4. Set the active window, caps, visibility, audience and scope (see below).
5. **Save.**
## Scheduling (active window)
Each promotion has an optional **Starts at** and **Ends at**. The list view shows a status badge:
* **Scheduled** - created but the start time hasn't passed yet. Codes won't validate.
* **Live** - within the active window. Available to redeem.
* **Ended** - past the end time. Codes return an "expired" reason and the promotion stops accruing redemptions.
Leaving both fields empty makes the promotion live indefinitely.
## Usage caps
Two independent limits control how often a promotion can be redeemed:
* **Max redemptions** - total across all members. Once reached, the promotion stops working for everyone.
* **Max redemptions per member** - how many times a single member can use it. Must be less than or equal to the global cap.
If a booking that consumed a promotion is later cancelled, the redemption is rolled back automatically - the global counter decrements and the member's per-member allowance is freed.
## Visibility
Controls where the promotion shows up in the member portal:
* **Public** - listed publicly (e.g. signup flow), visible to non-members.
* **Member-only** - visible to signed-in members.
* **Private** - never listed; can only be applied by typing the code or by staff action. Use this for codes you distribute through email, social, or partner channels.
## Audience targeting (segment filter)
Restrict who can redeem by:
* **Member types** - only contacts of these types can redeem.
* **Tags** - only contacts carrying at least one of these tags.
Within a list the match is **OR** (any tag matches). Between lists it's **AND** (the member must match both the type filter and the tag filter when both are set). Leave both empty to allow everyone in the audience defined by **Visibility**.
## Scope (valid for) - discounts only
A discount can target two independent surfaces. List entries on either surface; the discount is valid when the purchase matches **any** declared surface.
**Plan surface - what plan is being purchased:**
* **Plan** - the discount applies only when the buyer is purchasing one of the selected plans. Used for first-purchase intro pricing, trials, and plan-specific promo codes. Required for [auto-apply](#auto-apply-trials-and-intro-offers).
**Booking surface - what is being booked:**
* **Class types** - applies only when the booking is for one of these class types.
* **Area types** - applies only when the booking targets one of these area types.
* **Instructor types** - applies only when the assigned instructor is one of these types.
* **Clubs** - applies only to bookings at the selected gyms. Leave empty for every gym. Use it when two of your gyms share the same area type but the offer is for one of them.
A promotion with both surfaces (e.g. "Plan: Annual + Class types: Yoga") applies to purchases of the listed plans **and** to bookings of the listed class types. Leaving every surface empty makes the discount valid for any booking or order (but never for a plan purchase, since plan eligibility is opt-in).
Vouchers are wallet credit, so they aren't scoped - once a voucher is redeemed the credit can be spent on anything the wallet can pay for.
## Auto-apply (trials and intro offers)
A discount can be set to **Auto-apply**, meaning it lands silently at checkout for any first-time buyer of the targeted plan. No code needed. This is how you build trials, free days, and first-month-off offers.
### How it works
1. Create a **Discount** promotion.
2. Pick one or more plans in **Valid for** > **Plan**. Auto-apply requires at least one eligible plan or booking scope; the trial and intro-offer use cases below are all plan-based.
3. Set **Value** to the discount you want (100% for a free trial, e.g. 50 for "50% off the first period").
4. Toggle **Auto-apply** on.
5. Save.
From then on, anyone buying one of those plans **for the first time** automatically gets the discount applied to the membership creation charge. No code entry, no admin step.
### First-time buyer rule
Auto-apply only fires for a contact who has **no prior membership** on any of the targeted plans - active, expired, or cancelled. Once they've ever held a membership on one of those plans, they no longer qualify. This prevents the same person re-triggering an intro offer by signing up again.
When more than one auto-apply promotion could fire for the same plan and contact, the one with the **largest computed savings** wins. Only one applies.
### How it shows up on plan cards
The public plan API computes a `firstTimeOffer` for each plan and contact, which the member-facing apps use to render badges like **"Try free"** or **"Save 20%"** on plan cards. The card shows both the original price and the effective price after the discount.
### Effect on recurring plans
For recurring plans the discount is baked into the membership's stored price at creation, so it **carries through every renewal** for as long as that membership runs. If you want the discount to lapse after the first period, use a one-time plan or a recurring plan paired with a manual price change at the renewal you choose.
### Voucher promotions can't auto-apply
Vouchers credit the wallet; they have no checkout-time line to silently discount. The Auto-apply toggle is hidden when **Type** is **Voucher**.
## Attaching promotions to a plan
The plan detail page (**Sales** > **Plans**, then open a plan) has a **Promotions** section listing every promotion whose **Valid for** > **Plan** includes this plan. From there you can:
* **Add promotion** - opens the promotion editor pre-scoped to this plan. Pick the shape you want: 100% auto-apply (trial), code-driven percentage off, fixed-amount intro, etc.
* **Edit** any attached promotion in place.
* **Delete** an attached promotion if it has no redemptions yet.
This is the most common entry point when you're building a trial for a specific plan rather than running a cross-product campaign.
## Conditions
You can also require a checkout context before a discount applies. The **Conditions** section of the promotion has:
* **Minimum people** - the discount applies only when the class booking party has at least that many people (minimum 2). Use it to reward group bookings - for example, a discount that kicks in only when someone books a class for three or more. Leave it empty for no people requirement. See [Party bookings](/schedule/classes) for how party sizes work.
* **Only at certain times** - days (optional; empty means every day) plus a start and end time, read on the gym's clock. Choose how the booking must match the window:
* **Starts in the window** - the whole booking qualifies when it starts inside the window. A 15:00 booking that runs to 17:00 qualifies for an 8:00 to 16:00 offer. This is what most offers mean by "book between 8:00 and 16:00".
* **Fits entirely in the window** - the booking must start and end inside the window.
* **Minimum length** and **Maximum length** - in minutes. Set both to the same value to target one length exactly, for example 120 for a "2 for 1" offer.
The time window and the booking length judge the whole booking: the discount applies to a booking or it does not. They are never split at the window's edges the way a [pricing rule](/settings/pricing-rules) band is.
## Time-of-day offers on the booking calendar
An auto-apply discount with booking conditions is shown to members on your booking calendar before they check out:
* A line under the calendar names the offer and its hours, for example "Buy 1 get 1 - 08:00 to 16:00".
* When a member picks a booking that qualifies, the regular price is struck through, the discounted total is shown, and the promotion's name appears beside it.
* When a member picks a shorter booking and a longer one would qualify, one line under the duration tells them - "Buy 1 get 1 - 2 h for the price of 1" - and tapping it switches to the longer booking. If the longer booking costs more, the line shows its price instead.
Signed-in members see offers they qualify for. Visitors who are not signed in don't see offers limited by an audience or to a first booking, because the calendar can't check who they are. They do see offers with a per-member limit: that limits how often someone uses an offer, not who gets it, and it is checked again when they sign in to book. Checkout always has the final say.
### Example: 2 hours for the price of 1 on daytime courts
A padel gym wants a second hour free on daytime bookings at one of its gyms:
1. Create a **Discount** with **Discount format** set to percentage and **Value** 50.
2. Name it "Buy 1 get 1" and translate the name if your site has more than one language.
3. In **Valid for**, switch to **Selected bookings**, turn on **Areas** and pick your court type. Under **Clubs**, pick the gym running the offer. An auto-apply discount needs at least one booking type, so **All bookings** can't be saved with **Auto-apply** on.
4. Under **Conditions**, turn on **Only at certain times** and set 08:00 to 16:00 with **Starts in the window**, and set **Minimum length** and **Maximum length** both to 120. Leave the days empty for every day.
5. Turn on **Auto-apply** and save.
A 2-hour booking at 10:00 that normally costs 56 is shown - and charged - at 28. A 1-hour booking stays at its regular price, and the calendar offers the second hour for free.
### Courts booked with a coach
When a court and a coach are booked together as one booking, a discount valid only for areas takes its amount off the court's share and leaves the coach's fee untouched. A discount valid for both areas and instructors applies to the whole booking.
## Stacking
Only **one** promotion can be applied per booking or order. Discounts don't stack with other discounts, and a voucher's wallet credit is paid out at checkout independently of any discount applied to the same order.
## How members redeem
### Vouchers
1. Member signs into the portal and opens **Account**.
2. Taps **Add voucher** and enters the code.
3. The credit is added to their wallet immediately and recorded as a wallet transaction labelled with the promotion name.
The wallet balance can then be spent at checkout the next time they book a class or buy a product.
### Discount codes
Members enter a discount code during checkout (booking or product order). The portal validates it live, shows the resulting savings, and applies it when they submit. Invalid codes return a generic error on the member side - this is intentional, to prevent code-guessing. Staff using the admin dashboard see the specific reason (see **Validation reasons** below).
## Staff redemption
Staff can apply promotions on a member's behalf:
* **Vouchers** - open the contact and use **Redeem voucher**. Enter the code; the credit posts to their wallet.
* **Discounts** - when creating a booking or order in the admin, paste the code into the promotion field on the dialog. The dashboard validates it inline and tells you why a code is rejected if it is.
* **Discounts in the booking dialog** - **Add discount** lists every discount; the ones that don't fit this booking are greyed out. A time-of-day or booking-length discount becomes selectable once the booking's time and length match it, and the amount shown is what checkout will take off - for a court booked with a coach and a courts-only discount, that is the court's share only.
## Analytics
On **Sales** > **Promotions**, click **View Analytics** to open the promotions dashboard, where you'll find:
* KPI cards: total promotions, live promotions, total redemptions, credit issued, discount given.
* Breakdown of redemptions by promotion type.
* A redemption-over-time trend.
* A per-promotion summary table with uses, revenue impact, discount given, members acquired, and average order value.
* A per-member summary showing who redeemed which promotion, how many times, and the cumulative amount.
## Retiring a promotion
* **Promotions with no redemptions** can be deleted outright.
* **Promotions that have ever been redeemed** cannot be deleted - this preserves the audit trail on past bookings and wallet transactions. To stop them being used, set **Ends at** to a time in the past (or to now). The status badge becomes **Ended** and validation returns "expired".
## Validation reasons (admin reference)
When validating a code in the admin (for example, on a booking dialog) you may see one of these reasons. Members see a single generic "invalid code" message, except when the code needs more people, a different time or a different booking length - those they can fix, so they are told.
* **not\_found** - code doesn't exist in this organization.
* **not\_started** - the active window hasn't begun.
* **expired** - past the end time.
* **max\_redemptions\_reached** - global cap hit.
* **max\_redemptions\_per\_contact\_reached** - this member has used it as many times as allowed.
* **member\_type\_mismatch** - the contact's type isn't in the segment filter.
* **tag\_mismatch** - the contact has none of the required tags.
* **class\_type\_not\_eligible** - discount is scoped to class types and this booking isn't one of them.
* **area\_type\_not\_eligible** - discount is scoped to area types and this area isn't one of them.
* **instructor\_type\_not\_eligible** - discount is scoped to instructor types and this instructor isn't one of them.
* **plan\_not\_eligible** - discount is scoped to specific plans and this plan isn't one of them. Also fires when the buyer is purchasing a plan and the promotion has no plan rule set (plan eligibility is opt-in).
* **booking\_not\_eligible** - the booking-surface scope is set but the booking doesn't satisfy it (catch-all when the granular reason can't be narrowed).
* **scope\_not\_eligible** - the promotion declares both plan and booking surfaces, and neither matches.
* **club\_not\_eligible** - the discount is limited to some gyms and this booking is at another one.
* **time\_not\_eligible** - the booking does not start in (or fit inside) the promotion's time of day.
* **duration\_not\_eligible** - the booking is shorter or longer than the promotion's booking length.
* **not\_first\_time\_buyer** - an auto-apply discount was evaluated for a contact who already holds (or held) a membership on one of the targeted plans.
* **wrong\_type** - a voucher code was entered at checkout (vouchers credit the wallet; they aren't applied as a discount).
## Best practices
* **Cap per-member use** for codes you intend to share publicly. A per-member limit of 1 prevents the same member redeeming a public code repeatedly.
* **Prefer Ends at over deletion.** Setting an end date is reversible and keeps the redemption history intact.
* **Use segment filters for re-engagement campaigns.** Combine a "lapsed" tag with a generous voucher to bring members back.
* **Scope discounts to fill specific inventory.** A 30% discount valid only for low-demand class types is a more efficient lever than a blanket discount. A time-of-day auto-apply discount does the same for quiet hours, and your calendar advertises it for you.
* **Use a promotion, not a pricing rule, for an offer you want members to see.** Pricing rules set your everyday prices silently. A promotion is named on the calendar and the receipt, can be capped, and shows up in promotion analytics.
* **Build trials with auto-apply, not free plans.** A 100% auto-apply discount on a paid plan converts to a real subscription at the next renewal without any admin intervention. A separate "free trial plan" requires a manual migration step to start charging.
* **For one-time intro pricing on a recurring plan, prefer a coded discount over auto-apply.** Auto-apply embeds the discount in the membership and carries it through every renewal; a one-shot intro is cleaner as a code that only applies to the creation charge.
* **Don't confuse promotions with leads.** [Leads](/members/leads) are sales opportunities tracked in your pipeline under **Members** > **Leads** - a completely separate feature from promotions.
## Related
* [Leads and pipeline](/members/leads) - sales-opportunity tracking on the **Leads** tab of **Members** (it replaced deals).
* [Plans](/sales/plans) - membership plan pricing.
* [Charges](/sales/charges) - where voucher redemptions and discount adjustments appear on a member's ledger.
# Equipment rentals
Source: https://docs.1club.ai/sales/rentals
Lend gear out at the counter instead of selling it. Rented items leave stock when they go out, come back when they are returned, and stay on an overdue list until they do.
A rental is a product you hand over rather than sell. It leaves stock when it goes out, returns to stock when it comes back, and stays on an overdue list in the meantime. Front-desk staff lend gear and take it back at the counter; managers set up rental products and write off gear that never comes back.
Rentals are handed over by a member of staff. Members cannot rent anything themselves online, and every loan is recorded against a member so you always know who has the gear.
## When to use it
Use rentals for gear you expect to see again: rackets, balls, bibs, towels, mats. A sale closes the moment it is paid. A rental stays open until the item is back, so you can see what is out, who has it, and what is late.
Typical uses:
* **A racket with a court booking** - a padel player books an hour and rents a racket on the same order at the till, and the racket is due back when the court booking ends.
* **Free bibs for a session** - a coach hands out ten bibs from the member's profile at no charge, and the loans close themselves at the end of the session.
* **Chasing missing towels** - a member checks in still holding two towels from last week, and the check-in dialog tells the front desk before they walk past.
## How it works
* **Any product can be rented.** Rental is a setting on a product, not a separate catalog. Each product is either sold or rented out, and you decide which.
* **Stock follows the item.** Going out reduces the count, coming back restores it. A rental product with no stock record is still tracked: you see who has it and when it is due, and the shelf count simply has nothing to move.
* **The item sets the due time.** Gear is due at the end of the booking it went out with, or at the end of the gym's day.
* **Partial returns are recorded as such.** Two of three towels back is recorded as two of three, and the loan stays open for the one still missing.
* **Priced gear is sold, free gear is lent.** An item with a price goes out through a sale, so the fee is always charged. An item priced at zero opens a plain loan.
## Turn rentals on
Go to **Settings** > **Sales** > **Rentals** (`/settings/operations/rentals`) and turn on **Enable rentals**. The setting is organization-wide. Once it is on, the **Rentals** tab appears under **Sales** > **Products**, on the Point of Sale, and on member profiles.
Turning rentals **off** stops new gear going out. It does not hide what is already out: the rentals list, the loans on the Point of Sale, and the check-in notice keep working so you can get the remaining gear back. Once nothing is outstanding, those surfaces disappear on their own.
## Make a product rentable
With rentals on, open any product in **Sales** > **Products** and find the **Sale or rental** section.
| Field | What it does |
| - | - |
| **Fulfilment** | **Sold** or **Rented out**. Choosing **Rented out** turns the price into the fee for one rental. |
| **Due back** | **End of the booking** (the default) or **End of the day**. With no booking to follow, an item falls due at the end of the gym's day. |
| **Return automatically** | Close the rental when it falls due, without anyone confirming the item came back. |
| **Grace period (minutes)** | How long after the due time to wait before closing it automatically. |
Switching a product back to **Sold** clears its rental terms. Rental products show a **Rented out** chip in the products list.
### What a rental product cannot do
Two restrictions follow from gear being handed over in person:
* **It cannot go in a package.** A package is sold wherever packages are sold, including online, where there is no counter to hand anything over. A product already inside a package cannot be switched to a rental either; take it out of the package first.
* **It cannot be added to an online booking.** Members adding extras to their own booking see only items that are sold.
## Lend gear out
### At the Point of Sale
Open **Point of Sale** and choose the **Rentals** tab. Gear you can lend is listed on top, separately from the things you sell, so a rental is never rung up by mistake. Scanning a rental item's barcode still adds it.
Select the customer, add the item to the order, and charge as usual. A rental always needs a customer on the order. Free gear works the same way: an item priced at zero still opens a loan.
Ringing a rental up alongside a court booking on the same order links the two, and the gear is then due back when the booking ends. This needs a single booking on the order: with two or more, 1Club cannot tell which one the gear belongs to, so it falls back to the end of the gym's day.
Gear assigned to one gym can only go out from that gym, and gear from another
gym is not listed. If the till is set to **All clubs**, pick a gym first - a loan
records where the item physically is.
### From a member's profile
On a member's profile, click **Add** and choose **Rental** to open **Lend gear**. Pick the item, the gym, and the quantity. The due time always follows the item's own rule.
* **Free gear** is lent straight away as a plain loan.
* **Priced gear** is sold: choose how the member pays, and 1Club creates and pays the order in the same step. This needs permission to take payments, and the Point of Sale must be turned on for your organization.
The member's **Rentals** tab shows what they are holding right now, with a badge that turns red when something is overdue.
## See what is out
Go to **Sales** > **Products** > **Rentals**. Four tabs split the list:
| Tab | Shows |
| - | - |
| **Out now** | Everything still out, whether or not it is due |
| **Due back** | Still out, with a due time that has not passed yet |
| **Overdue** | Still out, past its due time |
| **All** | Every rental, including returned and written-off ones |
The first three tabs carry a count, so you can see what needs chasing without opening them. Search by the item or by who holds it, and sort by item, holder, checked-out time, due time, or status. Each row shows how much is still out, who holds it, when it went out, and when it is due. A partly returned loan says so, for example "2 of 3 returned".
## Take gear back
There are two places to close a loan.
* **At the till** - the lower half of the **Rentals** tab on the Point of Sale lists what is out, grouped by who holds it, with the selected customer's gear at the top. Tick a person to take back everything they hold, or tick single items, and confirm once. The tab stays visible while anything is out, even if no gear is left to lend.
* **From the rentals list** - use the row's **Return** action on **Sales** > **Products** > **Rentals**.
Either way, returning restores the stock.
### Return part of a loan
If three bibs went out and two come back, enter **2** when asked how many are coming back. The loan stays open for the remaining one and keeps appearing on the overdue list until it arrives or is written off.
### Write a rental off
When gear is not coming back, choose **Write off** on the rentals list. The rental closes as **Written off** and stock **stays reduced**, because the item is genuinely gone. This is the one rental action that needs the delete permission, so a cashier cannot quietly make a missing racket disappear from the books.
## Automatic returns
Turn on **Return automatically** for gear that is always handed back at the end of a session, such as bibs, balls, and borrowed mats. 1Club closes those loans when they fall due, after any grace period you set.
Automatic closures are recorded as **Auto-returned** rather than **Returned**. A return someone actually saw is evidence, and a return the system assumed is not. Keeping them apart lets you tell a healthy return rate from gear quietly walking out. Leave it off for anything you need to see handed over.
If the booking a rental went out with is moved or shortened, the due time moves with it.
## At check-in
When someone checks in while still holding gear, the check-in dialog tells the front desk what they have and whether it is overdue. This is the moment most gear comes back, so the notice shows whether or not rentals are still switched on. The member's profile shows the same alert.
## Permissions
| Action | Permission |
| - | - |
| View rentals and what is out | `rentals.read.club`, or `pos.read.club` for the Point of Sale |
| Lend gear out | `rentals.create.club` |
| Take gear back | `rentals.update.club`, or `pos.create.club` at the Point of Sale |
| Write a rental off | `rentals.delete.club` |
| Turn rentals on or off | `settings.manage.organization` |
Front desk can lend gear out and take it back, but not write it off. Managers and admins can do all four.
Selling priced gear also needs the usual till permissions, since a rental with a fee is rung up like any other line.
## Related
* [Products](/sales/products) - the catalog a rental item lives in
* [Inventory and stock](/sales/inventory) - stock counts that rentals move
* [Point of sale](/pos/point-of-sale) - the till the gear goes out from
# Wallet
Source: https://docs.1club.ai/sales/wallet
A per-member store-credit balance in your gym's currency that fills from vouchers, plan bonuses, refunds, and imports, and spends like cash or a card at checkout.
The wallet is a per-member store-credit balance held in your gym's currency. Credit lands in the wallet from redeemed vouchers, plan bonuses, and refunds, and members spend it at checkout the same way they'd use cash or a card. It is money-value credit, not loyalty points. Members see and spend their balance in the portal and app, and front-desk staff can apply it as part of a payment at the point of sale.
## When to use it
The wallet lets you give money back or add value without paying anything out: the credit stays with your gym until the member spends it. Every change is logged, and the wallet-funded portion of a sale is left out of revenue reporting. If you invoice through more than one company, the credit belongs to your default one and is spent on its sales; see [Billing entities](/settings/billing-entities).
Typical uses:
* **Keeping a cancellation refund with the gym** - a climber cancels a booking that qualifies for a refund, the amount lands in their wallet, and it is applied automatically the next time they book in the portal.
* **A "pay 100, get 120" plan** - a plan carries bonus credit that is granted when the membership activates, and again at each renewal on a recurring plan.
* **A voucher promotion** - you share a voucher code, the member redeems it under **Account** > **Add voucher** in the portal, and the credit posts immediately.
* **Carrying over existing credit** - when you import contacts from a spreadsheet, a wallet column seeds each member's opening balance.
## What the wallet is
Every member (contact) can have one wallet per organization. The wallet holds a single balance in your organization's currency - the same currency you sell plans and bookings in. A member who belongs to more than one organization has a separate wallet in each.
The wallet is created automatically the first time a member earns credit; until then their balance is simply zero. There is nothing to enable.
The wallet is a monetary store-credit balance, not a points or loyalty
program. One unit of wallet credit equals one unit of your currency, and it
can be spent on anything the member can pay for at checkout.
## How credit gets added
Credit reaches a member's wallet in a few ways. Each addition is recorded as a positive entry in the [wallet ledger](#the-wallet-ledger).
### Vouchers
A **voucher** promotion adds credit to the member's wallet when a code is redeemed. The member enters the code in the portal under **Account** > **Add voucher**, or a staff member redeems it on their behalf from the contact's page. The credit posts immediately and is labelled with the promotion name. See [Promotions](/sales/promotions) for how to create and target vouchers.
### Plan-included credit
A plan can carry a bonus credit amount (for example, "pay 100, get 120 in the wallet"). When a membership on such a plan **activates**, the bonus is granted to the buyer's wallet once for that billing cycle. On a recurring plan the bonus is granted again at each renewal.
The credit follows activation, not payment. If a membership activates before its charge settles and the charge later fails, the bonus is not automatically clawed back - it is treated as an activation perk rather than part of a refundable payment.
### Refunds from cancellations
When a member cancels a booking that qualifies for a refund, the refunded amount is credited to the member's wallet rather than returned to the original payment method. The refund goes to the contact who actually paid (which, for a booking made on behalf of another member, may differ from the attendee). The refund is capped at what was actually paid for the booking. Add-on products bought in the same order are refunded to the wallet in full on cancellation.
### Imported opening balances
When you import contacts from a spreadsheet, you can include a wallet column to seed each member's opening balance. Imported balances are always credited in your organization's currency.
## Spending wallet credit
Wallet credit is a payment method. It can cover part or all of what a member owes at checkout, and it never goes negative - a debit is capped at the available balance.
### Member portal and app
In the member portal and mobile app, wallet credit is applied automatically at checkout. The order deducts the wallet balance up to the amount due, and the member pays only the remainder by card or on arrival. The breakdown shows a **Wallet** line (for example `Wallet: -10.00`) above the total, and the **Pay** button reflects the post-wallet amount. When the wallet covers the whole order, the member pays nothing further.
### Admin point of sale
When a staff member takes a payment in the dashboard using a primary method (card, cash, and so on), they can also apply wallet credit as a secondary payment through the **Also apply wallet credit ( available)** checkbox and an **Amount from wallet** field. The split appears as "Wallet + via primary method". If the wallet already covers the full amount, the toggle is hidden and the order can be paid entirely from the wallet.
The amount drawn from a wallet is always capped at the member's balance and the outstanding amount. Where the balance is too small to cover an order, the wallet option is hidden as a standalone payment method.
## The wallet ledger
Every change to the balance is written as a wallet transaction:
* **Credits** are positive amounts - a redeemed voucher, a plan bonus, or a refund.
* **Debits** are negative amounts - credit spent on a booking, product, or membership charge.
Each entry carries a description (for example, the promotion name or "Refund to wallet - booking cancellation") and, where applicable, a link to the related charge so the spend can be traced back to what it paid for. This ledger is the wallet's source of truth: the balance is the sum of its entries.
Wallet activity also surfaces on the member's [charges](/sales/charges) - a voucher redemption appears there labelled with the promotion, and a wallet-funded charge shows the credit applied.
## Where the balance appears
* **Members** see their wallet balance in the portal and mobile app, and see credit applied automatically during checkout.
* **Staff** see a member's available balance in the booking and payment dialogs, where it gates and drives the wallet payment option described above.
### Wallet balances and movements in the admin
Go to **Sales** > **Finance** > **Wallet** to see every member who holds a wallet, with their **Balance** and **Last movement**. Search by name to find one member.
The **Wallet movements** tab next to it lists every entry across all wallets, newest first, with the date, member, **Type**, description, **Payment method**, the **Staff** member who recorded it, and the amount. Tabs filter by type, such as **Voucher**, **Refund**, or **Spend**, and only types that have entries appear.
## Wallet credit and revenue
Wallet credit is store value, not new cash. When a sale is funded by wallet credit - a redeemed voucher, a comp, or a refund the member is re-spending - that portion brought in no new money, so it is excluded from revenue reporting. A part-wallet, part-card payment counts only the card portion as revenue. This keeps revenue figures reflecting actual money collected rather than credit recirculating through the wallet.
## Related
* [Payments](/sales/payments) - How wallet credit is applied against what a member owes at checkout.
* [Sales overview](/sales/overview) - How plans, invoices, payments, and credit fit together.
* [Promotions](/sales/promotions) - Create vouchers that credit the wallet, and plan bonuses.
* [Member profiles](/members/member-profiles) - Where a member's account details live.
# Area blocks
Source: https://docs.1club.ai/schedule/area-blocks
Block a court, or a whole gym, for a set period so nothing new can be booked in it - without taking the area off your schedule permanently.
A block takes an area, or a whole gym, off the bookable schedule for a specific period. Owners and managers create blocks for anything with a start and an end, and members are simply not offered the blocked slots. A block is the area equivalent of an instructor's time off.
## When to use it
Because a block has an end, the area reopens on its own - you don't have to remember to undo it. While it runs, the closed period stops new bookings on the admin schedule, the member portal and app, the marketplace, and the API. Bookings already in the period stay, so you decide who to move or cancel.
Typical uses:
* **Resurfacing a court** - close one padel court for the days the work takes, labelled "Resurfacing" on the schedule, and it takes bookings again once the period ends.
* **A public holiday or a camp** - close every area in the gym with a single entry, created weeks ahead, which also covers any court you add in the meantime.
* **A burst pipe** - block the rest of the day on the affected room, then see on the schedule who is already booked in that period and move or cancel them.
## Block or maintenance status?
Both take an area out of the booking flow, but they answer different questions.
| Use a block | Use **Maintenance** status |
| - | - |
| The area reopens on a known date | The area is out of action indefinitely |
| You want the reason visible on the schedule for that period | You want the area off the schedule entirely |
| Members should see the area again afterwards | The area should stay hidden until you change it back |
Set **Maintenance** on the area itself (**Settings** > **Clubs** > **Areas**) when a space is out of action with no end in sight. Reach for a block whenever the period is known, which is most of the time.
Do not block a period by creating a placeholder booking. A booking is a
financial record and will show up in your revenue reports. A block is not.
## Creating a block
You create blocks from **Schedule**, on the area lanes. Two routes, both on **Schedule**:
* Click **Add** in the header, then **Block**.
* Drag out the hours you want on an area's lane, then choose **Add block**. The area and the times come pre-filled from what you selected.
The dialog asks for:
* **Gym** - which gym the block belongs to.
* **What is blocked** - either a single area, or **Whole club (all areas)** to close every area in that gym with one entry.
* **Start** and **End** - the period. These default to that day's opening and closing hours, so blocking a full day needs no editing. Change either one and your times are kept from then on.
* **Reason** - free text, shown as the label on the schedule. "Resurfacing", "Camp", "Public holiday".
### Blocking a whole gym
Choose **Whole club (all areas)** rather than creating one block per court. One entry is easier to remove afterwards, and areas you add later are covered by it automatically - useful when a camp or holiday block is created weeks ahead.
## What a block looks like on the schedule
The blocked period appears as a grey, dashed card on the area's lane, labelled with your reason. It sits behind the day's real bookings rather than replacing them, so you can still see who is booked in the period you just blocked.
A gym-wide block shows on every one of that gym's area lanes in the day view. In the week and 3-day views, where all resources share one column per day, it shows as a single card so it can't crowd out the day's bookings.
Click the card to edit it, or use its menu to delete it. Deleting a block reopens the period immediately.
## Booking into a blocked period
Members cannot book a blocked period. The slots are simply not offered.
Staff who can manage bookings get a different result: the booking dialog says **Area blocked at this time** in its footer before they save, and **Add** books past it on the first click. That override exists for backfilling - recording a session that really happened before the block was entered - and the booking records who made it. Staff who can't manage bookings see that the area can't be booked, before they save.
If the blocked period is also at capacity - another booking or class already occupies it - going through requires a second step: a dialog names what's already there and requires a note before you can save. See [Warnings and scheduling conflicts](/schedule/bookings#warnings-and-scheduling-conflicts) for how that works and who can do it.
## Recurring blocks
Blocks do not repeat. A block is one concrete period.
For a pattern that repeats every week - closed every Monday morning, or a shorter Sunday - set the area's **Operating hours** instead (**Settings** > **Clubs** > **Areas**). Operating hours are the recurring schedule; blocks are the exceptions to it.
## Permissions
Reading blocks needs the same access as reading the schedule. Creating, editing, and deleting them needs permission to manage areas, which is the same permission that governs the areas themselves. Admins and managers have it by default; see [Roles and permissions](/reference/roles-permissions).
Overriding a block - or a genuine conflict - while creating a booking is governed separately, by permission to manage bookings. See [Warnings and scheduling conflicts](/schedule/bookings#warnings-and-scheduling-conflicts).
## Related
* [Areas](/settings/areas) - area setup, status, and operating hours
* [Schedule](/schedule/calendar) - the calendar blocks appear on
* [Instructors](/staff/instructors) - time off, the staff equivalent of a block
* [Areas and availability](/api-reference/channel-integration/areas-and-availability) - reading and writing blocks over the API
# Attendance
Source: https://docs.1club.ai/schedule/attendance
Record and review member check-ins in 1club - view the attendance log, add check-ins with the right entrance method, change or delete them, and handle anonymous walk-ins.
Attendance is the record of every member visit to your gym. Each check-in captures who came in, when, at which club, and how they entered - against a booking, a membership, an external program, or a direct payment. The front desk records visits here as members arrive, and owners and managers review activity over any date range.
## When to use it
The attendance log keeps visits, membership usage, and payments in one place. Recording a check-in reuses the same pricing, payment, and membership-consumption logic as booking, so a walk-in is priced and debited exactly like a pre-booked session, and a limited plan's uses count down on their own. The activity chart shows your busy days and hours, and every check-in shows exactly what it was recorded against.
Typical uses:
* **A member arrives for a booking** - a padel player who booked and paid online is checked in against their booking, with nothing to charge.
* **A walk-in on a pass** - a jiu-jitsu member with a 10-session pass turns up without a booking, and the visit takes one session off the pass.
* **A trial visitor with no profile** - a yoga studio in flexible check-in mode logs an anonymous visit and collects the drop-in fee.
* **Fixing how someone entered** - reception recorded a climber against their membership, but they paid at the door, so you change the entrance method and the session is restored.
See [Worked examples](#worked-examples) for the steps behind each of these.
Before you start: how strict your check-in flow is depends on your booking policy and access-control settings. Read [Check-in models](/reference/check-in-models) for an overview, then configure the rules in [Booking policies](/settings/booking-policies) and [Access control](/settings/access-control).
## Viewing the attendance log
Attendance lives under **Schedule** > **Attendance** (`/schedule/attendance`). The full list of check-ins is **Schedule** > **Check-ins** (`/schedule/checkins`), and individual check-in records open under it. Check-ins are not under Members.
### Open the log
1. Open **Schedule** in the left rail, then choose **Attendance** from the calendar's **More actions** menu.
2. The log lists every check-in for your organization across the selected date range.
3. Use the filter bar at the top to change the timeframe, pick a custom date range, or filter by club (the club filter appears only when you have access to more than one club).
The selected timeframe and date range are stored in the URL, so you can bookmark or share a specific view.
### Read the activity chart
Above the grid, 1club shows a chart of check-in activity:
* Over a multi-day range, a line chart plots check-ins per day.
* When you narrow the range to a single day, the chart switches to an hourly bar chart bounded by that club's operating hours, so you can see your busy periods.
### Read the grid
Each row is one check-in. The columns are:
* **Member** - Member name, avatar, and email. Rows without a member show **Anonymous**.
* **Date** - The check-in date.
* **Time** - The check-in time.
* **Reference** - What the visit was recorded against: a class, a booking (class, area, or instructor), or a membership (with remaining usage). A row can show more than one, for example a booking plus the membership that covered it.
* **Entry method** - How the member entered: **Direct Payment**, **Membership**, or **External Program**. Shown as `-` when none was recorded.
* **Actions** - Delete the check-in (requires the `checkin.delete.club` permission).
Click any row to open its [check-in detail page](#the-check-in-detail-page). Click a member's name to jump to their profile.
## The check-ins list
The **Attendance Log** answers "how busy have we been lately": it loads a date window and charts the trend. When you need to browse the whole record instead, use the **Check-ins** page at `/schedule/checkins`. Find it by searching for "Check-ins" in global search, or through the **Check-ins** breadcrumb on any check-in detail page.
The check-ins list:
* Shows every check-in in your organization, paged and searched on the server, so it stays fast however far back your history goes.
* Sorts by the day the attendance is for, newest first, so a back-dated check-in sits on its own day rather than at the top.
* Uses the same columns as the attendance grid (**Member**, **Date**, **Time**, **Reference**, **Entry method**), and a row opens the check-in detail page.
* Has the same **Add Check-in** and **Scan QR** buttons, shown when you hold the `checkin.create.club` permission.
Opening the page needs at least the `checkin.read.own` permission.
## Recording a check-in
From the attendance log, click **Add Check-in** (top right) to open the check-in dialog. What you see depends on the member's bookings and your booking policy.
Use the **Contact** picker to find the member. In a club that allows anonymous check-ins, this field is labelled **Contact (optional)** and you can leave it empty - see [Anonymous check-ins](#anonymous-check-ins).
If the member has a confirmed or pending booking for today, it appears as a selectable button - pick it to anchor the check-in to that booking. If there is no booking, choose an entrance method instead: **Use Membership**, **Pay Now** (direct payment), or **External Programs**.
When the entrance method owes money (a Pay Now walk-in, a membership with an unpaid balance, or an external program with a per-visit fee), a payment section appears. Pick how the member pays (cash, card, POS terminal, or wallet), or record the visit without a payment and settle later. If your booking policy enforces payment at check-in, the dialog says so - that rule stops members checking themselves in, not the front desk - and without a payment the amount stays owed.
Click the confirm button. 1club records the check-in, decrements membership usage if the plan is limited, and settles any payment you collected - all in one atomic step.
### Entrance methods
When a check-in is not simply anchored to an already-paid booking, you pick how the member enters. The available methods and whether one is mandatory come from the matching booking policy (see [Check-in models](/reference/check-in-models)):
* **Use Membership** - Counts the visit against one of the member's active memberships. Usage is decremented automatically if the plan has a limit. Only memberships eligible for this visit are selectable. Members can check in more than once per day - a morning and an evening session are both recorded; a quick re-scan at the door within a few minutes is treated as the same visit rather than a new one.
* **Pay Now** - The member pays for this single visit at the front desk. You can apply a discount code to a class-anchored Pay Now walk-in before collecting payment.
* **External Programs** - The member enters through a third-party program (a partner curriculum, a corporate-wellness benefit, a federation pass). If the program charges an additional per-visit fee, you collect it here. See [External programs](/settings/external-programs).
**Pre-booked sessions honor the booking.** When a booking was created against a membership, that session was already debited from the membership at booking time. Checking the member in against that booking succeeds even if the membership has since become inactive or hit zero remaining uses - the booking is the receipt. This applies only to check-ins that select the booking. Walk-ins and entrance-method changes still require an active membership with remaining uses.
### Check-in window
Members checking themselves in at the kiosk or in the check-in app can do so from 1 hour before the booking starts until the end of the booking date. Staff can check a member in outside that window - early, or after the fact - and the dialog notes that it's outside the usual window.
Checking in a booking that was already marked as a no-show is different: it can undo the no-show fee, so it stays with staff who have full check-in access across the organization (Admins by default).
## The check-in detail page
Click a row in the attendance grid or the check-ins list to open the check-in at **Schedule** > **Check-ins**. The detail page shows the date and time, when it was created, the entrance method, the member (or **Anonymous**), the club, and full references to the linked booking, class, area, and membership (with links to each and the membership's remaining usage).
From here you can:
* **Change Entrance Method** - Available on a booking-anchored check-in while the booking is still confirmed. Use it to correct how the member actually entered, for example switching a membership entry to a direct payment. Converting a paid entry to a different method triggers the appropriate refund or charge and warns you before you confirm.
* **Delete** - Remove the check-in (see below).
There is no general "edit" for a recorded check-in beyond changing its entrance method. To correct anything else, delete the check-in and record a new one.
## Deleting a check-in
Delete a check-in from the **Actions** column in the grid, or from the **Delete** action on its detail page. You need the `checkin.delete.club` permission. Confirm in the dialog to proceed.
Deleting reverses everything the check-in did, in one all-or-nothing transaction:
* Restores membership usage (credited back, capped at the plan's maximum) and reactivates a membership that had flipped to `used` or `expired`.
* Voids any pending or zero-amount charges attached to the check-in.
* Reverses any discount redemption recorded for the visit.
* Drops the external-program usage record so the program's counter stays accurate.
* Reverts a linked booking out of `checked_in` back to `confirmed`.
You cannot delete a check-in that has a paid charge attached. 1club blocks it with "Cannot delete a checkin with a paid charge. Refund the charge first." Refund the payment in [Payments](/sales/payments), then delete the check-in.
## Anonymous check-ins
When a club runs in flexible check-in mode, you can log a visit without selecting a member - useful for trial visitors and guests who do not yet have a profile. The **Contact** field becomes optional (**Contact (optional)**), and leaving it empty records the check-in as **Anonymous** in the grid and on the detail page.
Anonymous check-ins are controlled by the booking policy and the club's check-in mode in [Access control](/settings/access-control) - turn **Check-in Mode** to **Flexible** to allow them. See [Check-in models](/reference/check-in-models) for the full picture.
## QR code check-in
Click **Scan QR** on the attendance log to open the scanner. Scanning a member's check-in code validates it and checks the member in to the current club, so busy front desks can process arrivals without opening the full dialog.
## Worked examples
### Padel court booked and paid online
A member books and pays for a padel court in advance. When they arrive, open **Add Check-in**, select the member, and pick their booking for today. Because payment was collected at booking, there is nothing to charge - confirm the check-in and the booking flips to attended.
### Brazilian jiu-jitsu drop-in on a class pass
A member with a 10-session BJJ pass walks in for the evening class without a booking. Select the member, choose **Use Membership**, pick the pass, and confirm. 1club decrements one session from the pass and records the visit against it.
### Trial visitor at a yoga studio
A prospective member turns up for a trial class. The studio runs flexible check-in mode, so leave the **Contact (optional)** field empty, pick the class, and choose **Pay Now** to collect the drop-in fee. The visit is logged as Anonymous.
### Corporate-wellness member at a fitness gym
A member enters through a corporate-wellness partner. Select the member, choose **External Programs**, and pick the program. If the program charges a per-visit top-up fee, collect it in the payment section; if it is fully covered, confirm without payment. See [External programs](/settings/external-programs).
### Correcting a mis-recorded entry
Reception checked a climber in against their membership, but the member actually paid at the door. Open the check-in from the attendance log, click **Change Entrance Method**, switch to **Pay Now**, and confirm. 1club restores the membership session and records the payment.
## Tips & best practices
* **Check members in promptly** - Record the visit as the member arrives so your attendance and busy-hour charts stay accurate.
* **Watch usage on limited plans** - The Reference column shows remaining allowance for membership check-ins; keep an eye on members approaching their limit.
* **Match the entrance method to reality** - Recording the true method (membership vs. pay now vs. external program) keeps revenue and usage reporting correct. If you get it wrong, use Change Entrance Method rather than deleting.
* **Refund before deleting** - If a check-in collected a payment, refund it first, then delete. The system will not let you delete a paid check-in.
* **Use QR scanning at peak times** - The Scan QR flow is faster than the full dialog when a queue forms.
* **Narrow to a single day for busy-hour insight** - Selecting one day switches the chart to an hourly view bounded by your operating hours.
## Troubleshooting
**Issue**: A member does not appear in the contact picker.
**Solution**: Confirm the member exists in your contacts. If you are checking in a guest without an account, the club must be in flexible mode to record an anonymous visit.
**Issue**: The system says the membership has no remaining uses.
**Solution**: The plan is limited and all sessions are consumed. If the member had already booked this session against the same membership, check them in through that booking - the session was debited at booking time and the check-in still succeeds. Otherwise select a different entrance method.
**Issue**: The check-in is blocked asking for payment.
**Solution**: The matching booking policy enforces payment at check-in (or at booking with an outstanding balance). Collect payment with a method in the payment section, confirm an active membership, or apply an external program. See [Booking policies](/settings/booking-policies).
**Issue**: Check-in is not open yet, or is closed for the day.
**Solution**: For non-admin roles, booking-anchored check-in opens 1 hour before the booking starts and closes at the end of the booking date. An owner or admin can check the member in outside that window.
**Issue**: You cannot delete a check-in.
**Solution**: Either you lack the `checkin.delete.club` permission, or the check-in has a paid charge. Refund the payment in [Payments](/sales/payments) first, then delete.
**Issue**: A new check-in is not in the list.
**Solution**: Check the date range and club filter at the top - the check-in must fall inside the selected range. Rows are ordered by creation time, newest first.
## Related
* [Check-in models](/reference/check-in-models) - Book & pay, walk-in with payment, or flexible check-in.
* [Booking policies](/settings/booking-policies) - Configure payment enforcement and check-in rules.
* [Access control](/settings/access-control) - Per-club check-in mode and identification methods.
* [External programs](/settings/external-programs) - Third-party entrance methods and per-visit fees.
* [Memberships](/sales/memberships) - Plans, usage, and allowances that check-ins consume.
* [Payments](/sales/payments) - Where check-in payments and refunds are recorded.
# Bookings
Source: https://docs.1club.ai/schedule/bookings
Create, edit, cancel, and take payment on reservations for areas, classes, and instructors.
A booking is the unit of revenue. Every reservation - a member joining a class, renting an area, booking a one-on-one with an instructor, or buying an [event ticket](/events/events) - is stored as a booking row with one or more charges attached. Members book themselves from the app, portal, website, or kiosk, and the front desk and managers create bookings for them in the admin.
## When to use it
Because every reservation is a booking, staff handle them all the same way: one list, one detail page for payment, check-in, cancellation, and notes, and a full money trail of charges, payments, and refunds per booking. You create bookings in the admin for phone and walk-in reservations, and you open existing ones to take payment, check a member in, or cancel.
Typical uses:
* **A padel court booked over the phone** - reception records tonight's court for a regular, collects the balance at the desk when they arrive, and gives each doubles player their own share.
* **A personal-training session covered by a plan** - book the coach, then switch the entrance to the member's membership so the booking carries no charge.
* **A standing weekly court** - reserve the same court every Tuesday at 18:00 for the whole season in one go, then edit or cancel a single week without touching the rest.
* **A drop-in class paid in cash** - record the class booking and the payment at the desk, without sending a confirmation email the member doesn't need.
See [Worked examples](#worked-examples) for the steps behind each of these.
If you haven't picked a check-in flow yet, start with [Check-in models](/reference/check-in-models). The payment and cancellation rules are set per [booking policy](/settings/booking-policies).
## The list page
The list page lives under **Schedule** > **Bookings** (open it from the calendar's **More actions** menu); the detail page is where you handle payment, check-in, cancellation, and notes.
The bookings list has four tabs with live counts: **All**, **Upcoming**, **Past**, and **Cancelled**. Upcoming is the default tab. Each tab is paginated and sortable; the cancelled tab uses a dedicated endpoint with a leaner response shape.
Click any row to open the booking detail page. Click **Add booking** in the top right to create one from scratch.
## Creating a booking
The booking dialog has three sections:
* **Contact** - pick the member from your contacts. If the matching [booking policy](/settings/booking-policies) allows anonymous bookings, the contact is optional and admins can record bookings for people who don't have a profile yet.
* **Type** - pick **Area**, **Class**, or **Instructor**. Each routes to its own selector (gym filter applies). Selecting a class also locks the date/time to the class occurrence.
* **Schedule** - start time, end time, and notes. The booking duration step from the policy controls how the end time snaps to allowed lengths.
The price breakdown appears below the type section. It shows the line item (area price per hour, instructor price, or class price), any promotion or manual price adjustment, and the total. If membership benefits or external programs cover the booking, that's reflected here.
At the end of the create form, **Send confirmation email** controls whether the member gets the confirmation. It's checked by default; uncheck it when you're recording a booking that's already been communicated out-of-band. The checkbox is only on create - editing an existing booking does not re-send confirmation.
Bookings created from the admin dashboard are confirmed without payment, whatever **Enforce payment** says, so you can record a reservation that will be paid later. The balance stays owed until you collect it. See [Booking policies](/settings/booking-policies) for the three enforcement modes.
## Warnings and scheduling conflicts
The booking dialog tells you what a slot crosses before you save, on one line on the left of the footer, next to **Add**. The footer stays in view while you scroll, so the note is there when you click. There are three kinds of note.
* **A rule for members** - the slot is outside the [booking policy](/settings/booking-policies)'s notice, advance-booking, duration, or grid limits, outside the working hours, outside who the area or class is open to, or the member's deposit is short of a deposit rule. These rules bind members booking for themselves, not staff. The note reads like "Members book up to 7 days ahead", the button stays **Add**, and the first click books it. The working hours, the audience, and the deposit are checked when you save, so the confirmation names them if they apply.
* **The coach's schedule or a blocked area** - the instructor is on time off, on a break, or outside their working hours, or the area is blocked. The note reads like "Instructor on time off", and **Add** books past it on the first click. The booking records the override: who made it, and what it went past.
* **A genuine conflict** - the court or the coach is already booked for that time. This opens a dialog that names exactly what's being overlapped (an existing booking, a class, a blocked period, time off, or a break) and requires a note before you can save. The note is stored on the booking and shown on its calendar card, so anyone glancing at the schedule can see the overlap was deliberate - useful for something like two half-court groups sharing one tennis court, or a one-off exception the team has agreed to.
A live event taking over an area or an instructor is never overridable - that stays a hard stop.
Who can book past what:
* **The coach's schedule** - anyone who can create bookings, including Front Desk and Instructor.
* **A blocked area, or an already-taken slot** - only staff who can manage bookings (**Full access** on the Booking module, Club scope or higher). The same split applies to classes, gated by the Classes module instead.
* **A slot that has already ended** - only staff who can create bookings at Organization scope, so a past session can be recorded.
When your role can't book past something, the note in the footer turns red and says so before you save, rather than after. See [Roles and permissions](/reference/roles-permissions).
If something changes after the dialog checked the slot - time off entered a moment ago, say - and your role can book past it, the save still goes through and the confirmation names what it booked past. You are never asked to press the same button twice.
## The booking detail page
The detail page header shows the booking title with status chips (confirmed, pending, cancelled, paid/unpaid) and a row of actions: **Add payment**, **Notify participants**, **Edit**, **Cancel**, and (when the booking is `pending`) **Confirm**. Once a booking is confirmed and within the check-in window, an **Add check-in** action also appears.
The body uses a two-column layout. The left column is the info table: created at, created by with the booking **source** (one of `admin`, `portal`, `app`, `network`, `website`, `kiosk`, `integration`, `api`), customer or coach, recurrence summary, entrance method, and any discount. The right column lists **Players** for area bookings whose area type allows multiple participants (each player can carry their own per-share payment) and shows a yellow note card if the booking has notes.
When the booking is part of a multi-line order, the order summary is rendered alongside the info table so you can see the other lines and the order total.
Cancelled bookings show a red alert at the top with the cancellation reason, timestamp, and the user who cancelled.
Event-ticket bookings are titled with the event name, the order line item names the event and ticket type, and the booking's charge reference reads `Event: {title} - {ticket}`. See [Events](/events/events) for the attendee list and check-in.
Below the info layout, three tabs hold the booking's financial and audit history:
* **Charges** - every charge tied to the booking, with payment status and remaining balance.
* **Payments** - every payment across those charges (most useful for split or partial payments).
* **Bookings** - only shown for recurring bookings; lists sibling occurrences in the same recurrence series.
When you switch a booking's entry to a **membership** or an **external
program**, the booking's total price is reset to 0. A membership has no
per-booking charge, and an external program bills through its own per-visit
charge - so the booking itself carries no price. Switching back to a paid
entry recalculates the price.
## Paying for a booking
The **Add payment** action opens the payment dialog. If the booking has one outstanding charge, the amount is editable and defaults to the remaining balance. If the booking has multiple outstanding charges (for example, a recurring series or a multi-line order), the amount is locked to the sum of all remaining balances and the server settles them atomically. Partial multi-charge payments are not supported - use the Charges tab to pay them individually.
The right-click and mobile-tap menus on the [Schedule](/schedule/calendar) page expose the same **Add payment** action without opening the booking.
## Cancellations
The cancel action opens a dialog where you record a reason. The refund follows the [cancellation rules](/settings/booking-policies) on the matching policy: tiered refund percentages based on how many hours in advance the cancellation happens. If no cancellation rules are configured on the policy, the cancellation refunds 100%.
The cancellation creates a reversal of the booking's charge (visible on the Charges tab) and emits a cancellation notice to the member. The booking row moves to the **Cancelled** tab.
## Recurring bookings
When the booking type is area or instructor, you can set a recurrence pattern in the dialog: how often it repeats, and how the series ends - **Until date**, **After N times**, or **Never**. Editing a recurring booking lets you change the current instance only, or apply changes to all future occurrences. Deleting offers the same scoping.
A series that ends on a date or after a set number of repeats is created in full when you save, so a court held for a season shows as occupied for every one of those dates straight away. **Never** keeps a rolling eight-week horizon instead. A series can cover up to 520 occurrences; see [Repeating schedules](/reference/repeating-schedules).
Each occurrence is priced and checked for availability on its own date. An occurrence that cannot be placed is skipped rather than double-booked - the court is blocked or already taken at that time, or the class it attends is full or does not run that day.
The **Bookings** tab on the detail page lists every sibling occurrence in the recurrence series.
## Worked examples
### Padel court booked over the phone
A regular calls to book a court for tonight. Reception opens **Add booking**, picks the contact, sets **Type** to **Area**, selects the court, and sets the hour. The price breakdown shows the court's per-hour rate. Because the booking is admin-created, **At booking** payment enforcement is bypassed - the reservation is recorded and the balance is collected at the desk when they arrive. For a doubles game, add the other players so each guest seat carries its own share.
### Court lesson with any available coach
A member calls for a lesson on court 3 tonight and doesn't mind which coach teaches it. Reception opens **Add booking**, selects the court, and sets **Instructor** to **Any instructor**. The booking holds the court and is priced as the court alone, and no coach's time is reserved. On the schedule it shows **Any instructor** in its own color, so the desk can see at a glance which lessons still need a coach.
The booking's detail page shows **Instructor: Any instructor** until a coach is named. When a coach is free, open the booking, click **Edit**, and pick them under **Instructor**. The booking stops waiting and is re-priced as a court with that coach. If that coach later drops out and is taken off the booking, it goes back to **Any instructor**, because the member still wants a lesson. **No instructor** is the choice for a court booking nobody asked a coach for, and choosing it withdraws the request. **Any instructor** needs a court, so pick the area first.
### Personal-training session
To book a one-on-one, set **Type** to **Instructor** and pick the coach. The instructor's session price seeds the breakdown. If the member's plan covers PT, switch the entrance to their membership and the booking price resets to 0.
### Standing weekly court
A member wants the same court every Tuesday at 18:00 for the season. Create an **Area** booking, turn on recurrence with a weekly pattern, and set **Ends** to **Until date** with the last Tuesday of the season. Every one of those Tuesdays is reserved when you save, so the court is visibly occupied for the whole season rather than just the next few weeks. The **Bookings** tab lists every occurrence; edit or cancel one week without touching the rest, or apply a change to all future weeks.
### Recording a class booking that's already paid
A member paid cash at the desk for a drop-in yoga class. Create a **Class** booking, add the payment on the detail page, and uncheck **Send confirmation email** if they were already told in person.
## Tips & best practices
* **Use admin bookings for phone and walk-in reservations.** They are not held to upfront-payment enforcement, so you can record the booking now and collect at check-in.
* **Check the price breakdown before saving.** It's the fastest way to confirm a membership or external program is covering the visit, or that a promotion applied.
* **Pay multi-charge bookings from the right place.** For a single balance, use **Add payment**; for individual lines of a recurring series or multi-line order, use the **Charges** tab.
* **Cancel with a reason.** The reason and refund are recorded and the member is notified automatically, which keeps disputes clean.
* **Prefer entrance switching over manual price edits.** Setting the entrance to a membership or external program zeroes the price correctly, rather than hand-editing the total.
## Troubleshooting
**Issue**: The check-in dialog says the gym asks for payment at check-in.
**Solution**: The matching [booking policy](/settings/booking-policies) enforces payment at check-in (or at booking with an outstanding balance). That rule binds members checking themselves in, not you. Take payment with **Add payment**, confirm an active membership, or apply an external program - or check the member in anyway and the amount stays owed.
**Issue**: The booking price is 0 and I expected a charge.
**Solution**: The entrance method is set to a membership or external program, both of which carry no per-booking charge. Switch the entrance to a paid method to recalculate the price.
**Issue**: The end time won't snap to the length I want.
**Solution**: The **booking duration step** on the policy controls allowed lengths. Adjust the step (or min/max slot) on the matching [booking policy](/settings/booking-policies).
**Issue**: A partial payment across several charges was rejected.
**Solution**: Partial multi-charge payments aren't supported from **Add payment** - it settles all balances atomically. Pay the lines individually from the **Charges** tab instead.
**Issue**: The member didn't get a confirmation email.
**Solution**: **Send confirmation email** is only offered on create, and editing never re-sends it. If it was unchecked, notify the member from **Notify participants** or [Compose](/inbox/compose).
**Issue**: I get "This area is already booked for this slot" (or the instructor equivalent) and can't save over it, or the footer says a blocked area can't be booked.
**Solution**: Booking into an already-taken slot or a blocked area needs **Full access** on Booking (or Classes, for a class) at Club scope or higher - Front Desk and Instructor don't have it by default. Grant it in the [permissions editor](/reference/roles-permissions), or make the change from a Manager or Admin account.
## Related
* [Booking policies](/settings/booking-policies) - Payment timing, cancellation refunds, lead times.
* [Schedule](/schedule/calendar) - The calendar where these bookings show up.
* [Classes](/schedule/classes) - Class-bound bookings have their own list and detail flow.
* [Events](/events/events) - Event tickets become bookings with their own attendee list and check-in.
* [Payments](/sales/payments) and [Charges](/sales/charges) - Money side of every booking.
* [Check-in models](/reference/check-in-models) - Entrance method that satisfies payment-at-check-in.
# Calendar
Source: https://docs.1club.ai/schedule/calendar
The unified day/week calendar that drives bookings, classes, instructor time off, and check-in.
The calendar is the hub of the **Schedule** section. It pulls classes, bookings, and instructor time off for the visible date range and renders them as colored blocks across resource columns. The front desk runs the day from it, managers plan the week on it, and instructors see a streamlined view of their own teaching schedule.
## When to use it
Most reservations and check-ins start here. Click an empty slot and the right dialog opens pre-filled for that court, instructor, or class type, and right-click a block to check someone in, take a payment, or cancel without leaving the grid.
Typical uses:
* **Build tomorrow's class grid fast** - drop a class into each start time, then copy a popular class like your 18:00 vinyasa to the other days that run it.
* **Reschedule a padel court booking** - a member asks to move their Tuesday court an hour later, and you drag the block to the new slot.
* **Front-desk check-in during class** - as members arrive for the 09:00 class, check them in straight from the class block.
* **Declutter a multi-court venue** - a racket center with a dozen courts and several coaches shows only the courts the desk is working.
See [Worked examples](#worked-examples) for the steps behind each of these.
## Layout
Open **Schedule** in the left rail; the calendar is the page it lands on.
The calendar has two view types saved per browser: **Day** and **Week**.
**Day view** is a vertical time grid with one column per resource, grouped left-to-right by type: class-type columns first, then a fallback "Classes" column, then instructor columns A-Z, then area columns grouped by area type. The time axis is built from your organization's operating hours, in 30-minute increments.
**Week view** is a horizontal grid with days as columns. Resources are merged - you see all activities for the week, not one column per resource - because the day-column layout would be unreadably wide.
The mini calendar on the left navigates dates; the **Today** button returns to today. The selected date is pushed to the URL as `?date=YYYY-MM-DD` so you can share a link to a specific day.
## Resources and what shows up where
The schedule reads three live data sources for the visible range:
* **Classes** - shown as blocks on their class-type column (or the fallback "Classes" column for classes without a type). Each class with assigned instructors also produces a mirror "teaching" block on each instructor's column in day view, so the instructor reads as busy.
* **Bookings** - shown on the area column if they have an area, on the instructor column if they have an instructor (both, if they have both), or on the relevant class-type column if they're class-bound. Class-bound bookings are suppressed in week view because the class itself already represents them.
* **Instructor time off** - shown as grey blocks on the instructor's column.
An hour that a [season pass](/schedule/season-passes) paid for carries a small season icon labelled with the season's name, so a contracted week reads as an hour of a prepaid contract rather than a casual booking. Makeup hours replayed on another date carry the same marker.
Live [events](/events/events) also render on the calendar as read-only cards: single-day events at a gym appear in their areas' lanes (or under their instructors when no areas are assigned), while multi-day and organization-wide events appear as chips in a banner above the grid. Event cards link to the event detail page and don't reserve the area they're shown in.
Active areas (`status=active`) and bookable instructors (`isBookable=true`) are the only ones included. Inactive or non-bookable resources don't get a column.
## Filtering what you see
The **Resource filter panel** in the calendar header lets you toggle individual resources on or off. Your overrides are saved per view (day vs week) in browser storage.
The organization-level defaults for what's hidden come from **Settings** > **Bookings** > **Schedule Display** (the **Default visibility** card), which lets you hide entire class types, instructor types, or area types, plus a flag for uncategorized instructors and areas. The toggle in the filter panel layers on top of these defaults: if you flip a resource to a state that matches the default, the override drops so future settings changes still take effect.
## Coloring
Two coloring modes can be toggled on the same **Settings** > **Bookings** > **Schedule Display** page:
* **Staff coloring** - when on, a booking takes the color of the instructor assigned to it. When off, blocks use the resource's configured color (or a hash of the resource name as a fallback). The same section sets the color of **Any instructor** bookings - court bookings still waiting for a coach - so they stand out from ordinary court bookings. The card also says **Any instructor** in words, so it reads the same with staff coloring off.
* **Source coloring** - when on, a second border color is added that encodes the booking **source** (admin, portal, website, kiosk, etc.). Source colors are configurable on the same settings page.
## Creating activities
Click an empty time slot to open the create dialog. The dialog is pre-filled based on which column you clicked:
* Click a class-type column to open the class dialog with the class type and time range pre-set.
* Click an area column to open the booking dialog with the area and time range pre-set.
* Click an instructor column to open the booking dialog with the instructor and time range pre-set.
You can drag-select multiple consecutive slots to extend the duration before the dialog opens.
In **week view**, clicking a slot opens a small menu instead of a dialog so you can pick between **Add booking**, **Add class**, or **Add time off** (the last option is shown only on instructor columns).
The same slot menu offers **Add Season Pass** when the date you clicked falls inside a season that is on sale at that gym, and you hold the permission to create memberships. It opens the season sale drawer with that hour pre-filled, so the whole run of weeks is sold from the slot the member asked for. See [Season passes](/schedule/season-passes).
The header has an **Add** button that opens a menu with **Booking**, **Class**, **Event**, **Checkin**, **Time off**, and **Block** (each shown only when you hold the permission for it). Next to it, the **More actions** menu links to the [Bookings](/schedule/bookings) list, [Classes](/schedule/classes) list, [Attendance](/schedule/attendance), [Areas](/settings/areas) (under **Settings** > **Clubs**), and **Configure**, which opens the Schedule Display settings. **Event** creates a draft [event](/events/events); the events list itself lives in the **Events** section.
## Acting on existing blocks
Click a block to open the matching dialog: bookings open the booking dialog, classes default to the add-booking flow for that class (so the most common case - someone wants to book into the class - is one click), time-off entries open the time-off dialog.
Right-click a block (or use the mobile tap menu) for the full set of actions: **View** (open the detail page), **Edit**, **Cancel**, **Delete**, **Add check-in**, **Add payment**, **Add booking** (for classes), and **Notify participants**.
**Add payment** is smart about split balances: if the booking has one outstanding charge, you get a single-charge payment dialog with an editable amount. If it has multiple, the amount is locked to the full sum and the server settles them atomically. See [Bookings](/schedule/bookings) for the full story.
## Duplicating a class
To copy a class to another time without reopening the class dialog, hold **Option** (Mac) or **Alt** (Windows) and drag the class block to a new slot, then release.
While you drag with the key held, the calendar shows that you are copying rather than moving: the cursor becomes a copy cursor, the source block gets a green outline and a small copy badge, and the target slots highlight in green. Let go of the key mid-drag to fall back to a normal move.
The result is a single one-off class at the drop time. It keeps the original's name, description, instructors, area, capacity, price, visibility, color, and class type, but never its recurrence, and it starts with no bookings or check-ins. The copy appears the instant you drop it, and a **Class duplicated** toast offers an **Undo** action that removes it again.
The gesture works with a mouse and keyboard and applies to classes only - bookings and time-off blocks keep their normal drag-to-reschedule behavior. A dismissible hint in the calendar header points it out until you close it.
## Bookings created with a member pre-selected
If you arrive at the schedule from a member profile with **Book a slot**, the URL carries `?contactId=X`. A toast prompts you to pick a slot, and every booking dialog you open from a slot click is pre-filled with that contact.
## Instructor-only and mixed-role views
Instructor-only accounts see a streamlined **My teaching schedule** view that hides resources they don't own. Mixed-role accounts (instructor + manager or admin) can open either the full **Schedule** page (described above) or their personal **My teaching schedule** through the instructor menu in the sidebar.
## Worked examples
### Build tomorrow's class grid fast
Open Day view on tomorrow, click the class-type column at each start time to drop in a class, then **Option/Alt-drag** a popular class (say your 18:00 vinyasa) to the other days that run it. Each drop is a fresh one-off class with the same name, instructor, area, and capacity but no bookings yet.
### Reschedule a padel court booking
A member asks to move their Tuesday court an hour later. Find the block on the court's column, drag it to the new slot, and confirm the old and new time in the prompt. The resource and duration stay the same; only the time shifts.
### Front-desk check-in during class
As members arrive for the 09:00 class, right-click the class block and choose **Add check-in** (or click the block, which defaults to the add-booking flow for walk-ins). No need to open the class detail page.
### Declutter a multi-court venue
A racket center with a dozen courts and several coaches can be noisy in Day view. Use the **Resource filter panel** to show only the courts you're working, saved per view so the desk keeps its layout.
## Tips & best practices
* **Day view for the desk, Week view for planning.** Day view gives you per-resource columns for live operations; Week view is better for seeing the whole week at a glance.
* **Option/Alt-drag to clone, plain drag to move.** Release the modifier mid-drag to fall back to a move; the copy cursor and green highlight tell you which mode you're in.
* **Share a day with a link.** The selected date is in the URL (`?date=YYYY-MM-DD`), so you can send a colleague a specific day.
* **Let settings own the defaults.** Set org-wide visibility under **Settings** > **Bookings** > **Schedule Display**; use the filter panel only for per-session tweaks, since matching the default drops your override.
## Troubleshooting
**Issue**: A court or coach is missing from the calendar.
**Solution**: Only **active** areas and **bookable** instructors get a column. Check the area status and the instructor's **Bookable** toggle. Also confirm the resource's type isn't hidden under **Settings** > **Bookings** > **Schedule Display**, or re-enable it in the resource filter panel.
**Issue**: A class booking doesn't appear in Week view.
**Solution**: This is by design. Class-bound bookings are suppressed in Week view because the class block already represents them. Switch to Day view to see individual bookings.
**Issue**: Block colors aren't what I expect.
**Solution**: Coloring depends on the modes under **Settings** > **Bookings** > **Schedule Display**. With **staff coloring** on, blocks use the assigned instructor's color, not the resource color, and **Any instructor** bookings use the color set for them. Turn it off to color by resource.
**Issue**: My duplicate-drag just moved the class instead of copying it.
**Solution**: Hold **Option** (Mac) or **Alt** (Windows) for the whole drag. If you release the key before dropping, it falls back to a move.
## Related
* [Bookings](/schedule/bookings) - The list view and detail page for reservations.
* [Classes](/schedule/classes) - Class creation, capacity, and attendance.
* [Events](/events/events) - One-off ticketed events that render as read-only cards.
* [Season passes](/schedule/season-passes) - Sell a whole season of a weekly court hour from a slot.
* [Booking policies](/settings/booking-policies) - Rules that govern payment timing and cancellation.
* [Check-in models](/reference/check-in-models) - Entrance flow your front desk should follow.
# Classes
Source: https://docs.1club.ai/schedule/classes
Create one-off or recurring classes, assign instructors, manage capacity, and run check-in and waitlists.
A class is a scheduled group activity with a capacity, optional price, one or more instructors, and an area. Owners and managers schedule classes, instructors and the front desk run attendance and check-in, and members book against a class occurrence - if the class is full they go on the waitlist.
## When to use it
Use a class for a group session with a single price per seat, whether it runs once or every week of the term. Capacity is enforced for you, and a seat that frees up 2-12 hours before the class is offered to the next person on the waitlist. For a one-off occasion with several ticket tiers or its own page, use an [event](/events/events#events-vs-classes) instead.
Typical uses:
* **A yoga class that fills up** - a vinyasa or reformer Pilates class capped at your mat count, with a drop-in price and a waitlist that offers freed seats to the next in line.
* **A free community kids' martial-arts class** - a free BJJ session where staff log attendance on the mat with no payment step.
* **A weekly spin class for the term** - one series created in full when you save, so you can move one week's class without disturbing the rest.
* **A padel clinic with friends** - a member books spots for themselves and friends in one booking, with each guest seat charged the drop-in rate.
See [Worked examples](#worked-examples) for how to set up each of these.
## The list page
The list page is under **Schedule** > **Classes** (open it from the calendar's **More actions** menu); the detail page is where you run attendance, check-in, and waitlist conversion.
The classes list has four tabs with live counts: **All**, **Active**, **Cancelled**, and **Completed**. Active is the default. Each tab is paginated and sortable. Multi-select is enabled - pick rows and use the bulk delete action.
Click any row to open the class detail page, or **Add class** to create one.
## Creating a class
The class dialog asks for:
* **Name**, **Description**, **Class type**, and **Sport**. Class types are managed under **Settings** > **Clubs** > **Class Types**; the type controls cancel-if-zero-attendance behavior and the revenue account the class books to.
* **Gym** and **Area** - where the class is held. The area selector is filtered to the chosen gym.
* **Instructors** - one or more, picked from your bookable instructors.
* **Date and time** - start and end. Multi-day classes are supported (the detail page formats the range accordingly).
* **Max capacity** - leave blank for unlimited. The class is never "full" while capacity is unlimited.
* **Price** - per booked seat, in your organization's currency. Set to `0` for a free class.
* **Visibility** - **Public**, **Member only**, or **Private**.
* **Recurrence** - turn on to repeat. Pick the pattern (daily, weekly with specific weekdays, monthly) and how the series ends: **Until date**, **After N times**, or **Never**.
A recurring class is stored as a series of N separate class occurrences, each with its own bookings, attendance, and check-ins. Editing or deleting offers a scope choice: **This instance** or **All future**.
A series that ends on a date or after a set number of repeats is created in full when you save, so the whole term shows on the schedule immediately. **Never** keeps a rolling eight-week horizon and extends itself each night, because there is no end to create up to. See [Repeating schedules](/reference/repeating-schedules) for the full model and the 520-occurrence limit.
## The class detail page
The header carries the class name, status chips (active/cancelled/completed plus time status), the visibility chip, and the class type chip. The action row exposes **Notify participants** (when the class has bookings), **Edit**, **Cancel**, and **Delete**.
The body is a two-column layout. The left card is **Information**: instructors (linked to their profiles), date and time with duration, gym, area, price, recurrence summary, review rating, sport, and description. If the class has images, a sortable gallery appears below the card.
The right column starts with three capacity tiles: **Max capacity**, **Current attendance**, and **Remaining seats**. Below them is the **Attendance** card with three action buttons:
* **Add check-in** - opens the check-in dialog. Available to anyone who can record check-ins, inside or outside the check-in window; it's disabled only for a cancelled class (see [Attendance](/schedule/attendance#check-in-window)).
* **Add to waitlist** - only enabled when the class is full (capacity set and current bookings >= max).
* **Add booking** - disabled when the class is full.
The attendance grid shows three groups: bookings (non-cancelled), anonymous check-ins (check-ins without a booking), and waitlist entries. The capacity is refetched every minute to keep counts current.
## Cancelling vs deleting
**Cancel** changes the class status to cancelled, leaves the row visible on the Cancelled tab, and notifies booked members. Cancellation refunds follow the cancellation rules on the matching [booking policy](/settings/booking-policies).
**Delete** removes the class. For a recurring class, you choose whether to delete just this instance or all future instances. Deleting fails if the class has bookings - cancel or move them first.
The right-click menu on a class block in the [Schedule](/schedule/calendar) page exposes the same cancel and delete actions.
## Waitlist
When a class is full, members can join the waitlist (from the portal) or admins can add them via **Add to waitlist** on the detail page. When a seat opens 2-12 hours before the class, the first person on the waitlist gets a notification with a claim window. The default claim window is 15 minutes and is configurable on the [booking policy](/settings/booking-policies).
Past the claim window, the offer rolls to the next person. Staff can manually move a waitlist entry to a booking from the attendance grid, even after its claim window has passed.
## Notify participants
The **Notify participants** action collects the contact IDs of all confirmed bookings and opens the [Compose](/inbox/compose) page pre-populated with the recipients. Use this for last-minute class changes, location swaps, or substitute instructors.
## Party bookings
Members can reserve more than one seat in a class in a single booking - for example, a parent bringing friends along. When booking, they pick how many seats using the **Book for** dropdown (options like "Me", "Me and another person", "Me and 2 others", plus any household members).
Each extra seat becomes a separate guest booking attached to the same order. The booker's own seat keeps its price (including any membership coverage); each guest seat is charged the class drop-in rate. All seats count toward the class capacity.
### Max party size
Party bookings are controlled per class type by **Max party size**, configured in **Settings** > **Clubs** > **Class Types**. It is the maximum number of people a member can reserve for a class at once, including themselves. The in-app help reads: "Maximum people a member can reserve for this class at once, including themselves."
The default is **1**, which effectively turns party bookings off. Raise it to allow members to book additional seats. You can also create a promotion that only applies when a party reaches a minimum size - see [Promotions](/sales/promotions).
## Worked examples
### Capacity-limited yoga class with a waitlist
Create a reformer Pilates or vinyasa class, set **Max capacity** to your mat count, and leave a price for drop-ins. When it fills, members join the waitlist; if a seat frees up 2-12 hours before, the first in line gets a claim window (default 15 minutes, set on the [booking policy](/settings/booking-policies)).
### Free community kids' martial-arts class
For a free kids' BJJ session, set **Price** to `0` and pair the class type with a [booking policy](/settings/booking-policies) whose **Enforce payment** is **None**, so staff simply log attendance on the mat with no payment step.
### Recurring weekly spin class
Turn on **Recurrence**, pick weekly with the right weekdays, and set **Ends** to **Until date** with the last day of the term. Assign the instructor and area; every week from now to that date becomes an occurrence, created the moment you save, so the court reads as occupied for the whole term. Editing offers **This instance** or **All future**, so you can move one week's class without disturbing the series.
### Padel clinic with friends
Raise **Max party size** on the clinic's class type so a member can book several spots at once. Their own seat keeps any membership coverage; each guest seat is charged the drop-in rate, and all seats count toward capacity.
## Tips & best practices
* **Configure the class type first.** It controls cancel-if-zero-attendance behavior, the revenue account the class books to, and the **Max party size** - set these before creating classes.
* **Use capacity plus waitlist for popular classes** so freed seats auto-offer to the next member instead of going empty.
* **Cancel, don't delete, a class with bookings.** Cancel keeps the record, refunds per policy, and notifies members; delete is blocked while bookings exist.
* **Leave capacity blank for open classes** that never fill - the class is never "full" and the waitlist stays off.
* **Use Notify participants for last-minute changes** (room swap, substitute instructor) - it opens Compose pre-addressed to confirmed bookings.
## Troubleshooting
**Issue**: I can't delete a class.
**Solution**: Deleting is blocked while the class has bookings. Cancel it instead (which notifies members and refunds per policy), or move the bookings first.
**Issue**: The **Add booking** button is disabled.
**Solution**: The class is full (capacity set and reached). Use **Add to waitlist**, or increase **Max capacity** if you can take more.
**Issue**: Members can't book more than one seat.
**Solution**: The class type's **Max party size** is 1. Raise it under **Settings** > **Clubs** > **Class Types** to allow party bookings.
**Issue**: A recurring edit changed every week when I meant just one.
**Solution**: Editing a recurring class asks for a scope - choose **This instance** rather than **All future**.
**Issue**: Only the next couple of months of a recurring class appear on the schedule.
**Solution**: The series ends in **Never**, which keeps a rolling eight-week horizon. Edit it with the **All future** scope and set **Ends** to **Until date** with the real last day. See [Repeating schedules](/reference/repeating-schedules).
## Related
* [Events](/events/events) - For one-off ticketed happenings with price tiers, use an event instead of a class.
* [Bookings](/schedule/bookings) - Class bookings show up here too.
* [Booking policies](/settings/booking-policies) - Payment timing and refund tiers per class type.
* [Schedule](/schedule/calendar) - Classes render as colored blocks on the calendar.
* [Attendance](/schedule/attendance) - Cross-class attendance history per member.
# Schedule
Source: https://docs.1club.ai/schedule/overview
The Schedule section: the day and week calendar, plus the Bookings, Classes, Attendance, and Check-ins lists your front desk runs the day from.
**Schedule** is where you run the gym day to day. One calendar shows classes, bookings against areas and instructors, instructor time off, and area blocks, and the same booking dialog records reservations whether they happen at reception, on the public site, or over the phone.
## What's in the section
Open **Schedule** in the left rail to land on the calendar. The other pages open from the calendar's **More actions** menu in its header, from links on detail pages, or from global search.
| Page | What it's for |
| - | - |
| [Calendar](/schedule/calendar) | The day and week grid. Create, move, and act on bookings, classes, time off, and blocks. |
| [Bookings](/schedule/bookings) | The list of every reservation, with tabs, search, and bulk actions, plus each booking's detail page. |
| [Classes](/schedule/classes) | The list of scheduled classes and each class's attendance, check-in, and waitlist. |
| [Attendance](/schedule/attendance) | The **Attendance Log**: recent check-ins with an activity chart. The full, paged **Check-ins** list (`/schedule/checkins`) and each check-in's detail page are covered on the same page. |
Related features that run on the calendar:
* [Waitlists](/schedule/waitlists) - who gets a seat when a full class frees up.
* [Area blocks](/schedule/area-blocks) - take a court or a whole gym off the bookable schedule for a set period.
* [Season passes](/schedule/season-passes) - sell the same weekly court hour for a whole season as one prepaid contract.
Two things that used to sit next to the schedule now live elsewhere. **Areas** are configured under **Settings** > **Clubs** > **Areas** (see [Areas](/settings/areas)); the calendar's **More actions** menu still links there. **Events** have their own **Events** section in the rail (see [Events](/events/events)), though live events still show on the calendar as read-only cards.
## How the pieces fit together
A booking is the unit of revenue. Every reservation - a member joining a class, booking an area, or buying a personal-training session - becomes a booking row with a charge attached. The calendar reads bookings, classes, instructor time off, and area blocks for the visible date range and renders them as colored blocks on the resource grid.
Classes are scheduled occurrences with capacity and instructors. When a member books a class, a booking row is created against that class. If the class fills up, the waitlist takes over (see [Booking policies](/settings/booking-policies) for the claim window).
A check-in records that someone actually came in. It can be anchored to a booking, or recorded against a membership, a direct payment, or an external program, and it reuses the booking pipeline for pricing, payment, and membership usage. See [Attendance](/schedule/attendance).
A season pass is the long end of the same idea. Instead of one seat it holds one court hour every week of a season, booking every week at the moment of sale, and the member pays once. See [Season passes](/schedule/season-passes).
Areas and instructors are the bookable resources. Areas have operating hours and a price per hour; instructors have a bookable flag, hourly rate, and operating hours or a shift roster. The calendar shows one column per resource in day view, and the booking dialog asks you to pick one (area, instructor, or class) when you create a booking.
A [booking policy](/settings/booking-policies) decides the rules that apply to any given booking - when payment is required, whether check-in is enforced, refund tiers on cancellation. Policies can be org-wide or scoped to a gym, area type, class type, instructor type, or plan.
## Where to start
If you're setting up a new gym, the usual order is:
1. Create your gyms under **Settings** > **Clubs** > **Clubs**.
2. Add area types and areas under **Settings** > **Clubs** > **Areas** (see [Areas](/settings/areas)).
3. Add instructors under **Staff** > **Instructors** (see [Instructors](/staff/instructors)).
4. Configure [booking policies](/settings/booking-policies) under **Settings** > **Bookings** > **Booking and cancellation policies** for payment timing and cancellation.
5. Open **Schedule** and start adding classes and bookings.
If you're running an established gym, you'll mostly live on the [calendar](/schedule/calendar), with [Bookings](/schedule/bookings) and [Classes](/schedule/classes) as drill-down lists.
## Related
* [Members](/members/overview) - The contacts you book classes and sessions for.
* [Memberships](/sales/memberships) - Plans that cover bookings without per-visit payment.
* [Payments](/sales/payments) and [Charges](/sales/charges) - The money side of every booking.
* [Check-in models](/reference/check-in-models) - Pick the right entrance flow before configuring policies.
# Season passes
Source: https://docs.1club.ai/schedule/season-passes
Sell the same court hour every week of a season as one prepaid contract - hold the courts, price it from your rate card with a volume discount, credit makeup hours for cancelled weeks, and release the whole run in one step.
A season pass sells the same hour on the same court, every week of a season, as one prepaid contract. Staff sell it from the [schedule](/schedule/calendar), on the hour the member wants: the member picks a Tuesday at 19:00 for the winter, pays once, and every week is booked the moment the sale goes through. What you get afterwards is an ordinary [membership](/sales/memberships) row plus a booked weekly series that the calendar marks as contracted time.
## When to use it
This is the default commercial model of indoor racket sports, and it has a local name almost everywhere: contract time or permanent court time in the US, block booking in the UK, Winterabo in Germany, fast tid in Sweden, vakiovuoro in Finland. Selling it as a season pass takes the courts off the market for the whole season in one sale, prices every week from your rate card, credits missed weeks back as makeup hours, and releases every remaining week in one step if the pass is cancelled.
Typical uses:
* **A winter tennis contract** - a member holds Tuesday at 19:00 on the same court for the whole winter, pays once at the desk, and every week is booked straight away.
* **Several hours a week in one contract** - a squash regular holds a Monday and a Thursday evening, on different courts if needed, with one volume discount for the hours they hold each week.
* **Joining mid-season** - a member who signs up in December buys only the weeks that are left, at the same rates, with nothing to prorate.
* **A missed week** - the holder cancels one week in good time, and the hour is credited back to replay on another date inside the season.
## The vocabulary
| Term | What it means |
| - | - |
| **Season** | The fixed period the pass is sold for - a winter, a term, a league. Set up under [Seasons](/settings/seasons). |
| **Weekly hour** | One court, one weekday, one start time and a duration, held every week of the season. Also called a season slot. |
| **Season pass** | What the member buys: the whole run of weeks for their weekly hours, paid once. |
| **Makeup hour** | An hour credited back when a week is cancelled in good time, replayable on another date inside the same season. |
## Season pass vs membership
Both end up as a membership row, but they behave differently on every axis that matters at the desk:
| | Season pass | Membership |
| - | - | - |
| What it holds | A named court, weekday and time, every week of the season | Nothing; the member books as they go |
| Billing | Paid once, up front | Recurring, or a one-off pass |
| Renewal | Never auto-renews | Auto-renew available |
| Price | The sum of the weeks it books, with one volume discount | The plan's price |
| Allowance | The season's hours, spent up front on the weeks it books | Debited as the member books or checks in |
| Cancellation | Releases every remaining week in one atomic step | Ends the term, leaves existing bookings standing |
| Sold from | The schedule | The member profile, POS, portal, or marketplace |
## Before you can sell
1. **A season that is on sale.** Create it under **Settings** > **Billing** > **Seasons** and check its **Setup** chip reads **On sale**. See [Seasons](/settings/seasons).
2. **Hourly rates in pricing rules.** A season plan carries no price of its own, so an hour with no rate prices at zero and the sale is refused. Author one rule per time band, and a second rule per band scoped to the season's plan if you publish a separate holder rate. See [Pricing rules](/settings/pricing-rules).
3. **Permission to create memberships.** The sale option is offered only to staff who hold it.
## Selling a season pass
Open the [schedule](/schedule/calendar), click the free hour the member wants, and choose **Add Season Pass**. The option appears only when the date you clicked falls inside a season that is actually on sale at that gym.
The **Season Pass** drawer opens with that hour already filled in. It shows the venue's timezone at the top, because every time in it is read on the venue clock, not your browser's.
* **Choose a member** and **Choose a season**.
* **Weekly hours** - one row per hour held every week. Each row carries a day, a start time, a duration and a court. Use **Add another hour** for a second or third weekly hour; rows may sit on different days and different courts. A repeated row is refused, because two identical rows would book the court against itself and charge twice.
* **Starts from** - sell from a later week. The member buys the weeks that are left, at the same rates.
* **Price** - itemised down to the week so you can check it against your printed price list, with the volume discount shown as its own line.
* **Paid now** - toggle on and pick a payment method to take the money at the desk.
* **Sell season pass** completes the sale.
Each row tells you how much of the season it can actually deliver: **Free all 27 weeks**, **19 of 27 weeks free**, or how many weeks the venue is closed. An hour that is not for sale at all says so - the venue is closed then, or the court is held back from season sales.
### Clearing the weeks that are not free
A season slot is sold as a complete run, so blocked weeks have to be cleared before the hour can be sold. The drawer offers two ways out, per row:
* **Move it to** - relocate the booking standing in the way to another free court. A moved booking is marked as moved out of the way, so the next person to look at the calendar can see why it is not where it was.
* **Or move the whole hour** to a court that is free for the whole season, chosen from the list the drawer offers.
Hard stops cannot be moved, and another season's contracted week is never moved without confirmation.
### Paid now, or paid later
Either way the courts are held from the moment you sell, and the charge is recorded. The difference is only the money:
* **Paid now** takes the payment at the desk through your usual payment methods.
* **Not paid now** leaves the pass pending until the money arrives. The weekly series is the hold, so nobody else can take those hours in the meantime.
If something takes one of the hours while the sale is being written, nothing is booked at all: the drawer tells you, recalculates the price, and lets you try again. You never end up with half a contract.
## How the price is worked out
A season pass has no fixed price. It costs the sum of the prices of the weeks it will actually book, with the volume discount applied once to that sum.
1. **Every occurrence is priced** by your active pricing rules, on the venue clock, so a Tuesday 19:00 hour picks up your peak rate and a Wednesday 10:00 hour picks up your day rate.
2. **The weekly hours are totalled** across the whole remaining season.
3. **One volume discount** is applied to the total, from the [season's ladder](/settings/seasons), based on how many hours a week the member is holding.
Nothing is prorated and no week count is stored, which is why a mid-season sale needs no special rule: it simply buys fewer weeks at the same rates. It is also why holiday closures, refunds and makeup credits stay ordinary arithmetic.
## What the holder gets
* **Every remaining week is booked** against their name on the courts they chose.
* **The calendar marks those hours** with a small season icon labelled with the season's name, so a contracted week never reads as a casual booking that has not been paid for.
* **The member rate applies to their other bookings.** A rule you scoped to the season's plan gives the holder their rate when they book an extra hour, on the calendar and at checkout alike. It applies from the moment the pass is sold, not from the season's first day, so a member who buys the winter in September is already quoted their rate for a December hour. Hours before the season starts are still priced at the published rate, and a pass waiting on payment grants no rate until the money is in.
* **Coverage is fenced to what was sold.** The pass covers court time, inside the season's dates, inside the operating hours the plan carried at the moment of sale. Editing the plan in December cannot change what a pass sold in October admits, and a credit cannot be spent after the season ends.
## Missed weeks and makeup hours
When a holder cannot make a week, cancel that week's booking like any other. If the applicable [booking policy](/settings/booking-policies) grants anything back for a cancellation that early, the hour is credited to the pass as a makeup hour.
To replay it, book the member another hour on a court. The pass covers the booking and spends the credit, and the calendar marks that booking as the season's too, so a replayed hour does not look like unpaid court time.
Two things to know:
* **A pass sitting at zero remaining is normal, not exhausted.** The sale spends the season's hours up front on the weeks it books, so zero is where the pass rests. It returns to zero after each makeup is replayed.
* **Credits expire with the season** and can only be replayed inside the hours the pass was sold for. A credit earned on an off-peak hour cannot be replayed at peak.
## Cancelling a season pass
Cancel from the [Memberships](/sales/memberships) list or the membership detail page. A season pass opens its own cancel dialog rather than the generic one, because ending it has to release the courts and close the pass together.
The dialog tells you what the weeks not yet played are worth, then asks two things:
* **Refund** - what the gym is giving back. It starts at nothing on purpose: a refund is a decision, not a calculation, and nothing in a typical gym contract makes a season pass refundable. Entering more than the unplayed weeks are worth is allowed, with a note pointing it out.
* **Reason** - recorded with the cancellation.
Confirming does all of it in one atomic step: every remaining hour is released, the weekly series stops, and the pass is closed. A season pass is never released court by court.
The refund goes back the way the money came in - a card payment reverses through the gateway, wallet credit returns to the wallet, and anything else is returned as cash at the till or as wallet credit. If the cancellation succeeds but the refund does not, the dialog says so plainly: the pass is cancelled, and you give the money back from the charge for the sale.
## Renewal
A season pass never renews itself. The next season is a fresh sale at the new season's prices, which is how seasonal venues price and how holders expect to be treated.
The **Existing subscribers claim by** date on the season is a note for your own outreach to last season's holders. Nothing is reserved for them automatically and no reminder is sent.
## Current limits
* **Staff sell, members do not.** There is no self-serve season purchase in the member portal or app yet. Members see the resulting pass and its booked weeks.
* **No automated renewal or renewal priority.** Both are manual, helped by the claim-by date you record on the season.
* **Moving a sold weekly hour to another court is API-only.** The admin has no screen for it yet. It moves every remaining week or none of them, and never reprices the member.
* **A freshly sold pass reads as fully committed.** Allowance figures are generic across the platform, and the sale spends the season's hours up front on the weeks it books. So the admin Usage column shows `27 / 27 hours`, and member-facing surfaces (the portal, the app, the booking picker, and the check-in confirmation) show `0 of 27 hours left`. Both are a healthy pass, not an exhausted one. The counter rises only when a cancelled week credits a makeup hour back.
* **Court time only.** A weekly hour is a court, a day and a time. A season slot with a coach attached is not supported.
* **One gym per season.** A pass cannot span two venues.
## Troubleshooting
**Issue**: **Add Season Pass** is missing from the slot menu.
**Solution**: Three things gate it: the clicked date must fall inside one of that gym's seasons, that season must be on sale (check the **Setup** chip under [Seasons](/settings/seasons)), and you must hold the permission to create memberships.
**Issue**: The sale is refused and tells me to configure pricing first.
**Solution**: The quote came to zero, which means no pricing rule matched the hours being sold. Author an hourly rate per time band in [Pricing rules](/settings/pricing-rules) and try again. The quote itself may display zero; only the sale refuses.
**Issue**: A row says some weeks are not free and I cannot sell.
**Solution**: A season slot is sold as a complete run. Use **Move it to** on the row to relocate what is in the way, or move the whole hour to a court the drawer reports as free for the whole season. Weeks when the venue is closed are excluded rather than blocking.
**Issue**: The total does not match my price list.
**Solution**: The price is the sum of the weeks, not a rate times a round number of weeks. Open the itemised **Price** panel and check week by week: a season that is not a whole number of weeks gives some weekdays one occurrence more, and the volume discount applies once to the total, not per week.
**Issue**: The member's pass shows `27 / 27 hours` used the day they paid.
**Solution**: That is correct. The allowance is the hours the pass sold, and the sale spends them on the weeks it booked. The counter rises only when a cancelled week credits a makeup hour back.
**Issue**: A holder's extra booking was charged the published rate, not their rate.
**Solution**: The member rate comes from a pricing rule scoped to the season's plan. Check that such a rule exists and is active, that the booked hour falls inside the season, and that the pass itself still grants a rate: a cancelled or expired pass grants none, and one still waiting on payment grants none until the money is in. See [Which memberships grant a member rate](/settings/pricing-rules#which-memberships-grant-a-member-rate).
**Issue**: I cancelled a week but no makeup hour appeared.
**Solution**: The credit follows the [booking policy](/settings/booking-policies) that applies to the booking. If the policy grants nothing back at that notice, the hour is forfeited rather than credited. Check the policy's cancellation tiers.
## Related
* [Seasons](/settings/seasons) - Define the period and the volume discount ladder.
* [Pricing rules](/settings/pricing-rules) - Hourly rates per time band, and the holder rate.
* [Schedule](/schedule/calendar) - Where a season pass is sold and where its weeks appear.
* [Memberships](/sales/memberships) - The sold pass, its allowance, and cancellation.
* [Booking policies](/settings/booking-policies) - What a cancelled week gives back.
* [Repeating schedules](/reference/repeating-schedules) - How recurring bookings work in general.
# Waitlists
Source: https://docs.1club.ai/schedule/waitlists
Let members join a waitlist when a class is full, then automatically notify and book the next person in line when a spot opens up.
A waitlist is the ordered queue of members who want a spot in a class that is already full. Members join it themselves, staff can add someone on the class page, and owners and admins set the claim window on the booking policy. When a booked member cancels, 1club works down the queue for you: it either books the next person straight away or sends them a time-limited offer to claim the spot.
## When to use it
A full class doesn't have to mean a lost booking or the front desk ringing round for a replacement. The queue fills a freed seat on its own, and each seat is only ever promised to one person at a time. This page walks through the full workflow, from joining a waitlist to the moment an offer turns into a confirmed booking.
Typical uses:
* **An early cancellation** - a member cancels their seat in a pass-covered yoga class a day ahead, inside the full-refund window, and the first person in line is booked straight in without doing anything.
* **A last-minute drop-out** - someone cancels two hours before a padel clinic, and the first person in line gets a spot-available notification with a claim window (15 minutes by default) before the offer rolls to the next person.
* **A paid drop-in class** - when the policy takes payment at booking, the next person gets a claim window instead of an automatic booking, so they pay when they take the seat.
* **A caller without an account** - the front desk adds a phone caller to a full class's waitlist, and when a spot opens they set a password from the claim link and book.
## When a waitlist applies
Every class supports a waitlist by default, so nothing needs to be switched on for the queue to work. A waitlist only comes into play when both of these are true:
* The class has a **Max capacity** set. Classes with unlimited capacity are never full, so there is nothing to wait for.
* The class is full - the number of booked seats plus anonymous check-ins has reached the max capacity.
While seats are still open, members book directly and never touch the waitlist. If a member tries to join the waitlist on a class that still has space, 1club blocks it and points them back to the normal booking flow.
A member can only be in one place at a time for a given class occurrence. If someone already has a booking or a check-in for that class on that date, they cannot also join the waitlist for it, and they cannot join the same waitlist twice.
## How members join a full class
When a class is full, the booking control changes from **Book** to **Join waiting list** everywhere the class appears - the class detail page, the [schedule](/schedule/calendar) grid, and class cards.
1. The member taps **Join waiting list** and confirms in the dialog.
2. If they are not signed in, they are asked to log in first, then the join continues automatically.
3. 1club creates a waitlist entry with the status **pending** and emails the member to confirm they are on the list, including their current position in the queue.
Position is calculated from the order members joined - the earliest to join is first in line. Members can see their entries, their position, and any live offers under **Account** > **Waitlist** in the member portal.
Staff can also add someone to a waitlist on the member's behalf. On the class detail page, the **Add to waitlist** button in the **Attendance** card is enabled only when the class is full. See [Classes](/schedule/classes) for the attendance grid, which lists booked seats, anonymous check-ins, and waitlist entries side by side.
## What happens when a spot opens
The queue is processed automatically whenever a booked member cancels and frees a seat. 1club picks the person at the front of the queue and takes one of two paths, depending on how far away the class is.
Cancelling a booking frees a seat and triggers waitlist processing for that class occurrence. Only occurrences that have not yet started are processed - a cancellation on a class that has already begun does not generate offers.
1club looks at the first person in line:
* **Auto-book** - If the class is at least 12 hours away and the cancellation still falls inside the full-refund window of the matching [booking policy](/settings/booking-policies), 1club books the next member straight into the seat. No action is needed from them.
* **Offer to claim** - Otherwise, 1club marks the entry **notified**, generates a one-time claim link, and sends a "A spot is now available" notification with a deadline to claim.
The member opens the claim link and confirms. 1club turns their waitlist entry into a confirmed booking and clears the offer.
If the deadline passes with no claim, the entry expires and the same offer is passed to the next person in line.
Only one person is ever offered a spot at a time, so a single freed seat is never promised to two people at once.
### The claim window
The claim window is how long a notified member has to confirm before the offer moves on. It defaults to 15 minutes and is set per policy by the **Waitlist claim window (minutes)** field - see [Booking policies](/settings/booking-policies). The spot-available notification tells the member the exact deadline.
Offers are only sent when the freed seat is realistically claimable, so notifications cluster in the hours before a class rather than days ahead.
### How members claim
Taking an offered seat is booking the class. Whichever door the member comes through, they land in the ordinary booking flow, so whatever the club's [booking policy](/settings/booking-policies) asks for at booking happens there - a card payment when payment is due at booking, a covering pass, or pay on arrival. Creating that booking converts the waitlist entry.
* **The claim link** in the notification. It opens a page showing the class, date and gym. If the member has no password yet - for example, a contact a staff member added to the list - they set one there, and 1club creates their account and signs them in. Continuing takes them to the class, where **Book** completes the seat.
* **Account** > **Waitlist** in the portal. A notified entry shows the deadline and a **Book now** button that opens the class.
* **The class in the mobile app.** The spot-available push notification opens the class, where the member sees the deadline with **Accept spot** and **Decline**. Accept opens the booking screen. Tapping a notification for a class at another of their clubs switches the app to that club first.
* **Booking the class outright.** A claim window is a deadline, not a hold, so the seat stays open to everyone until it is taken. A notified member who books the class from anywhere else while their window is open takes the seat the same way.
The two claim-window notices - a spot is available, and the window has passed - are always sent, whatever the member's booking notification setting says. Turning booking notifications off silences confirmations and changes, not an offer that expires on its own.
Booking runs the usual checks. If the window has passed, the class has already started, or the seat was filled in the meantime, the booking is refused with a clear message and the class shows what is true now.
The claim window binds the member claiming the seat. Staff converting an entry to a booking from the class's attendance grid can do so after its window has passed.
A member can also **decline** or **leave** a waitlist. Declining a live offer removes them from the queue and immediately passes the spot to the next person in line. Leaving a pending entry simply takes them off the list.
**Payment due at booking.** When the policy requires payment at booking and the class has a price, 1club never books the seat for the member automatically: the auto-book path is skipped and the member gets a claim window instead, so they pay when they take the seat. Free classes and classes covered by a pass still auto-book.
## Entry statuses
Every waitlist entry moves through these statuses:
| Status | Meaning |
| - | - |
| **Pending** | The member is in the queue, waiting for a spot. This is the starting status for everyone who joins. |
| **Notified** | A spot opened and this member has an active offer. The claim window is counting down. |
| **Converted** | The member claimed the spot (or was auto-booked) and now has a confirmed booking. |
| **Expired** | An offer was sent but the claim window passed without a claim. The spot was offered to the next person. |
| **Cancelled** | The member left the waitlist, or staff removed the entry, before it converted. |
A converted entry is linked to the booking it created, so the seat and the queue history stay connected.
## Behind the scenes: expiring offers
A background job runs every few minutes to keep the queue moving without anyone having to watch it. For each offer whose claim window has passed, the job:
1. Checks whether the member already holds a seat on that class. If they booked it while the window was open, the entry is marked **converted** and linked to that booking, and no expiry notice is sent.
2. Otherwise marks the entry **expired**.
3. Notifies the member that their opportunity has expired and the spot went to the next person.
4. Offers the freed spot to the next pending member in line, starting a fresh claim window.
This is what lets an unclaimed offer cascade down the queue automatically until someone claims it or the class fills.
## Where staff manage waitlists
Waitlists live on the class itself. Open a class from **Schedule** > **Classes** and use the **Attendance** card on the detail page to:
* See who is on the waitlist alongside the booked seats and check-ins.
* Add a member to the waitlist with **Add to waitlist** (enabled only when the class is full).
* Convert a waitlist entry into a booking manually, when you want to seat someone directly rather than wait for the automatic flow.
For the full class detail layout and capacity tiles, see [Classes](/schedule/classes).
## Related
* [Classes](/schedule/classes) - Set capacity, run the attendance grid, and convert waitlist entries.
* [Booking policies](/settings/booking-policies) - Set the waitlist claim window and the refund tiers that drive auto-booking.
* [Bookings](/schedule/bookings) - Where a claimed or auto-booked seat shows up as a confirmed booking.
* [Events](/events/events) - For one-off ticketed happenings, use an event instead of a class.
# Access control
Source: https://docs.1club.ai/settings/access-control
How members identify themselves at your door (QR, access card, PIN) per club, and how the check-in modes that decide who staff can check in are configured through booking policies.
Access control settings cover two things: how members identify themselves at the door (QR code, access card, or PIN), and how strict your check-in flow is - whether staff can check in walk-ins or anonymous visitors, and whether payment is required. You configure them, the front desk checks people in within those rules, and members use the identification methods you allow.
## When to use it
Set these up to match how people actually arrive at your gym. A strict setup keeps entry tied to bookings and paying members. A flexible one lets the front desk handle walk-ins and guests without creating an account for everyone, while still collecting payment.
Typical uses:
* **A studio full of trial visitors** - walk-ins and anonymous check-ins on, with payment enforced at check-in so drop-ins without an account still pay, and returning members scanning their own QR code.
* **A booking-only class gym** - walk-ins and anonymous check-ins off, check-in required, and payment taken at booking, so every visit starts from a booking.
* **Different rules per location** - a flagship gym with turnstiles on access cards and a booking-based policy, next to a community-run satellite that allows walk-ins and uses QR only.
See [Worked examples](#worked-examples) for the exact settings behind each of these.
## How it works
The two halves live in different places. Identification methods and enrollment are set per gym under **Settings** > **Access Control** > **Settings**. The strictness of check-in is governed by your **booking policy**, not by a single toggle on that page. Physical door hardware (access grants and door sync) is part of the native access control feature, documented separately in [Access control settings](/settings/access-control-settings).
If you're choosing how members get into your club, start with [Check-in models](/reference/check-in-models).
There is no longer a single "Check-in mode" (Strict/Flexible) switch on the access control page. That legacy toggle was replaced by the booking-policy settings described below - **Allow walk-ins** and **Allow anonymous check-ins**.
## Check-in modes (booking policy)
How strict your door is comes from your [booking policy](/settings/booking-policies) (**Settings** > **Bookings** > **Booking and cancellation policies**). The relevant toggles:
* **Allow walk-ins** - staff can check in an existing contact who has no booking. Off by default.
* **Allow anonymous check-ins** - staff can check in a visitor with no contact at all. Independent of walk-in check-ins. Off by default.
* **Allow anonymous bookings** - a booking can be created without selecting a contact. Off by default.
* **Check-in required** - a booking is marked no-show if the member isn't checked in by end of day.
* **Enforce payment** - **None**, **At booking**, or **At check-in**. This decides whether payment is collected before a check-in is recorded.
A "strict" club leaves the anonymous and walk-in toggles off and requires bookings. A "flexible" club turns on walk-ins and, if it accepts true guests, anonymous check-ins.
### How anonymous check-ins interact with Enforce payment
**Allow anonymous check-ins** and **Enforce payment** work together:
* **Anonymous off** - staff must pick a contact (with or without a booking, depending on the walk-in setting).
* **Anonymous on + Enforce payment: None** - anyone can be checked in, including a visitor with no account. The check-in is recorded with no contact link.
* **Anonymous on + Enforce payment: At check-in** - anonymous check-ins are still allowed, but only when the front desk also collects payment via an entrance method (external program or direct payment). A "no contact and no payment" check-in is rejected.
Anonymous check-ins support only the **direct payment** and **external program** entrance methods. **Membership** entries are contact-scoped, so they can't back an anonymous check-in. Entrance methods are covered in [Check-in models](/reference/check-in-models).
## Anonymous check-ins in practice
When anonymous check-ins are allowed, the front desk can record a check-in without picking a contact:
1. From the check-in flow, choose to record an anonymous check-in (a club must be selected).
2. Optionally note what they're entering for (class, area, instructor).
3. If your booking policy enforces payment at check-in, collect a direct payment or apply an external program now.
4. Save.
What gets recorded:
* A check-in row with no member/contact link.
* A reference to the class, area, or instructor (if selected).
* A charge and its payment (if you collected one).
What does **not** happen:
* No membership usage is decremented (there's no member to attribute it to).
* The check-in won't appear in any single member's history.
Anonymous check-ins are best for guests, trial visitors, and one-off drop-ins. For repeat members, create a contact even if it's lightweight, so you can track usage and follow up.
## Member identification methods (per club)
Open **Settings** > **Access Control** > **Settings**. Each club appears as its own block, and changes across clubs are saved together with the floating save alert at the bottom.
Turn on **Access Control** for a club to enable **credential enrollment** - letting members register an access card, NFC device, or PIN themselves for physical entry, instead of staff registering one for them. When it's on, choose the **Enabled Identification Methods**:
* **QR Code** - members enroll a QR code and scan it at the door.
* **Access Card** - members enroll a physical access card (NFC/RFID).
* **PIN Code** - members enroll a numeric PIN to type at the door.
All three are enabled by default; you can turn off any you don't accept. When a method is off, members can't enroll it themselves, though staff can still register one manually.
At the door, 1Club identifies the member in this order: **PIN**, then **access card**, then **QR code**. A PIN is matched on the full 6-digit code, and a kiosk can accept just the last 4 digits as a shortcut.
The **Access grants** tab next to **Settings** lists the access grants that drive physical locks. That side of the native access control feature is documented separately; see [Access control settings](/settings/access-control-settings).
The **Check-in modes** setting on the [Check-in App](/settings/kiosk#check-in-app) (**Settings** > **Member Apps** > **Check-in App**) is a different thing: it chooses whether members check in to **Classes**, **Open Gym**, or both on the self check-in tablet. Who staff may check in is still decided by the booking policy above.
## Worked examples
### Kiosk-style walk-in studio
A small studio with lots of trial visitors that wants minimal friction at the door.
* **Booking policy** - **Allow walk-ins** on, **Allow anonymous check-ins** on, **Enforce payment** set to **At check-in** so the system still collects from anonymous visitors.
* **Identification** - **Access Control** on with **QR Code** enabled, so returning members self-serve.
### Strict members-only class gym
A class-based gym where every member has an account and bookings are mandatory.
* **Booking policy** - **Allow walk-ins** and **Allow anonymous check-ins** off, **Check-in required** on, **Enforce payment** set to **At booking**.
* **Identification** - **Access Control** on with all three methods enabled.
### Multi-club organization
A flagship club can run a strict booking-based policy while a community-run satellite allows walk-ins. Scope different booking policies per club (see [Booking policies](/settings/booking-policies)), and set identification methods independently for each club on the access control page.
## Tips & best practices
* **Match your booking policy to your front-desk culture.** If staff routinely handle walk-ins, turn on **Allow walk-ins**. If your model depends on bookings, leave it off to prevent accidental free entries.
* **Only allow anonymous check-ins if you truly accept accountless visitors.** They aren't attributable to any member, so revenue and attendance reporting won't tie back to a person.
* **Collect payment on anonymous entries.** Pair **Allow anonymous check-ins** with **Enforce payment: At check-in** so drop-ins still pay.
* **Set identification methods per club.** A flagship with turnstiles might use access cards, while a studio uses QR only.
## Troubleshooting
**Issue**: I turned on walk-ins but staff still can't check someone in without payment.
**Cause / fix**: The booking policy that applies at this club has **Enforce payment** set to **At check-in**, so an entrance method (external program or direct payment) is required. Collect payment, or set Enforce payment to **None**.
**Issue**: Members can't enroll their QR code themselves.
**Cause / fix**: Confirm **Access Control** is on for that club and **QR Code** is in the enabled identification methods.
**Issue**: A check-in shows up with no member attached.
**Cause / fix**: That's an anonymous check-in. They only appear when **Allow anonymous check-ins** is on for the applicable booking policy. Turn it off if you didn't intend to accept them.
**Issue**: I can't find the old "Check-in mode" toggle.
**Cause / fix**: It was removed. Use the booking-policy toggles **Allow walk-ins** and **Allow anonymous check-ins** instead.
## Related
* [Check-in models](/reference/check-in-models) - pick how members get into your club, and how entrance methods work.
* [Booking policies](/settings/booking-policies) - where check-in modes, walk-ins, anonymous check-ins, and payment enforcement are configured.
* [External programs](/settings/external-programs) - partner offerings and corporate wellness benefits used as an entrance method.
* [Access control settings](/settings/access-control-settings) - the native access control feature (access grants, WebLock door hardware).
* [Attendance](/schedule/attendance) - review and manage check-in records.
# Physical access control
Source: https://docs.1club.ai/settings/access-control-settings
How 1Club's native access control turns memberships and bookings into physical door access - doors and door groups, credentials, access grants, auto-grant, access events, and edge gateway sync.
Physical access control is the native 1Club feature that opens real doors. It links a member's [credential](/members/member-cards) (an access card, QR code, or PIN) to a time-bound **access grant** derived from their membership or booking, syncs those grants to door hardware through an **edge gateway**, and records every tap as an **access event**. You turn it on per gym, staff enroll members' credentials, and members tap, scan, or type their credential at the door. This is the hardware side of the platform: locks, readers, and controllers.
This page is different from [Access control](/settings/access-control), which covers **check-in modes** (walk-ins, anonymous check-ins, payment enforcement) and how members **identify themselves** at the front desk. That page is about the front-desk check-in flow. This page is about physical door hardware and the grants that drive it. The two share one thing: the identification methods (QR, access card, PIN) a member enrolls are the same credentials that back a door grant.
## When to use it
Use physical access control when your doors should open based on what a member has paid for or booked, without staff updating the door system by hand. Grants are created and revoked as memberships and bookings change, and every attempt at a door is logged.
Typical uses:
* **Door access that follows the membership** - a 24/7 strength gym where a member's fob opens the main door for as long as their membership is active, and stops working when it expires or is cancelled.
* **Entry only for a booked session** - a reformer Pilates studio where a confirmed booking lets the member's QR code in from class start to class end, and a cancellation revokes it.
* **An audit trail at the door** - a climbing gym that reviews flagged access events to catch tailgating, cloned cards, or misconfigured hardware.
See [Worked examples](#worked-examples) for how to set up each of these.
## How it works
The physical access control system is made of five moving parts:
* **Doors and door groups** - the physical endpoints (a turnstile, a studio door) and the logical groups you grant access to.
* **Credentials** - the QR code, access card (RFID/NFC), or PIN a member presents at a reader. Managed on [Access cards](/members/member-cards).
* **Access grants** - time-bound permissions that authorize one credential to enter, derived automatically from an active membership or a confirmed booking.
* **Access events** - the audit trail of every attempt at a door, reconciled against grants to flag discrepancies.
* **Edge gateway** - the on-site bridge that pulls grants down to your controllers and pushes events back up.
Grants are the single source of truth. 1Club's own edge gateway reads them, and so do the door access integrations: [WebLock (Valnes)](/settings/integrations/weblock), [Impro](/settings/integrations/access-bridge), and [Salto KS](/settings/integrations/salto).
## Turning on access control
Native access control is enabled per club. Until it is on for a club, no grants are created for that club's memberships or bookings, and no credentials are auto-provisioned.
1. Open the **Check-in & access control** settings page (see [Access control](/settings/access-control)).
2. Turn on **Access control** for the club. This enables credential enrollment and switches on grant creation for that club.
3. Choose the **Enabled identification methods** (QR Code, Access Card, PIN Code) you accept. These are the credential types members can enroll and that the system can auto-provision.
Access control is off by default for every club. Enabling it is what makes membership and booking activity flow through to door grants.
## Doors and door groups
A **door** is a single physical access endpoint controlled by a reader and a controller. Each door belongs to a club and carries a name, an optional description and location, and the controller details (`controllerType`, `controllerId`) that tie it to hardware.
A **door group** is a logical grouping of doors for one club, so you can grant access to a set of doors at once (for example, "Main entrance + locker rooms") instead of door by door. An access grant that is scoped to a club can target a door group; the grant then applies to every door in that group.
There is no self-serve admin screen to create or edit doors and door groups. In the current product they are provisioned as part of hardware setup (and are copied when an organization is cloned from a template), then referenced by grants and events. If you need doors or groups configured, that happens during onboarding of your access hardware. Flag this to your implementation contact rather than looking for an admin page.
## Credentials
A credential is the token a member presents at a door. Three types exist:
* **QR Code** (`qr`) - a code the member scans from their phone.
* **Access Card** (`card`) - a physical RFID or NFC card or fob.
* **PIN Code** (`pin`) - a numeric PIN typed at a keypad.
You manage credentials on **Members** > **Access cards** (`/members/access-cards`), where you can enroll, assign, revoke, and delete them. See [Access cards](/members/member-cards) for the full management flow. A few facts that matter for door access:
* **Identifiers are protected.** The raw card number or PIN is encrypted at rest and stored alongside a one-way hash. Readers and the gateway match on the hash, never the plaintext.
* **A credential is assigned to a contact.** A credential assignment links the credential to a contact (and optionally to a specific membership or booking). A contact's **primary** credential is the one auto-grant uses.
* **Credential lifecycle.** A credential is `active`, `suspended`, `revoked`, or `expired`. Only an `active` credential can back a working grant. Revoking a credential keeps it as an audit record; deleting is only allowed once it has no active assignments.
When access control is on for a club but a member has no credential yet, the system auto-provisions a **primary** credential the first time it needs one for a grant. It prefers a PIN (a random 6-digit code) when PIN is an enabled method, otherwise it generates a QR code from the member's check-in code. If no identification method is enabled, no credential is created.
## Access grants
An access grant authorizes one credential to enter during a time window. Each grant records:
* The **credential** it applies to.
* A **membership** or a **booking** as its source (exactly one is required).
* An optional **club** and **door group** scope (a door group is only honored when a club is set).
* **Valid from** and **valid until** timestamps, plus a timezone.
* Optional **access windows** - recurring time-of-day rules (for example, `07:00`-`22:00`, days Monday-Friday) that further restrict when the grant works inside its overall validity.
* A **status**: `active`, `expired`, `revoked`, or `suspended`.
You review grants under **Settings** > **Access Control** > **Access grants** (route `/settings/access/grants`). The list is read-only and shows each grant's **Contact**, **Source** (Booking or Membership), **Valid from**, **Valid until**, and **Status**, with search by contact name and sorting by validity dates or status.
Grants are created and revoked automatically (see below). Creating or revoking a grant by hand is an operations-permission action available through the API, not a form on the grants list.
## How grants derive from memberships and bookings (auto-grant)
You rarely create grants yourself. The auto-grant service watches membership and booking lifecycle events and keeps grants in step. It only acts when access control is enabled for the relevant club.
### From a membership
When a membership becomes **active**, 1Club creates a grant that is valid for the membership period:
* **Valid from** the membership start date, **valid until** its end date (or one year out if the membership has no end date).
* Scoped to the membership's club.
* Backed by the member's primary credential, auto-provisioning one if needed.
When the membership **expires or is cancelled**, its active grants are revoked.
### From a booking
When a booking is **confirmed**, 1Club creates a grant valid only for that session:
* **Valid from** the booking start time, **valid until** its end time.
* Scoped to the booking's club.
* Backed by the booked member's primary credential (auto-provisioned if missing).
If an active grant already exists for the booking, it is reused rather than duplicated. When the booking is **cancelled**, its active grants are revoked. Bookings that are not confirmed do not get a grant.
Auto-grant needs a credentialed member. If a booking has no linked contact, or the club has access control disabled, or no identification method is enabled to provision from, no grant is created. The most common reason a member cannot get through a door is simply that they have no enrolled credential - fix it on [Access cards](/members/member-cards).
## Access events
Every tap, scan, or PIN entry at a door produces an access event. Events flow in from the edge gateway and are stored with:
* The **credential** and **door** involved.
* An **event type** (`access_attempt`, `access_granted`, `access_denied`) and a **result** (`granted` or `denied`).
* A **timestamp**, timezone, gateway, and controller reference.
* A **denial reason** when access was refused.
Events can be queried through the API by credential, door, date range, or result. Each event also carries a hash that chains to the previous event, so the log is tamper-evident.
### Reconciliation and discrepancies
After ingestion, events are reconciled against grants:
* A granted event that matches a valid grant is marked **reconciled**.
* A denied event is reconciled as expected behavior.
* An event is flagged as a **discrepancy** when the credential is unknown, when access was granted with no matching grant, when the grant was expired or not yet valid at the event time, or when entry happened at the wrong door group.
If a reader reports a credential 1Club has never seen, the event is still recorded against a placeholder (revoked) credential and flagged as a discrepancy, so nothing is lost from the audit trail. You can pull the list of discrepancies for a date range to investigate tailgating, cloned cards, or misconfigured hardware.
Access events and reconciliation are available through the access-control API. There is no dedicated admin screen for browsing the raw event log in the current product; front-desk attendance is reviewed on [Attendance](/schedule/attendance) instead. Treat the event log as an audit and integration surface.
## Edge gateway sync
An edge gateway is a small on-site service (shipped as a Docker container) that bridges your door controllers to the cloud. Each gateway belongs to one club and is provisioned with a long-lived gateway token.
The sync loop is pull-based and offline-tolerant:
1. **Pull grants.** The gateway periodically asks the cloud for grants for its club (plus org-wide grants), passing a `since` timestamp so it only fetches what changed. It receives the current grants, the IDs of any newly revoked grants, and a sync timestamp.
2. **Provision controllers.** The gateway pushes those grants to each connected controller and removes the revoked ones, so the door itself can decide access even if the internet drops.
3. **Report health.** The gateway sends a heartbeat with its version and status; the cloud records `lastSeenAt` and the sync status.
4. **Push events.** Access events are queued locally and flushed to the cloud in batches, and re-queued if the upload fails.
Grants are pulled on an interval (30 seconds by default) and queued events are flushed frequently (every few seconds). A gateway's token can be rotated, which immediately invalidates the previous token. A gateway is `active`, `suspended`, or `revoked`.
Gateway provisioning, token rotation, and event ingestion are access-control API operations (they require an operations-manage permission or the gateway's own token). There is no admin form to provision a gateway in the current product - it is set up during hardware onboarding.
### Native gateway vs. door access integrations
There are two ways door hardware consumes grants:
* **Native edge gateway** - 1Club's own gateway talking to generic controllers, as described above.
* **Door access integrations** - [WebLock (Valnes)](/settings/integrations/weblock), [Impro](/settings/integrations/access-bridge), and [Salto KS](/settings/integrations/salto). Each is set up per gym on its own integration page, where you link the gym's areas to that system's doors.
The door access integrations work from **area access**: a grant for one member, one area, and one time window. It comes from a booking or a check-in in that area, or from staff adding access by hand, and the integration widens it by the gym's access buffer. A membership alone does not open a door through them. Each integration keeps its own system in step with that access, and withdraws it when the access ends. Only one door access integration can be on for a gym at a time.
## Worked examples
### 24/7 gym door access by membership
A strength-and-conditioning gym wants members to badge in at the main door any time their membership is active.
* Turn on **Access control** for the club and enable **Access Card** (and optionally **PIN**) as identification methods.
* Enroll each member's fob on [Access cards](/members/member-cards) and mark it primary.
* When you activate a member's plan, an access grant is created for the whole membership period, scoped to the club. When the plan lapses, the grant is revoked automatically.
* Point the main-entrance door (or a "Main entrance" door group) at the club so the grant opens it.
### Class door that opens only for the session
A reformer Pilates studio wants the studio door to open only for members with a booking, and only during their class.
* Enable access control for the club and enable **QR Code** so members scan in from the app.
* When a member's booking is confirmed, a grant is created that is valid from the class start to the class end.
* The grant is scoped to the studio door group, so the front door stays members-only while the studio unlocks per session.
* If the member cancels, the grant is revoked and the door will not open for them.
### Climbing gym with zoned access
A climbing gym has a public entrance, a bouldering area, and a lead-climbing zone that requires a certification add-on.
* Model each zone as a door group and enable access control for the club.
* Sell the lead-climbing entitlement as a separate plan or add-on membership so its grant can be scoped to the lead-climbing door group, while the base membership grant covers the entrance and bouldering groups.
* Use **access windows** on the base grant (for example staffed hours only) if you do not want unsupervised entry, and leave a 24/7 plan without windows.
* Review discrepancies periodically to catch anyone tailgating into the lead zone without a valid grant.
## Tips & best practices
* **Enable access control before you expect grants.** Grants and auto-provisioned credentials only start once the club toggle is on. Flipping it on does not backfill grants for memberships that were already active - re-save or renew if you need them created.
* **Give every member a primary credential.** Auto-grant follows the primary credential. A member with several cards but no primary can end up without a grant on the card they actually tap.
* **Prefer door groups over single doors.** Grouping doors keeps grants simple and lets you re-wire which physical doors belong to a zone without touching every grant.
* **Use access windows for time-of-day rules, validity for date rules.** Validity (`valid from`/`valid until`) tracks the membership or booking period; access windows restrict the hours within it.
* **Watch the gateway heartbeat.** A gateway that stops sending heartbeats has likely lost connectivity. Because sync is offline-tolerant, doors keep working on the last synced grants, but new grants and revocations will not land until it reconnects.
* **Revoke, do not delete, a lost card.** Revoking keeps the audit trail intact and drops the grant; deleting is only for credentials that were never assigned.
## Troubleshooting
**Issue**: A member with an active membership cannot open the door.
**Cause / fix**: Either access control is off for that club, or the member has no active primary credential. Confirm the club toggle on [Access control](/settings/access-control) and check the member's credential on [Access cards](/members/member-cards). If the credential is `suspended`, `revoked`, or `expired`, no working grant exists.
**Issue**: A booking was confirmed but no grant appeared.
**Cause / fix**: Grants are only created for confirmed bookings at clubs with access control enabled, and only when the booked member has (or can be auto-provisioned) a credential. A booking with no linked contact, or a club with access control off, will not produce a grant.
**Issue**: A grant exists but the door still will not open.
**Cause / fix**: Check the grant's validity window and any access windows - the current time must fall inside both. Also confirm the door belongs to the door group the grant targets, and that the edge gateway has synced since the grant was created.
**Issue**: The access event log shows an "access granted but no matching grant" discrepancy.
**Cause / fix**: The reader let someone in without a corresponding grant. This usually means a manually configured door override, a credential enrolled directly on the hardware, or a grant that had already expired. Review the discrepancy and revoke or re-provision as needed.
**Issue**: A tapped card shows up as an unknown credential.
**Cause / fix**: The card was never enrolled in 1Club, so the event was recorded against a placeholder and flagged. Enroll the card on [Access cards](/members/member-cards) and assign it to the contact, or treat it as an unauthorized card.
**Issue**: New grants are not reaching the door.
**Cause / fix**: The edge gateway may be offline or its token may have been rotated. Check that it is still sending heartbeats and running the current token. Doors keep working on the last synced grants until it reconnects.
## Related
* [Access control](/settings/access-control) - check-in modes and how members identify themselves at the front desk (the non-hardware side).
* [Access cards](/members/member-cards) - enroll, assign, revoke, and delete the credentials that back door grants.
* [WebLock](/settings/integrations/weblock), [Impro](/settings/integrations/access-bridge), and [Salto KS](/settings/integrations/salto) - the door access integrations that read area access grants.
* [Memberships](/sales/memberships) - the memberships that generate long-running door grants.
* [Bookings](/schedule/bookings) - the bookings that generate per-session door grants.
* [Check-in models](/reference/check-in-models) - how members get into your club and how entrance methods work.
* [Attendance](/schedule/attendance) - review front-desk check-in records.
# Users
Source: https://docs.1club.ai/settings/admin-users
Invite admin staff, assign one of five roles, reset passwords, and manage invitations from Settings > Organization > Users.
Users are the people who can sign into your 1Club admin, and every organization user holds exactly one of five roles that decides what they can do once they are in. Owners, admins, and managers invite and manage them. A role is a default bundle of permissions, not a hard-coded wall - what an operation allows is decided by the permission it requires, and an admin can fine-tune each role's permissions in the permission editor. See [Roles and permissions](/reference/roles-permissions) for the full model.
Staff can sign into the admin dashboard with **Google**, **Apple**, **Facebook**, or an email and password. See [Signing in](/reference/sign-in) for all supported methods and how to add a password to an account that was created with a social provider.
## When to use it
Give each person on your team their own login with the narrowest role that fits their job. A receptionist can run the desk without reaching settings, a coach sees only their own classes, and you can take access away when someone leaves.
Typical uses:
* **Onboarding a receptionist** - a climbing gym invites new front-of-house staff as **Front desk**, so they can take bookings, check climbers in, and run point of sale, but not open organization settings or analytics.
* **Bringing in a guest coach** - a muay-thai gym adds a visiting coach as **Instructor**, either with an admin login to see their own schedule or as a bookable resource only.
* **Promoting from within** - a padel gym manager promotes a trusted front desk lead to **Manager** without needing an admin.
* **Handing over ownership** - a yoga studio founder makes the incoming owner an **Admin** with **Account owner** on, and can be removed once the handover is complete.
See [Worked examples](#worked-examples) for the steps behind each of these.
Managing users is a staff-management capability (`staff.manage.organization`), so admins and managers can reach this page. Managers act within a role hierarchy (see [Who can manage whom](#who-can-manage-whom)).
## The roles
1Club ships with five roles. Pick the one closest to what the person actually does day-to-day. Each role is a default set of `${module}.${action}.${scope}` permissions.
| Role | Typical use | What it can do |
| - | - | - |
| **Admin** | Owner or main administrator | Full management of every module (members, messaging, operations, staff, billing, marketing, analytics, settings, inventory) at organization scope. |
| **Manager** | Club or department manager | Members, operations, marketing, analytics, and inventory full management within a club; staff management; billing read and create. No settings. |
| **Front desk** | Receptionist or front-of-house staff | Members create and read, messaging full, bookings and check-ins create and read, billing read and create, inventory read. No settings, no organization analytics. |
| **Instructor** | Coach or teacher | Manages only their own bookings, classes, and check-ins. Reads their own earnings, analytics, and messages. Reaches their own profile and notification preferences. |
| **Member** | End member | No admin dashboard permissions. Uses the member portal and public apps. |
The full module-by-module matrix lives in [Roles and permissions](/reference/roles-permissions). You can also open the **Role permissions** info icon in the invite or edit dialog to see the same grid without leaving the page.
An admin can customise what each role can do in **Settings** > **Organization** > **Permissions** (a per-role permission editor). The five role names are fixed and you cannot add a sixth, but the permissions inside a role are adjustable. The permission editor is admin-only.
### Owner flag
Owner is a separate flag on the user, not a sixth role. An Admin can be marked as **Account owner**, and only owners can delete the organization. The **Account owner** toggle appears only when the role is set to Admin - switching to any other role clears it. Owners bypass permission gates by design.
## The Users page
Open **Settings** > **Organization** > **Users**. The page is a single table of every organization user with these tabs across the top:
* **All** - everyone in the organization.
* **Admin**, **Manager**, **Front desk**, **Instructor**, **Member** - filtered by role.
Each tab shows a badge with the count. The search box filters by name or email and resets pagination.
Each row shows the user's name and avatar, a role chip (plus an **Owner** chip if applicable), status (Active, Invited, Pending invite, Expired), and their join date. Click an active user row to open the edit dialog.
## Inviting a user
Click **Invite user** in the top right. The dialog opens with a role picker and a mode toggle:
* **Add new user** - enter first name, last name, and email (plus optional contact details).
* **Select existing contact** - pick someone who already exists in your contact list.
Then choose a role from the five above. If you pick **Admin**, an **Account owner** toggle appears - turn it on only if this person should be allowed to delete the organization. Submit, and 1Club sends an invitation email. The user appears in the table with status **Invited** until they accept.
If you pick **Instructor** for someone who does not yet have an instructor profile, the dialog shows the instructor fields inline so the profile is created in the same step. This is so an instructor always has a profile to attach classes, bookings, and schedule slots to. For an instructor you can also toggle **Allow admin portal access** to decide whether they get a login and invitation email or exist only as a bookable resource.
## Editing, resetting, and removing
Click any active user row to open the edit dialog. You can change their role, toggle the owner flag (Admin only), edit their contact details, or - if they have an instructor profile - edit instructor-specific fields. You cannot edit your own account from this page.
From the row's actions menu:
* **Send invitation** / **Resend invitation** - **Send invitation** appears for users in **Pending invite** state (no email sent yet); **Resend invitation** appears for users still **Invited** or **Expired**. Both send a fresh email.
* **Reset password** - Sends a password reset email to another active user, so they can set a new password from the link. This does not set a password directly for them. Available to admins for active users other than themselves.
* **Delete invitation** - Cancels an unaccepted invite.
* **Remove user** - Removes an active user from the organization. The contact record (if any) stays; only the organization-user link is deleted.
## Statuses
* **Active** - signed up and using the admin.
* **Invited** - invitation sent, waiting for the user to accept.
* **Pending invite** - the invitation has not been sent yet (for example, created from a contact without an email). Use **Send invitation** when ready.
* **Expired** - the invitation link expired. Use **Resend invitation** to send a new one.
## What changing a role does
Roles replace each other - there is no permission delta or stacking. When you move someone from Front desk to Manager, their old permissions are dropped and the Manager set is applied wholesale. The owner flag is preserved across role changes, but the owner flag still requires the Admin role.
Existing sessions pick up the new role on their next request. There is no force sign-out button - if you need someone out immediately, **Remove user**.
## Who can manage whom
User management is bounded by a role hierarchy (admin is more senior than manager, which is more senior than front desk and instructor, which are more senior than member):
* **Admins and owners** can manage anyone, assign any role, and grant Admin or account ownership.
* **Managers** can invite, edit, reset the password of, and remove users whose role is **below** manager (front desk, instructor, member), and can promote someone up to manager. Managers cannot modify themselves, an admin, or another manager, and can never grant the Admin role or account ownership.
* **Front desk, instructor, and member** roles cannot manage users at all.
This hierarchy is enforced on both the client and the server, so a blocked action fails even if someone reaches it by a direct link.
## Worked examples
### Onboarding a receptionist at a climbing gym
You hire a new front-of-house staff member. Click **Invite user**, keep **Add new user**, enter their name and email, and choose **Front desk**. They receive an invitation email and appear as **Invited**. Once they accept, they can take bookings, check climbers in, and run point of sale, but they cannot open organization settings or analytics.
### Adding a visiting muay-thai coach as an instructor
A guest coach will run a block of classes. Invite them with the **Instructor** role. Because they have no instructor profile yet, fill in the inline instructor fields (sports, type, bookable). Leave **Allow admin portal access** on if they need to see their own schedule and check-ins in the admin, or turn it off to keep them as a bookable resource only.
### A manager promoting a padel front desk lead
A club manager wants to promote a trusted front desk lead. The manager opens the user, changes the role from Front desk to Manager, and saves. The manager can do this because promoting up to manager is allowed. They could not promote the same person to Admin - only an admin or owner can grant Admin.
### Handing over ownership at a yoga studio
The studio founder is stepping back. An admin who is also the owner invites the incoming owner as **Admin**, turns on **Account owner**, and saves. Both are now owners and can delete the organization; the founder can be removed later once the handover is complete.
## Tips and best practices
* **Match the role to the job.** Start people on the narrowest role that lets them work, and move them up only when they need more.
* **Reserve owner for the few who truly need it.** Only owners can delete the organization, so keep the owner list short.
* **Use Reset password, not shared logins.** Never share one admin login - invite each person and send a reset if they are locked out.
* **Clean up invitations.** Delete stale **Pending invite** or **Expired** rows so the table reflects who really has access.
* **Remove leavers promptly.** There is no force sign-out, so **Remove user** is how you cut access immediately.
## Troubleshooting
**Issue**: A manager cannot edit or remove another manager or an admin.
**Solution**: This is by design. Managers can only manage roles below manager and promote up to manager. Ask an admin or owner to make the change.
**Issue**: The Account owner toggle disappeared.
**Solution**: Account owner is only available for the Admin role. If you switched the role away from Admin, the owner flag is cleared. Set the role back to Admin to re-enable it.
**Issue**: I invited an instructor but they have no schedule to manage.
**Solution**: The instructor needs an instructor profile. Complete the inline instructor fields in the invite or edit dialog so classes, bookings, and slots have a profile to attach to.
**Issue**: A user says they never got their invitation.
**Solution**: Check the status. If it is **Pending invite**, use **Send invitation**. If it is **Invited** or **Expired**, use **Resend invitation**. Ask them to check spam.
**Issue**: I need to change my own role.
**Solution**: You cannot edit your own account here. Ask another admin to update it in **Settings** > **Organization** > **Users**.
## Related
* [Roles and permissions](/reference/roles-permissions) - The full module-by-module permission matrix and the permission editor.
* [Profile](/settings/profile) - Your own account, password, language, and theme.
* [Signing in](/reference/sign-in) - Sign-in methods and adding a password to a social account.
* [Access control](/settings/access-control) - Door check-in modes, separate from admin roles.
* [API tokens](/settings/api-tokens) - Service tokens for programmatic access; not the same as user invites.
# API tokens
Source: https://docs.1club.ai/settings/api-tokens
Create, scope, edit, and revoke API tokens for programmatic integrations, from Settings > Organization > APIs & webhooks.
API tokens are keys that external systems - your website, an integration, or a script - use to read and write your organization's data through the public API, without anyone sharing an admin login. Each token carries its own set of scopes and its own optional expiry, so you can grant exactly the access an integration needs and revoke it independently. Owners and admins create and manage tokens, and hand them to whoever runs the integration.
## When to use it
Create a token whenever software, rather than a person, needs your data. Give each integration its own token with only the scopes it needs and an expiry that matches how long it should live, so one leaked or retired key never exposes more than it has to.
Typical uses:
* **A website showing your timetable** - a padel gym's marketing site reads **Classes**, **Clubs**, and **Areas** with a read-only token that can never change anything.
* **Piloting a booking integration** - a climbing gym gives a third-party kiosk read and write access to bookings on a one-month token, then extends or reissues it once the pilot is stable.
* **A one-off export** - a yoga studio pulls a year of transactions for reconciliation with a read-only token that expires after a day.
See [Worked examples](#worked-examples) for the exact scopes and expiry behind each of these.
Programmatic API access is a paid feature. On a free or trial plan you can view and revoke existing tokens, but the **Add** button is disabled until you upgrade. A banner at the top of the page explains this when it applies.
## Access and permissions
* **Viewing** the token list requires the `settings.read.organization` permission.
* **Creating, editing, and revoking** tokens requires `settings.manage.organization`.
In practice, only admins (and owners) hold organization settings management, so managers and other roles cannot create or view API tokens. See [Roles and permissions](/reference/roles-permissions).
## Creating a token
API tokens live on **Settings** > **Organization** > **APIs & webhooks**. The list shows each token's prefix, scopes, expiry, last-used date, and created date.
1. Click **Add** in the API tokens section.
2. Fill in the token details:
* **Name** - A label that identifies where this token is used, for example "Website class feed".
* **Description** - An optional note on what the token is for.
* **Expires** - Pick an expiry from the dropdown, or leave it as **No expiry** for a token you manage manually.
* **Scopes** - In the resource table, open the **Permissions** dropdown next to each resource and select the actions you want to allow. Leave a resource untouched for no access to it. You must select at least one scope.
3. Click **Save**.
4. Copy the token immediately from the success banner. The raw token is shown only once and is never displayed again after you dismiss the banner. Store it in your integration's secret store.
The token list shows only a short **prefix** of each token for identification, never the full value.
## Token scopes
Scopes use the format `resource:action`. Each token is a set of these strings. The resource table in the dialog offers the resources and actions that are currently available:
| Resource | Available actions |
| - | - |
| **Classes** | Read, Write |
| **Content** | Read |
| **Clubs** | Read |
| **Transactions** | Read |
| **Areas** | Read |
| **Bookings** | Read, Write |
| **Contacts** | Read, Write |
| **Instructors** | Read |
| **Plans** | Read, Write |
| **Memberships** | Read, Write |
| **Check-ins** | Read, Write |
The **Transactions** scope covers the records the admin calls [Charges](/sales/charges).
* **Read** allows fetching data (GET requests).
* **Write** allows creating, updating, and deleting data where that resource exposes those operations.
Write actions are highlighted in the interface so you can spot them at a glance. Grant only the scopes an integration needs, and avoid write access unless the external system genuinely has to create or change data. The available resources and actions can grow over time; the dialog always reflects the current list.
A wildcard scope exists that grants access to all current and future scopes. Treat it as an all-access key: use it only for a fully trusted, internally managed integration, and prefer explicit `resource:action` scopes everywhere else. The wildcard is an API-key feature only - an MCP client that asks for it over OAuth is rejected and must name the scopes it wants. See the [MCP tools reference](/mcp/tools#how-a-connection-gets-its-scopes).
## Token expiry
| Option | Use when |
| - | - |
| 1 hour / 6 hours / 1 day | Short-lived scripts or one-time operations |
| 1 week / 2 weeks / 1 month | Temporary integrations or testing |
| No expiry | Long-running integrations that you actively manage |
Tokens with no expiry stay active until you revoke them. The list shows the time remaining for tokens that will expire, and **Expired** once they lapse.
## Editing a token
Open a token's actions menu and choose edit to change its name, description, scopes, or expiry. Editing does not reveal the raw token again.
Choosing an expiry in the edit dialog sets a fresh countdown from now. If you edit a token without picking an expiry option, its expiry is cleared and it becomes a no-expiry token. Set the expiry deliberately each time you edit.
## Revoking a token
1. Find the token in the list.
2. Open its actions menu and choose **Revoke**.
3. Confirm.
Revoking is immediate and permanent. Any integration using that token stops working at once. Create a new token if the integration needs to continue.
## Webhooks and MCP
* **Webhooks** remain on this page. Register endpoint URLs that 1Club calls when availability changes, each with its own signing secret. See the [Webhooks reference](/api-reference/channel-integration/webhooks).
* **MCP** is separate from API tokens. Open **Settings** > **Artificial Intelligence** > **AI assistants (MCP)** to copy the endpoint, review connected assistants, and revoke OAuth grants. See [Connect an assistant](/mcp/connect).
## Worked examples
### Website pulling the class schedule for a padel club
Your marketing site shows the upcoming padel timetable. Create a token named "Website schedule", grant **Classes: Read**, **Clubs: Read**, and **Areas: Read**, and leave everything else off. The site can render the schedule but can never modify anything. Give it **No expiry** and rotate it if the site is rebuilt.
### A booking integration for a climbing gym
A third-party kiosk needs to read availability and create bookings. Create a token named "Kiosk bookings", grant **Bookings: Read** and **Bookings: Write**, plus **Areas: Read** and **Clubs: Read** for context. Set a **1 month** expiry while you pilot the integration, then extend or reissue once it is stable.
### A short-lived data export script
You need to pull transactions once for a year-end reconciliation at a yoga studio. Create a token named "Year-end export", grant **Transactions: Read**, set the expiry to **1 day**, run the script, and let the token lapse on its own.
## Tips and best practices
* **Name tokens descriptively** so you always know what each one powers.
* **Use the minimum scopes** an integration needs, and avoid write access when read is enough.
* **Prefer explicit scopes over the wildcard** unless the integration is fully trusted and internally managed.
* **Set an expiry** for scripts and pilots so forgotten tokens cannot linger.
* **Watch the last-used date** to spot tokens that are no longer active and revoke them.
* **Store tokens in a secret manager** - never commit them to source code, paste them into emails, or share them in chat.
* **Revoke and reissue on any suspicion** of exposure; there is no way to reveal an existing token again.
## Troubleshooting
**Issue**: The Add button is disabled.
**Solution**: API access is a paid feature. Free and trial plans can view and revoke tokens but not create them. Upgrade the plan, then create the token.
**Issue**: I closed the dialog and lost the token value.
**Solution**: The raw token is shown only once. Revoke the token and create a new one, and copy it before dismissing the banner this time.
**Issue**: An integration suddenly returns authorization errors.
**Solution**: Check whether the token expired (the list shows **Expired**) or was revoked. Also confirm the token has the scopes the request needs - a read-only token cannot create or update data. Reissue with the correct scopes if needed.
**Issue**: A manager cannot see the API tokens page.
**Solution**: Viewing tokens requires organization settings access, which only admins hold. Ask an admin to create or manage the token.
**Issue**: My integration gets 403s on writes even with a token.
**Solution**: The token needs a write scope for that resource (for example **Bookings: Write**). Edit the token and add the write action, or reissue it with the right scopes.
## Related
* [Public API](/api-reference/introduction) - API reference documentation and authentication.
* [Webhooks](/api-reference/channel-integration/webhooks) - Event callbacks configured on the same page.
* [Integrations](/settings/integrations) - Connect third-party services like Stripe and Google.
* [Roles and permissions](/reference/roles-permissions) - Why only admins can manage tokens.
# Areas
Source: https://docs.1club.ai/settings/areas
Configure the physical spaces members book - courts, rooms, fields - under Settings > Clubs > Areas, with area types, operating hours, pricing, attributes, and schedule colors.
Areas are the bookable physical spaces in your gym: tennis and padel courts, fitness rooms, soccer fields, studios, pools, and climbing walls. Owners and managers set them up, members book them by the hour, and staff book them for members from the schedule. Each area belongs to one gym, has an area type for grouping, an hourly price, operating hours, a set of attributes, and a color used on the schedule.
## When to use it
Create an area for every space someone can book on its own. The area decides what a booking costs, when the space is open, how many bookings it takes at once, and what members can filter on when they pick a space. It also gets its own column on the [Schedule](/schedule/calendar), so staff can see at a glance what is booked where.
Typical uses:
* **Padel courts members pick by conditions** - six courts, where members filter for indoor courts with panoramic glass or outdoor courts with floodlights, and each court has its own hourly price and schedule color.
* **A multi-sport racket center** - tennis, padel, and squash courts under separate area types, so each offers the right surfaces and your premium courts appear first in the booking flow.
* **A martial-arts dojo** - a tatami mat area for jiu-jitsu and muay thai, plus a boxing ring or an MMA cage as its own space.
* **A climbing gym** - bouldering and roped walls, with a cap on how many climbers can book a wall session at once.
See [Worked examples](#worked-examples) for how to set up each of these.
## The list page
You manage areas under **Settings** > **Clubs** > **Areas**. You can also open the page from the overflow menu on the [Schedule](/schedule/calendar), next to Bookings, Classes, and Attendance.
Areas are grouped by gym. Each gym section shows its areas in a sortable row layout with these columns: drag handle, area name, area type, price, status, visibility, and a row menu. Drag a row by its handle to reorder areas inside that gym; the order is what members see in the booking flow. If two admins reorder at the same time, the second save returns a conflict and the list reloads to the latest order.
Click **Add** in a gym section header to create an area scoped to that gym, or open a row to view its details.
## Creating or editing an area
The area dialog asks for:
* **Name** and **Gym** - which gym the area belongs to.
* **Area type** - the grouping (Court, Room, Field, Studio, etc.). Area types are managed under **Settings** > **Clubs** > **Area Types**, carry their own sort order, and define the **sports** played in that type of space plus a **default max players**. The chosen area type's sports also decide which attributes are offered (see below).
* **Description** and **Images** (up to 5) - shown on the public booking pages.
* **Color** - the color used for the area's blocks on the schedule.
* **Price per hour** - in your organization's currency. Used when a booking is created against this area; the booking duration multiplied by this rate seeds the line total before discounts.
* **Max concurrent bookings** - how many simultaneous bookings the area accepts (e.g. a 4-court padel block modeled as a single resource might be set to 4). Minimum 1.
* **Status** - **Active**, **Inactive**, or **Maintenance**. The schedule page filters its area query to active areas, so inactive and maintenance areas don't appear on the calendar.
* **Visibility** - **Public** (visible to anyone), **Member only** (visible after sign-in), or **Private** (admin-only). The visibility chip appears on the detail page and the row.
* **Attributes** - a multi-select of the area's intrinsic characteristics (see below).
* **Operating hours** - per-day open and close times, with an option to fall back to the gym's default hours.
### Area attributes
Attributes describe what an area physically *is* - separate from club-level amenities, which describe services the whole club offers (showers, parking, cafe). Attributes are set per area, so Court 1 can be floodlit while Court 2 is not. They power marketplace filtering and help members pick the right space.
The catalog is platform-curated and grouped into five categories:
* **Environment** - Indoor, Outdoor, Covered, Semi-covered.
* **Lighting** - Floodlights, Natural light.
* **Climate** - Heated, Air-conditioned, Climate controlled.
* **Surface** - sport-specific, e.g. Clay, Grass, Acrylic hard court, Carpet, Artificial grass (racket); Natural turf, Artificial turf, Sport court, Sand (team); Rubber flooring (fitness); Sprung floor, Marley floor (studio and dance); Tatami (martial arts).
* **Feature** - sport-specific, e.g. Panoramic glass (padel), Lifting platform and Functional rig (fitness), Ballet barre (dance), Boxing ring and Cage (martial arts), Lap lanes (water), Bouldering wall, Top rope, Lead wall, Auto belay (climbing).
The picker is **sport-aware**. Universal attributes (environment, lighting, climate) always show. Surface and feature attributes appear only when they match the area type's sports - so a padel court offers Artificial grass and Panoramic glass, a dojo offers Tatami and Cage, and a climbing area offers Bouldering wall and Auto belay. If you haven't picked an area type yet, all attributes show until you do. An attribute you've already selected is never hidden, even if you later change the area type.
## The area detail page
Click an area row to open its detail page. The header shows the name with action buttons to toggle visibility (cycles Private to Member only to Public), edit, and delete. The body has two cards:
* **Information** - area type, price per hour, status chip, color swatch, visibility chip, description, and attributes.
* **Operating hours** - the weekly schedule, read-only.
If the area has images, a sortable image gallery sits above the information card. Drag thumbnails to reorder.
Deleting an area is destructive: it removes the area, all its bookings, and the invoices linked to those bookings. The confirmation dialog spells this out before you commit. Prefer setting an area to **Inactive** if you only want to stop new bookings.
## How areas show up on the schedule
The [Schedule](/schedule/calendar) page reads active areas and renders one column per area in day view, grouped by area type. Each block on the calendar uses the area's color (or a hash-generated color if none is set). Hidden area types or hidden uncategorized areas - configured under **Settings** > **Bookings** > **Schedule Display** - default to off but can be toggled per browser via the resource filter panel.
The booking dialog uses the area's price per hour to seed the price breakdown. A [booking policy](/settings/booking-policies) scoped by area type can override payment timing or refund rules for a subset of areas.
## Worked examples
### Padel club with indoor and outdoor courts
You run six padel courts and want members to filter by conditions.
* Create an **Area type** called *Padel court* under **Settings** > **Clubs** > **Area Types**, with the sport set to padel.
* Add one area per court. On the indoor courts set attributes **Indoor**, **Panoramic glass**, **Artificial grass**, **Climate controlled**. On the outdoor courts set **Outdoor**, **Floodlights**, **Artificial grass**.
* Set **Price per hour** per court, and give indoor and outdoor courts different colors so the schedule reads at a glance.
* Pair with a [booking policy](/settings/booking-policies) scoped to the *Padel court* area type for lead times and cancellation windows.
### Multi-sport racket center
You offer tennis, padel, and squash.
* Create three area types (*Tennis court*, *Padel court*, *Squash court*) so the attribute picker offers the right surfaces for each (Clay and Acrylic hard court for tennis, Artificial grass for padel).
* Reorder areas within each gym so your show-court or premium courts appear first in the booking flow.
### Martial arts dojo
* Create a *Mat area* (sport: jiu-jitsu, muay-thai) and set attributes **Indoor**, **Tatami**, **Mats**, **Air-conditioned**.
* Add a *Ring* area for boxing with **Boxing ring**, or a *Cage* area for MMA with **Cage** and **Padded walls**.
### Climbing gym
* Create a *Bouldering* area with **Bouldering wall** and **Training board**, and a *Roped* area with **Top rope**, **Lead wall**, and **Auto belay**.
* Use **Max concurrent bookings** to cap how many climbers can book a wall session at once.
## Tips & best practices
* **Set the area type first.** It scopes the attribute picker to relevant surfaces and features and seeds the default player count, so create your area types before your areas.
* **Attributes are for members, not billing.** They drive filtering and discovery; price and payment live on the area and the [booking policy](/settings/booking-policies).
* **Prefer Inactive over Delete.** Deleting removes bookings and invoices. Use **Inactive** or **Maintenance** to take a court offline temporarily.
* **Give each area a distinct color** so the schedule is readable when several areas run side by side.
* **Model multi-court blocks deliberately.** Either create one area per court (best for per-court pricing and attributes) or one area with **Max concurrent bookings** set to the court count (simpler, but no per-court detail).
## Troubleshooting
**Issue**: An area doesn't appear on the schedule.
**Solution**: The schedule only shows **Active** areas. Check the area's status is Active, not Inactive or Maintenance. Also confirm its area type isn't hidden under **Settings** > **Bookings** > **Schedule Display**, or toggle it back on in the resource filter panel.
**Issue**: The attribute I want isn't in the list.
**Solution**: The picker filters to attributes relevant to the area type's sports. Set or change the **Area type** so its sports match (e.g. pick a racket sport to see Panoramic glass and Artificial grass). Universal attributes (Indoor, Floodlights, Heated) always show.
**Issue**: I can't delete an area.
**Solution**: Deleting an area also deletes its bookings and their invoices, so the confirmation is deliberate. If you only want to stop new bookings, set the area to **Inactive** instead.
**Issue**: My reorder didn't stick.
**Solution**: If another admin reordered the same gym's areas at the same time, the later save is rejected and the list reloads to the latest order. Reapply your order on the refreshed list.
## Related
* [Schedule](/schedule/calendar) - Where bookings against areas show up.
* [Bookings](/schedule/bookings) - Day-to-day reservation management.
* [Booking policies](/settings/booking-policies) - Rules that apply per area type.
* [Classes](/schedule/classes) - Group activities held in an area.
# Billing entities
Source: https://docs.1club.ai/settings/billing-entities
Run more than one company from a single 1Club organization, each with its own legal details, invoice numbering, and payment accounts.
A **billing entity** is a company you invoice and take money through. Owners and admins set these up so each sale is invoiced and paid under the right legal name. Every organization has one from the day it is created, holding the legal name, tax number, registered address, bank details, and invoice numbering that appear on your documents, and nothing changes until you add a second one.
Most gyms are one company and never need a second. If yours trades through several - a separate company per venue, or one company for classes and another for the bar and shop - add a billing entity for each, and tell 1Club which one sells what.
## When you need a second company
Each company keeps its own legal details, invoice and pro-forma numbering, bank details, and its own Stripe, myPOS, or DatecsPay account. You point a gym or a revenue account at a company and every sale follows. A sale belongs to one company, decided when the sale is made and kept with it forever, so re-pointing a gym later never rewrites what you already sold. Reports and exports narrow to one company, so your bookkeeper gets the file for the company they are filing for.
Add one when money legally belongs to a different business, not merely to a different place or department. Typical cases:
* **A company per venue** - each venue is its own registered company, so each must invoice under its own name and number series and be paid into its own bank account.
* **A separate company for the bar or shop** - your classes and courts sit in one company while the bar, the shop, or the physio room sits in another.
* **A gym you have taken over** - it keeps trading under its own company for now.
If you only want to see what each venue earns, you do not need a second company - the club filter on your reports already does that.
## Set up a company
1. Go to **Settings** > **Billing** > **General**. The **Billing entities** section lists the companies you have, with the default marked.
2. Click **Add billing entity**, give it a name (this is an internal label, members never see it), and save.
3. Open the row and fill in what should print on its documents: legal name, tax and registration numbers, registered address, and bank details.
4. Set its **Invoice numbering** and **Pro-forma numbering** templates. Each company numbers its own documents, so give each a distinct prefix - `INV-` for one and, say, `BAR-` for another - or both companies will produce a document numbered `INV-1`.
Your first company is the **default**. It is the one every sale falls back to when nothing else names a company, and it is the only one that can hold wallet credit. You can make another company the default later from its row.
## Decide what sells through which company
There are two ways to route a sale, and you can use both.
### By gym
Open **Settings** > **Clubs**, pick the gym, and set **Billing entity**. Everything sold at that gym - bookings, memberships, check-ins, the till - bills through that company.
Leave it empty and the gym bills through your default company.
### By revenue account
Open **Settings** > **Billing** > **General**, scroll to **Revenue Accounts**, pick the account, and set its billing entity. Everything posted to that account bills through that company **wherever it was sold**.
This is how a bar or a shop owned by a separate company gets its own invoices without having a venue of its own: point the bar's revenue account at the bar company, and a drink sold at any of your gyms bills through it.
### Which one wins
The revenue account decides first, then the gym, then your default company.
| The sale | Bills through |
| - | - |
| A court booking at a gym pointed at Company B | Company B |
| A drink whose revenue account is pointed at Company C | Company C, at any gym |
| A membership at a gym that names no company | Your default company |
## Connect each company's payment accounts
Each company takes money into its own accounts. Under **Settings** > **Integrations**, pick the company at the top of the Stripe, myPOS, or DatecsPay page and connect it there, the same way you connected your first one.
* Card payments online settle into the account of the company that sold.
* A card reader charges through the account of the company that bills for the gym it sits at. Register the reader after you have pointed the gym at its company, and connect that company's payment account first.
* Saved cards belong to the company they were saved with. A member who pays two of your companies has a card stored with each, and their payment methods page in the portal lists both.
Re-pointing a gym at a different company is refused while that gym still has card readers registered. Retire the readers, change the company, then register them again - a reader belongs to one payment account and cannot charge through another.
## What each company issues
Invoices and pro-formas carry the legal name, tax number, registered address, and bank details of the company that sold, and draw their number from that company's own series. Two companies therefore number independently, and both can have a document numbered 1 on the same day. That is normal: each company keeps its own sequence, which is why distinct prefixes are worth setting up front.
Bill runs group by company before they issue, so "one invoice per customer" never puts two companies on one document. A member who bought from two of your companies in the same period gets one invoice from each.
Raising an invoice by hand is the one place nothing else names the company for you, so the invoice form asks. Leave it alone and your default company issues it.
See [Invoices](/sales/invoices) for the rest of the invoicing flow.
## Two things that stay whole
**A basket cannot mix companies.** If someone tries to pay for a court booking that bills through one company and a drink that bills through another in a single checkout, the sale is refused and the two companies are named. Sell them separately: one payment goes to one company, and one invoice comes from one company.
**Wallet credit belongs to your default company.** Members can only spend credit on that company's sales, it is only offered at checkout for those sales, and a refund goes back to the wallet only when the original payment was taken by it. Cancelling a booking sold by another company leaves the refund owed instead of crediting the wallet, so you can return it from the payment - see [Payments](/sales/payments).
## Reporting per company
The revenue and charges reports carry a **Billing entity** filter next to the club filter, and the invoice list carries one too. Narrowing the report narrows its export, and the exported file names the company.
The accounting export under **Settings** > **Data Tools** > **Export** can be narrowed the same way, which is usually what your bookkeeper wants: one file per company, per period.
The filter only appears once you have more than one company.
## Retiring a company
Deactivate a company from its row when it stops trading. A deactivated company takes no new sales and is no longer offered when you point a gym or a revenue account at one, while its history, documents, and numbering stay exactly as they were.
Before you can deactivate it, re-point anything that still sells through it. 1Club tells you what is left - gyms or revenue accounts - rather than quietly moving those sales to another company.
Deleting is only possible for a company that never sold anything and holds no payment credentials. Anything with history is deactivated instead.
## Troubleshooting
| What you see | Why | What to do |
| - | - | - |
| "These items are billed by different billing entities" | The basket mixes companies. | Sell the items in separate sales. |
| Two companies both issued a document numbered 1 | Each company has its own series. | Give each company a distinct numbering prefix. |
| Wallet is missing at checkout | The sale bills through a company other than your default. | Take another payment method; wallet credit is your default company's. |
| A card reader stops working after a change | The gym was re-pointed, or the reader was registered before its company was connected. | Connect that company's payment account, then register the reader again. |
| A member's saved card is not offered for a renewal | The card is saved with a different company than the one billing the renewal. | Ask the member to pay once through the right company, or to add a card for it; the card is then saved there. |
| You cannot deactivate a company | Gyms or revenue accounts still bill through it. | Re-point them first; the message says how many of each. |
## What members see
Nothing changes for members. They browse your gyms, book, and pay as before. The only difference is on the paperwork: a receipt or an invoice carries the legal name and details of the company that sold to them, and if they buy from two of your companies they get a document from each.
# Booking policies
Source: https://docs.1club.ai/settings/booking-policies
Configure how members book, when they pay, and how check-in is enforced, under Settings > Bookings > Booking and cancellation policies.
Booking policies decide when payment is collected, whether bookings need a contact, whether check-in is required, and how cancellations are refunded. Owners and admins set them up, and members and the front desk meet their rules when a booking is made, checked in, or cancelled. You can have one policy for the whole organisation or different policies per location, area type, class type, instructor type, or plan.
## When to use it
A booking policy is a reusable configuration that turns your house rules into the booking flow, so staff don't have to enforce payment, no-show, or refund rules by hand. When a member books, the system finds the matching policy (most specific scope wins) and applies its rules.
Typical uses:
* **A drop-in yoga studio** - walk-ins are welcome, the front desk collects payment at check-in, and a trial visitor with no member account can still be checked in.
* **A strict class-based gym** - classes are paid online at booking, and any booking not checked in by the end of the day is marked as a no-show.
* **Personal training booked over the phone** - a policy scoped to personal training lets admins book callers who haven't signed up yet.
* **Courts with a cancellation deadline** - a policy scoped to your padel court area type refunds in full up to 24 hours ahead, half up to 4 hours ahead, and nothing after that.
See [Worked examples](#worked-examples) for the settings behind the first three.
If you're picking a model for the first time, read [Check-in Models](/reference/check-in-models) before configuring.
## Rules for members, warnings for staff
Most booking rules exist to shape what a member may book or do for themselves. They apply in full wherever members act on their own: the portal, the mobile app, your website, the marketplace, the kiosk, and the check-in app.
Staff are not held to them. When an admin, manager, front desk, or instructor books or checks someone in from the admin dashboard or the instructor portal, these rules never refuse them. A short note names the rule, the button stays the same, and the first click goes through. The same applies to bookings made by the in-app assistant and booking imports.
The rules that work this way:
* **Min. notice** and **Max. ahead** (including a repeating booking that reaches past **Max. ahead**)
* **Min. booking slot**, **Max. booking slot**, and **Booking duration step**
* The working hours of the gym or the area
* Who an area type or class type is open to
* A deposit rule that restricts new bookings when a member's wallet is short
* **Enforce payment** (at booking or at check-in), and **Allow walk-ins**
* The **Waitlist claim window**
* The check-in window (from 1 hour before the start until the end of the day)
* A plan's **Require payment upfront**, when staff sell or renew a membership
Nothing is recorded as an override for these, because nothing was overridden. A balance the rule would have collected still stays owed, so you can collect it later.
Two kinds of rule still apply to staff:
* **Staff-only switches.** **Allow anonymous bookings** and **Allow anonymous check-ins** decide whether staff may book or check in someone with no contact at all. Only staff can do either, so they stay enforced.
* **Facts about the schedule.** A coach on time off, on a break, or outside their hours, a blocked area, and a court or coach that is already taken. The booking dialog names these before you save, and you book past them with the same click when your role allows it. See [Bookings](/schedule/bookings#warnings-and-scheduling-conflicts).
## Managing booking policies
### Viewing all policies
1. Go to **Settings** > **Bookings** > **Booking and cancellation policies**.
2. Each policy appears as a card with quick-glance metadata: cancellation rules count, min slot, scope, clubs, and which payment and check-in toggles are on.
3. Click a policy to edit it, or click **Add** to create a new one.
### Creating or editing a policy
1. Click **Add** in the top right (or click an existing policy to edit it).
2. Fill in the sections: **Policy details**, **Scope**, **Booking rules**, **Cancellation rules**, and **Orders**.
3. Click **Save** when done.
## Booking rules
This is the heart of the policy. Every toggle and field below lives in the **Booking rules** section of the policy dialog.
### Min/max booking slot
* **Min. booking slot (minutes)** - Minimum booking duration. Members can't book anything shorter.
* **Max. booking slot (minutes)** - Maximum duration. Members can't book anything longer.
* **Booking duration step (minutes)** - Booking duration must be the minimum plus a multiple of this step (e.g. min 60 + step 60 means only 60, 120, 180 min are allowed).
Staff booking a slot outside these limits see a note such as "Members book on a 60 min grid" and can still book it.
### Booking window
* **Min. notice** (hours) - Members must book at least this many hours ahead. Leave blank for no limit.
* **Max. ahead** (days) - Members can only see and book up to this many calendar days ahead. Leave blank for no limit.
Staff can book outside the window. The booking dialog shows a note such as "Members book up to 7 days ahead", and **Add** books it on the first click.
#### Max. ahead and repeating bookings
**Max. ahead** is measured against the **last** occurrence a repeating booking would
create, not just the first one. A series that ends on a date, or after a set number of
repeats, is created in full the moment you save it (see
[Repeating schedules](/reference/repeating-schedules)), so every occurrence in it has to
fit the window.
With **Max. ahead** set to 14 days, a weekly court booking repeating "after 52 times"
reaches nearly a year out and is refused. The same booking repeating "after 2 times" ends
inside the window and is created normally.
Two things to know:
* **Staff are not held to it.** A series staff create from the admin dashboard is
created in full, and the confirmation says it reaches further ahead than members can
book.
* **Repeating with no end is exempt.** A series set to **Never** has no last occurrence to
measure, and it is written a rolling eight weeks at a time, so each occurrence lands
inside the window when it is created. Use **Never** for a standing weekly booking at a
gym that also sets a short **Max. ahead**.
### Waitlist claim window (minutes)
When a spot opens 2-12 hours before a class, the first person on the waitlist has this many minutes to claim it. The default is 15. Leave empty to use 15.
The window binds the member claiming the spot. Staff can still turn an entry into a booking after its window has passed - see [Waitlists](/schedule/waitlists).
### Enforce payment
A single dropdown that decides when payment is enforced. Pick one of three options:
* **At booking** - Members must pay in the portal before the booking is confirmed. A booking that still has unpaid charges can't be self-checked-in at the kiosk or in the check-in app.
* **At check-in** - Members can create bookings without paying in advance, but they can't check themselves in until the payment is collected (or an active membership / external program covers the visit).
* **None** - Payment is not enforced. Members can be checked in even if they still have unresolved payments. The front desk uses judgment to follow up.
Pick **At booking** when you sell classes online and want guaranteed revenue per booking, or when you don't want the front desk to chase payment at the door.
Pick **At check-in** when bookings can happen without immediate payment and you collect payment at the door. This is the typical drop-in or walk-in model.
Pick **None** for community gyms, martial-arts schools, kids' programs, and other settings where staff log attendance on the spot and handle payment separately.
This replaces the older **Require Payment Upfront** and **Require Payment at Check-in** toggles. The two are now mutually exclusive options in a single dropdown - you can no longer enable both at once. Existing policies are migrated automatically: upfront wins if both were on, otherwise the matching single toggle is preserved, and policies with neither toggle land on **None**.
**At the front desk** - Staff are not held to **Enforce payment**. A booking created from the admin dashboard is confirmed without payment, so you can record bookings that will be paid later. At check-in, the dialog says the gym asks for payment at check-in, and staff can still let the member in: without a payment, the amount stays owed. One exception remains: a booking with no entrance method still needs one picked (membership, external program, or direct payment) before check-in, whatever the policy says.
### Upsell available plans
When enabled, members see suitable membership plans on the booking confirmation screen and can add one to the same order. Useful for converting drop-ins to members. The in-app help reads: "When enabled, members can see suitable membership plans on booking confirmation and add one to the same order."
By default every matching plan is offered. To show only a specific subset, use the **Plans to upsell** multi-select that appears once the toggle is on:
* Leave it empty to show all matching plans.
* Pick specific plans to show only those.
Display order follows the global plan order set on the Plans page - see [Membership plans](/sales/plans).
### Check-in required
> Mark no-show if not checked in by end of day.
When on, any booking that isn't checked in by the end of the booking day is automatically marked as a no-show. Use this to keep your attendance reports accurate and to enforce no-show fees if you have them.
### Auto check-in at booking start
> Auto check-in at start time.
When on, the system automatically checks the member in at the booking start time without staff action. Useful for venues where staff don't physically scan members in (e.g. self-service court reservations).
### Allow anonymous bookings
> When enabled, admin can create bookings without selecting a contact.
Use this when admins need to book for someone who doesn't have a 1club profile yet - for example, a phone caller asking for a personal-training session before they've signed up. The booking is recorded without a contact link; you can attach a contact later if the person creates a profile.
This setting only affects what admins can do in the admin dashboard. It does not let members book anonymously from the public portal. Because only staff can book with no contact, this is one of the rules staff are still held to.
### Allow guest bookings
> When enabled, logged-out visitors can book without an existing account.
When this is on, a visitor who isn't logged in can book from your public portal, website, or network without creating a password first. They enter their email, verify it with a one-time code we send them, and complete the booking. After the booking is made, 1Club creates a passwordless member account for them and signs them in, so they can see and manage the booking.
If the email already belongs to an active member, the visitor is routed to log in instead. This is off by default - turn it on per policy when you want to let new visitors book before signing up. See [Member onboarding](/members/onboarding) for the full signup and verification experience.
## Scope
Use the **Scope** section to control which bookings the policy applies to.
* **Clubs** - Leave empty to apply to all clubs, or pick specific clubs.
* **Scope by type** - Pick one of *All types*, *Area type*, *Class type*, *Instructor type*, or *Plan*. Then narrow further by selecting a specific area type, class type, instructor type, or plan(s).
When more than one policy could apply to the same booking, the most specific scope wins. A policy scoped to a particular class type beats a policy scoped to a club, which beats an org-wide policy.
## Cancellation rules
Pick one of three **Cancellation terms** in the policy:
* **Free cancellation** - Members get a full refund whenever they cancel. This is the default for a new policy, and members see it stated as "Full refund at any time".
* **Refund by notice period** - The refund shrinks as the booking approaches. Add a rule per notice period.
* **Non-refundable** - Members get nothing back, however early they cancel.
### Refund by notice period
Add a rule for each notice period. For each rule:
* **Hours in Advance** - How many hours before the booking the member cancels.
* **Refund %** - What percentage of the booking price is refunded.
Refunds can only shrink as the booking gets closer, so a later cancellation can never be worth more than an earlier one.
Example - two rules, `24h / 100%` and `4h / 50%`:
* 24 hours or more in advance - full refund
* 4 to 24 hours in advance - 50% refund
* Less than 4 hours - no refund
The last band is added for you: anything inside your smallest notice period refunds nothing, so you never have to add a `0%` rule yourself.
### What members see
Once a member is booking a specific slot, the policy is shown as the **deadlines** for that booking rather than as notice periods, because a date needs no arithmetic:
* Full refund until Friday, Sep 11 at 1:00 PM
* 50% refund until Saturday, Sep 12 at 11:00 AM
* No refund after that
Deadlines are shown in the club's timezone. A band whose deadline has already passed is left out, so a member booking a court an hour before it starts is told plainly that there is no refund rather than being shown a tier they cannot reach. Where no specific booking is in context, the notice periods are shown instead.
Policies with no rules at all say so too: under **Free cancellation** a member reads "Full refund at any time" on the confirmation before they pay, and again when they open the cancellation.
### Non-refundable
Members can still cancel a non-refundable booking, and doing so releases the slot for someone else to book. They just get no money back.
Two things follow from the refund being zero, and it is worth telling members before they book:
* A membership session spent on the booking is **not** credited back.
* The instructor is still paid in full for the cancelled booking.
Members see a single line on the booking confirmation and in their account: "No refund on cancellation". No hours or percentages are shown, because there is no deadline to meet.
If you previously faked non-refundable bookings with a very large **Hours in Advance** value (such as 9999 hours at 100% refund), switch the policy to **Non-refundable** and save. The old setup refunds nothing in practice, but members reading the booking confirmation are told to cancel 9999 hours ahead for a full refund.
## Orders
The **Product categories offered with the bookings** field let you allow specific product categories on the booking confirmation page (e.g. drinks, towels, equipment rental). Members can add these to the same order.
## Worked examples
### Drop-in yoga studio
You want walk-ins to be welcome, and payment collected at the door.
* **Enforce payment** - At check-in
* **Allow anonymous bookings** - off
* **Check-in required** - off (members may walk in without a booking)
* **Allow walk-in check-ins** - on (reception can check in a walk-in who has no booking)
* **Allow anonymous check-ins** - on (a trial visitor with no member account can be checked in; an anonymous check-in can only be covered by direct payment or an external program)
The walk-in and anonymous check-in controls live on the booking policy itself (they replace the older Access Control "Flexible" check-in mode). See [Check-in models](/reference/check-in-models) for how the front desk uses them; per-club identification methods (QR, card, PIN) are still set under [Access control](/settings/access-control).
### Strict class-based gym
You sell classes online and the schedule is the source of truth.
* **Enforce payment** - At booking
* **Check-in required** - on (so unclaimed bookings auto-mark as no-shows)
* **Auto check-in at booking start** - off (staff scan members in)
* **Allow anonymous bookings** - off
### Personal training booked over the phone
You let admins book PT for callers who haven't signed up yet.
* Scope the policy to your *Personal Training* class type or instructor type.
* **Allow anonymous bookings** - on
* **Enforce payment** - your call (At check-in if you collect when the client arrives, At booking if you charge a card-on-file). Staff can always create the booking, even with **At booking** selected; the balance stays owed until you collect it.
## Tips & best practices
* **Start broad, narrow as needed** - One org-wide policy is easier to reason about than five overlapping ones. Add scoped policies only when a specific area or class needs different rules.
* **Mirror your front desk** - The settings should match what your staff actually do. If reception always collects payment at the door, set **Enforce payment** to **At check-in**.
* **Test with a real booking** - After saving a policy, create a test booking and walk through it end-to-end before going live.
* **Don't combine Auto check-in with Check-in required without thought** - Auto check-in defeats the no-show detection of Check-in required.
## Troubleshooting
**Issue**: Member can't book - system says payment required.
**Solution**: The matching policy has **Enforce payment** set to **At booking**. Either pay through the booking flow, or switch the policy to **At check-in** (or **None**) if upfront payment isn't your intent. Staff booking from the admin dashboard are not blocked by **At booking** - they can record the booking and collect the balance later.
**Issue**: A member can't check themselves in - payment blocked.
**Solution**: The policy has **Enforce payment** set to **At check-in** (or **At booking** with an outstanding balance), so the kiosk and the check-in app wait for payment. Collect it, confirm the member's active membership, or apply an external program. The front desk can also check the member in from the admin dashboard and leave the amount owed. See [Check-in Models](/reference/check-in-models) for entrance methods.
**Issue**: I want to require payment at booking *and* block check-in on overdue balances.
**Solution**: Set **Enforce payment** to **At booking**. Members can't check themselves in while a booking has unpaid charges. Staff checking someone in at the front desk see that the gym asks for payment and can still admit them, with the amount left owed.
**Issue**: Two policies seem to apply to the same booking
**Solution**: The more specific scope wins. Class-type-scoped beats club-scoped beats org-wide. Open both policies and check their **Scope** sections to confirm precedence.
**Issue**: A repeating booking is refused for being too far in advance, but its first session is next week.
**Solution**: **Max. ahead** measures the series' last occurrence, not its first. Shorten
the series (an earlier end date or fewer repeats), raise **Max. ahead**, or set it to
repeat with no end. Staff creating the series from the admin dashboard are not held to
**Max. ahead**.
**Issue**: Cancellations are giving full refunds when I expected partial
**Solution**: Check the **Cancellation terms** in the **Cancellation rules** section. A new policy starts on **Free cancellation**, which refunds in full. Switch to **Refund by notice period** to refund partially, or **Non-refundable** to refund nothing.
## Related
* [Check-in Models](/reference/check-in-models) - Pick the right model before configuring policies.
* [Access Control](/settings/access-control) - Per-club check-in mode and identification methods.
* [External Programs](/settings/external-programs) - Third-party entrance methods that satisfy payment-at-check-in.
* [Bookings](/schedule/bookings) - Day-to-day booking management.
* [Repeating schedules](/reference/repeating-schedules) - How a repeating series is created, and why **Max. ahead** measures its last occurrence.
* [Payments](/sales/payments) - How and when payments are recorded.
# Class types
Source: https://docs.1club.ai/settings/class-types
Reusable categories for classes that set the sport, age group, and level, the progression track sessions count toward, multi-day scheduling, auto-cancellation on zero attendance, party size, and which revenue account classes book to.
A class type is a reusable category you assign to [classes](/schedule/classes). It says what the class is - its sport, who it is for (the age group), and how hard it is (the difficulty) - and which [progression track](/settings/progression) its sessions count toward. It also sets rules that apply to every class of that type: whether the class can span multiple days, whether it auto-cancels when nobody books, how many seats one member can reserve at once, and which revenue account the class books its income to. Owners and managers set up class types, and members and instructors see their rules on every class of that type.
## When to use it
Class types let you set a rule once for a whole program instead of on every class. Group classes the way you run them - "Vinyasa", "Reformer Pilates", "BJJ Fundamentals", "Padel clinic" - and each new class inherits its type's behavior.
Typical uses:
* **Kids and adult classes side by side** - a tennis gym tags "Kids Tennis" as Kids and Beginner and "Tennis Squad" as Juniors and Intermediate. Parents see who each class is for on the schedule, and someone searching 1club.me for "kids tennis" finds the gym.
* **Belts that count the right classes** - a jiu-jitsu academy links "BJJ Fundamentals" and "Advanced BJJ" to its adult belt track, so a strength class no longer moves anyone toward their next belt.
* **A martial-arts school with program strands** - "BJJ Fundamentals", "Advanced BJJ", and "Kids Muay Thai", where an empty weekday session cancels itself 2 hours ahead and frees the coach.
* **Padel clinics that allow groups** - a member books a spot for themselves and three friends in one booking, and clinics get their own cancellation window through a booking policy.
* **Studio classes with their own revenue line** - "Reformer Pilates" books to its own revenue account, so equipment-class income is split from mat classes for tax and reporting, without changing how members book.
See [Worked examples](#worked-examples) for how to set up each of these.
## How class types are used
* **Creating a class** - you pick a class type in the class dialog; the type's rules then govern that class.
* **Party bookings** - the type's **Max party size** caps how many seats a member can book in one go (see [Classes](/schedule/classes#party-bookings)).
* **Booking policies** - scope a [booking policy](/settings/booking-policies) to a class type to give it its own payment and cancellation rules.
* **Auto-cancellation** - the zero-attendance rule runs on the type, so every class of that type inherits it.
* **Schedules and search** - the type's age group and difficulty show under each class on your schedules and can filter them, and they let people find your gym on 1club.me (see [Age group and difficulty](#age-group-and-difficulty)).
* **Progression** - the type's **Counts toward** track decides which sessions count toward a member's next level (see [Counting toward a progression track](#counting-toward-a-progression-track)).
## Managing class types
The list at **Settings** > **Clubs** > **Class Types** shows one row per type with its name, its sport, the track it counts toward, whether it allows multi-day, its description, and its max party size. Use **Add class type** to create one, the row menu to edit, and delete to remove a type that's no longer used.
### Fields
* **Name** - required. Shown when creating a class and on the class detail chip.
* **Description** - optional, for your own reference.
* **Sport** - optional. New classes of this type start with this sport, so you don't pick it on every class. A class can still be set to another sport.
* **Age group** - optional. Who the class is for: **Infants**, **Kids**, **Juniors**, **Adults**, **Seniors**, or **All ages**. See [Age group and difficulty](#age-group-and-difficulty).
* **Difficulty** - optional. **Beginner**, **Intermediate**, **Advanced**, or **All levels**.
* **Counts toward** - optional. The [progression track](/settings/progression) sessions of this type count toward. Leave it at **No track** if the class trains for no belt or level. See [Counting toward a progression track](#counting-toward-a-progression-track).
* **Allow multi-day** - when on, classes of this type can start on one day and end on another. Off by default.
* **Cancel if attendance is zero** - when on, a class of this type is automatically cancelled if it has no bookings by a set number of **hours in advance** of its start. Use it to avoid running empty sessions and to notify the assigned instructor early.
* **Max party size** - the most seats one member can reserve for a single class of this type, including themselves. Defaults to **1**, which turns [party bookings](/schedule/classes#party-bookings) off. Raise it to let members bring guests.
* **Revenue account** - the account class income books to. See [Charges](/sales/charges) for how revenue is recorded.
* **Default assessment template** - the [assessment template](/members/assessments) a score card on a class of this type starts on. Only active templates are offered; leave it at **None** to let the instructor pick. Shown once your organization has at least one template.
Names and descriptions can be translated per locale.
## Age group and difficulty
The age group and difficulty describe who a class is for. Members see them under each class name on your schedule - on 1club.me, in the member portal, and on your website - and on the **Book a session** list on your gym's 1club.me page, for example "Juniors · Intermediate". A label the class name already says is left out, so "Kids Tennis" shows only "Beginner" rather than "Kids · Beginner".
The age groups are fixed bands, the same ones the [Age smart tags](/settings/smart-tagging#age-disabled-by-default) start from:
| Age group | Ages |
| - | - |
| Infants | 0-4 |
| Kids | 5-12 |
| Juniors | 13-17 |
| Adults | 18-64 |
| Seniors | 65+ |
| All ages | Anyone |
They also work as filters:
* **On your schedule** - the **Filters** menu offers **Age group** and **Level** sections, listing only the values your classes carry.
* **On 1club.me search** - people can search for "kids tennis" or "advanced jiu jitsu", or pick an **Age group** and **Level** in the search filters. A gym matches only through an upcoming public class whose type has that age group or difficulty, and that is of the searched sport. A class type with no age group set doesn't match an age group search. Opening a gym from those results shows its schedule already narrowed to the matching classes.
* **For AI assistants** - the `search_clubs` tool on the [discovery MCP server](/mcp/tools#discovery-server) takes the same age group and level.
## Counting toward a progression track
**Counts toward** decides which sessions move a member toward their next level on a [progression track](/settings/progression#sessions-to-the-next-level).
* **A track with no class types linked counts every session** - a check-in or a completed booking of any kind. This is how every track starts.
* **Once any class type is linked, only sessions of the linked types count** toward that track. Link "BJJ Fundamentals" and "Advanced BJJ" to "Adult BJJ Belts", and a strength class or an open mat of another type stops counting toward a belt.
* **Linking or unlinking a type recounts everyone on the track.** Counts are worked out from attendance, not stored, so a member's "13 / 20 sessions" can change the moment you save. Check the counts before a grading if you have just changed the links.
* **Bookings that are not a class seat** - a private lesson or a court booking - don't count toward a track that has class types linked, because only a class has a type. Leave the track unlinked if 1:1 lessons should count.
Each track card under **Settings** > **Members** > **Progression Levels** shows which class types count toward it, or that all sessions count.
## Worked examples
### Kids and adult tennis
Create "Kids Tennis" with **Sport** tennis, **Age group** Kids, and **Difficulty** Beginner, and "Tennis Squad" with **Age group** Juniors and **Difficulty** Intermediate. Every class of those types shows its age group and level on your schedule, parents can filter the schedule to Kids, and your gym appears when someone searches 1club.me for "kids tennis".
### Belts that count only the classes that train for them
Enable or create an "Adult BJJ Belts" track, then set **Counts toward** to it on "BJJ Fundamentals" and "Advanced BJJ". Set "Kids BJJ" to count toward your kids belt track instead. Leave "Strength & Conditioning" at **No track**. Each student's session count now reflects only the classes that prepare them for their next belt.
### Martial arts program with belt levels
Create class types for each program strand - "BJJ Fundamentals", "Advanced BJJ", "Kids Muay Thai" - and set each one's **Age group** and **Difficulty**. Turn on **Cancel if attendance is zero** (say 2 hours in advance) so an empty weekday session cancels itself and frees the coach. Keep **Max party size** at 1 for adult classes.
### Padel clinics that allow groups
Create a "Padel clinic" type with **Max party size** set to 4 so a member can book a spot for themselves and three friends in one booking. Scope a [booking policy](/settings/booking-policies) to this type if clinics need a different cancellation window than open court time.
### Studio classes with their own revenue line
Give "Reformer Pilates" its own **Revenue account** so equipment-class income reports separately from mat classes, without changing how members book.
## Tips & best practices
* **Set up class types before creating classes.** The type controls the sport, party size, auto-cancel, and the revenue account, so classes inherit the right behavior from the start.
* **Set an age group on every type that has one.** An untagged type never matches an age group search on 1club.me, so a kids class with no age group is invisible to parents searching for one.
* **Link every type that trains for a track, or none.** Once one type counts toward a track, sessions of an unlinked type stop counting toward it.
* **Use auto-cancel to protect instructor time**, but set the hours-in-advance long enough that members still have a fair chance to book.
* **Match party size to the activity.** Court-based and social classes benefit from a higher party size; skill or safety-limited classes usually stay at 1.
* **Give distinct programs distinct revenue accounts** if your accounting needs class income split out.
## Troubleshooting
**Issue**: Members can't book more than one seat in a class.
**Solution**: The class type's **Max party size** is 1. Raise it on the type under **Settings** > **Clubs** > **Class Types**.
**Issue**: A class cancelled itself unexpectedly.
**Solution**: Its class type has **Cancel if attendance is zero** on, and no one had booked by the cutoff. Turn it off for that type, or lengthen the hours-in-advance.
**Issue**: My gym doesn't show up on 1club.me when someone searches for "kids tennis".
**Solution**: Set **Age group** to Kids on the class type, and check the gym has an upcoming class of that type that is public and set to tennis (the class's own sport, or the type's sport when the class has none).
**Issue**: A member's session count dropped after I edited a class type.
**Solution**: You linked a class type to their track under **Counts toward**. Only sessions of linked types count from then on. Link every class type that trains for that track, or set them all back to **No track** to count every session again.
**Issue**: I can't set a class to span two days.
**Solution**: Turn on **Allow multi-day** for its class type first.
## Related
* [Classes](/schedule/classes) - Where class types are assigned and party bookings happen.
* [Booking policies](/settings/booking-policies) - Scope payment and cancellation rules to a class type.
* [Progression tracks](/settings/progression) - The tracks a class type's sessions count toward.
* [Smart tagging](/settings/smart-tagging) - The Age tags use the same age bands as class types.
* [Charges](/sales/charges) - How the revenue account records class income.
# Email inboxes
Source: https://docs.1club.ai/settings/email-inboxes
Route inbound member emails into the 1Club Inbox by forwarding your club's address to a verified forwarding inbox.
An email inbox connects a club's real email address (for example `hello@yourclub.com`) to the 1Club **Inbox** so that messages members send to that address show up as inbound conversations your staff can read and reply to in one place. You set it up once, and members keep writing to the address they already know. You forward mail from your provider to a 1Club forwarding address, verify the inbox once, and from then on every inbound email threads into the [Inbox](/inbox/messages) alongside your outbound messages.
## When to use it
Add an inbox when members email your gym and you want those emails next to the rest of their conversation history, instead of in a separate mailbox only one person checks. Each inbound email is matched to a contact and threaded onto the conversation it replies to.
Typical uses:
* **Replies to booking confirmations** - a padel gym's members reply "can I move my court to 7pm?" to their confirmation, and the front desk answers from the Inbox, threaded under the original message.
* **One address per location** - a gym chain with three locations adds an inbox for each, so every email is attributed to the right location automatically.
* **Catching first-time enquiries** - a martial-arts school forwards its shared `info@` mailbox, and prospective students who write in are created as contacts so they can be followed up.
See [Worked examples](#worked-examples) for how to set up each of these. Sending email from your own domain is a separate setup, covered in [Email sending](/settings/email-sending).
You don't need an inbox for members' replies to messages you send from 1Club. Those carry their own reply address and land in the Inbox on their own (see [Replies to your messages](/settings/email-sending#replies-to-your-messages)). An inbox is for the mail members send to your gym's address themselves.
## Adding a club inbox
Inboxes live under **Settings** > **Communication** > **Email Inboxes**. Adding one takes three moves: create the inbox, forward your provider's mail to the address 1Club gives you, then verify.
1. Click **Add Inbox** in the top right.
2. Choose the **Club** this inbox belongs to.
3. Enter the **Email address** members already write to (for example `frontdesk@yourclub.com`). This is the address you own and want to capture mail from.
4. Click **Add**. 1Club creates the inbox and shows a green **Inbox created successfully!** panel containing the **forwarding address** to use in the next step.
The forwarding address is generated for you in the form `{organization-slug}_{club-slug}@inbox.1club.ai`. Copy it with the copy button - you will paste it into your email provider.
The email address you enter cannot be changed after the inbox is created (the field is read-only when editing). If you typed it wrong, delete the inbox and add a new one.
### Set up forwarding at your provider
In your email provider (Gmail, Outlook, or similar), set the club address to forward incoming mail to the 1Club forwarding address you just copied. The in-app setup instructions summarize the same three steps:
1. Create an inbox with your email address.
2. Copy the forwarding address and set up forwarding to it in your email provider (Gmail, Outlook, etc.).
3. 1Club sends a verification email to your address. Once forwarding works, it reaches 1Club by itself and the inbox turns **Verified**. You can also click its link.
**Gmail** won't forward until you confirm the forwarding address, and it sends that confirmation to the forwarding address itself. While an inbox is being set up (in its first week, or after **Resend Verification**), 1Club emails your gym's address Gmail's confirmation link and code, so you can finish in Gmail's forwarding settings.
**Microsoft 365** blocks automatic forwarding to outside addresses by default. Your administrator may need to allow it in the outbound spam policy before mail reaches 1Club.
## Domain verification
There are two distinct "verifications" in play. Keep them separate:
* **Inbox verification** activates inbound capture for one inbox. It is a single click on a link 1Club emails to the address you entered. Covered below.
* **Sending-domain verification** lets 1Club send outbound email and replies *from* your own domain. It is a DNS setup under **Settings** > **Organization** > **Domains**, covered in full in [Email sending](/settings/email-sending).
### Verify the inbox (activate inbound capture)
When you create an inbox (or resend verification), 1Club emails a verification message to the address you entered. It shows the forwarding address to set up. Either of two things marks the inbox **Verified** and activates inbound capture:
* **The email comes back through your forwarding.** Once forwarding works, the verification email reaches 1Club by itself. This also proves forwarding is set up correctly, so it's the better check.
* **You click its link.**
Notes:
* The link is valid for 7 days. If it lapses, use **Resend Verification** to get a fresh one.
* **Resend Verification** doubles as a forwarding test: if forwarding works, the new email verifies the inbox on its own.
* Until an inbox is verified, inbound mail to its forwarding address is dropped - the webhook only accepts mail for a verified inbox.
* An unverified inbox shows an **Unverified** status chip (amber). A verified inbox shows **Verified** (green).
To resend: open the inbox row's actions menu and choose **Resend Verification**, or open the inbox and click **Resend Verification** in its status panel. Confirm the prompt and a new link is sent to the inbox address.
### Verify your sending domain (optional, recommended)
Verifying the domain your club emails come from means outbound messages and replies leave as `you@yourclub.com` instead of a platform address, which improves deliverability and trust. Without it, 1Club still sends, but falls back to the platform sending address and sets your club address as reply-to.
The full procedure - DNS records, which address becomes the **From**, and how the organization and club Email fields interact - lives in [Email sending](/settings/email-sending).
The inbox forwarding address always uses the platform's `inbox.1club.ai` domain and does not require any DNS setup from you. Sending-domain verification only affects outbound mail, not inbound capture.
## How inbound email threads into the Inbox
When a member emails your club address and your provider forwards it to the 1Club forwarding address, 1Club receives it through a SendGrid Inbound Parse webhook and does the following:
1. **Finds the inbox** by the address the email was actually delivered to (your forwarding address), not by its To: line, which a forward leaves as your gym's own address. If no verified inbox matches, the mail is ignored.
2. **Skips mail that isn't a member writing in**: out-of-office auto-replies, mail from your gym's own sending address, mail from your staff's sign-in addresses (an internal note to the shared mailbox, for example), and an email it has already filed.
3. **Resolves the sender** by email. If no contact exists yet, a new contact is created from the sender's name and email. An existing contact keeps the name you gave them.
4. **Creates an inbound message** with `direction: inbound`, tagged to the inbox, storing the original club address as the recipient.
5. **Threads replies** when the incoming mail carries `In-Reply-To` / `References` headers that match an earlier email, or when the subject is a `Re:` of the same sender's earlier email. Otherwise it starts a new conversation.
Staff are alerted about new inbound messages according to their [Notifications](/settings/notifications) settings.
The result appears in the [Inbox](/inbox/messages) as an inbound conversation you can open and reply to. Replies you send from the Inbox go out from the inbox's own address; see [Email sending](/settings/email-sending) for how that address is chosen and when it needs a verified domain.
Inbound messages are attributed to a staff user internally (the owner, or the earliest-added admin, manager, front-desk, or instructor). This is a system detail; in the Inbox the conversation is shown against the member contact who wrote in.
## Viewing captured messages
The inbox list shows a **Messages** count per inbox. Day-to-day, you read and answer captured mail in the [Inbox](/inbox/messages) rather than on this settings page - this page is for setting inboxes up and checking their status.
## Worked examples
### Padel club front desk
Your racket-sports club publishes `bookings@acepadel.com` on its website and members reply to booking confirmations there.
* Add an inbox for the **Ace Padel** club with email `bookings@acepadel.com`.
* Forward `bookings@acepadel.com` to the generated `acepadel_downtown@inbox.1club.ai`.
* Verify the inbox. Now a member replying "can I move my court to 7pm?" lands in the Inbox threaded under the original confirmation, and the front desk answers without leaving 1Club.
### Multi-location gym chain
Your fitness organization runs three clubs, each with its own address.
* Add one inbox per club (`northside@`, `harbor@`, `airport@`), each scoped to its club.
* Each gets its own forwarding address, so mail is attributed to the right location automatically.
* Verify your `yourgym.com` sending domain once under **Settings** > **Organization** > **Domains** so replies from every club go out branded.
### Martial-arts school with one shared mailbox
Your jiu-jitsu and muay-thai academy runs everything through `info@northgatemma.com`.
* Add a single inbox for the academy's club with that address.
* Forward `info@` to the forwarding address and verify.
* New senders who aren't members yet are auto-created as contacts, so first-time enquiries from prospective students still land in the Inbox and can be followed up.
## Tips & best practices
* **Verify before you rely on it.** Inbound mail is silently dropped for unverified inboxes. Let the verification email come back through your forwarding rather than clicking its link, and the **Verified** chip proves forwarding works. Then send a test email to the club address from an outside account and confirm it appears in the Inbox.
* **One inbox per real mailbox.** Don't point two clubs' forwarding at the same source address; capture is keyed on the forwarding address per club.
* **Pair inbox capture with a verified sending domain.** Members reading a reply from your own domain trust it more, and deliverability is better. See [Email sending](/settings/email-sending).
* **Keep the club address stable.** Because the address can't be edited, choose the mailbox you intend to keep before creating the inbox.
## Troubleshooting
**Issue**: Emails sent to the club address don't appear in the Inbox.
**Cause / fix**: The inbox is not verified, or forwarding isn't actually enabled at your provider. Confirm the status chip reads **Verified**; if not, use **Resend Verification** and click the link. Then re-check that your provider is forwarding to the exact `..._...@inbox.1club.ai` address (copy it again to be sure).
**Issue**: The verification link says it is invalid or expired.
**Cause / fix**: Verification links last 7 days. Open the inbox and click **Resend Verification** (or use the row action) to send a fresh one. If forwarding already works, the new email verifies the inbox without a click.
**Issue**: Gmail won't enable forwarding until I confirm a code sent to the forwarding address.
**Cause / fix**: While the inbox is being set up, 1Club emails your gym's address Gmail's confirmation link and code. If it's more than a week since you added the inbox, click **Resend Verification** first, then turn forwarding on in Gmail again.
**Issue**: Mail stops at Microsoft 365 with a "forwarding is disabled" bounce.
**Cause / fix**: Microsoft 365 blocks automatic forwarding to external addresses by default. Ask your administrator to allow it for this mailbox in the outbound spam filter policy.
**Issue**: Replies I send from the Inbox come from a platform address, not my club's.
**Cause / fix**: Your sending domain isn't verified. Add it under **Settings** > **Organization** > **Domains** with purpose **Email (SendGrid)**, add the DNS records, and click **Verify**. Until the domain is verified, your club address is used as reply-to only. See [Email sending](/settings/email-sending).
**Issue**: An inbound email created a duplicate-looking contact.
**Cause / fix**: The sender wrote from an email 1Club hadn't seen, so a new contact was created. Merge or update the contact from the member's profile if it duplicates an existing record with a different address.
## Related
* [Email sending](/settings/email-sending) - The outbound counterpart: sending domain and the From address members see.
* [Inbox](/inbox/messages) - Where captured inbound mail lands and where you reply.
* [Notifications](/settings/notifications) - Route new-message alerts to the right staff.
* [Message templates](/settings/templates) - Reusable content for replies and sends.
* [Integrations](/settings/integrations) - Other external connections for your organization.
# Email sending
Source: https://docs.1club.ai/settings/email-sending
Control what members see in the From line: verify your email domain, then set the organization and club email addresses 1Club sends from.
Every email 1Club sends on your behalf - booking confirmations, invoices, reminders, automations, and replies from the Inbox - carries a **From** name and address. Three settings decide what that looks like: a verified **email domain**, your **organization Email**, and each **club Email**. Get all three right and members see mail from your own gym. Miss the domain and they see a 1Club address instead, with your address only as reply-to. An owner or admin sets this up once, and members see the result in every email they receive.
This page covers outbound mail. For capturing mail members send *to* you, see [Email inboxes](/settings/email-inboxes).
## When to use it
Set this up so members recognize every email as coming from your gym. Replies reach you either way, but a verified domain changes trust and deliverability, and a club Email per location lets each one send from its own address.
Typical uses:
* **A single-location gym on its own domain** - a padel gym verifies its domain so booking confirmations come from its bookings address and invoices from its main address.
* **One sender per location** - a gym chain verifies its domain once, gives each location its own club Email, and sends invoices from a separate billing address.
* **Fixing mail that arrives from a 1Club address** - a martial-arts school finds one location still on an old Gmail address and moves it onto the verified domain.
* **Unlocking sending on a trial** - a new organization on a trial verifies its domain before it can send messages or run automations.
See [Worked examples](#worked-examples) for the exact settings behind the first three, and [Trial sending](#trial-sending) for the last.
## What members actually see
A delivered email's From line is built as `Display name `. Which address lands there depends on whether the domain is verified:
| Domain verified? | From | Reply-to |
| - | - | - |
| Yes | Your address (`frontdesk@yourgym.com`) | Your address |
| No | 1Club's platform address | Your address |
| No address set at all | 1Club's platform address, named `1club` | Not set |
In all three cases replies still reach you. Verification changes trust and deliverability, not routing.
## The two Email fields
There are two places to type an email address, and they are not interchangeable.
### Settings > Organization > Details > Email
The organization-level address. It is used for mail that is not tied to a single location, and it is also surfaced as your public contact address on the marketplace listing. See [Organization](/settings/organization).
### Settings > Clubs > (each club) > Email
The per-location address, set on each gym alongside its phone and website. **This is what members see on most of the email they receive**, because the highest-volume mail (booking confirmations, cancellations, reschedules, order confirmations, daily summaries) is club-scoped.
A club Email always wins for club-scoped mail, even when its domain is **not** verified and your organization Email's domain **is**. 1Club does not fall back to the organization address in that case. The result is mail sent from the 1Club platform address with the club address as reply-to, despite a perfectly good verified domain being available. Keep every club Email on a domain you have verified.
### Which address is used for what
| Email | Address used |
| - | - |
| Booking confirmation, cancellation, reschedule | Club Email |
| Order confirmation | Club Email |
| Daily summary | Club Email |
| Instructor calendar | Club Email |
| Invoices and proformas | Organization-scoped (see below) |
| Payment and renewal notices | Organization-scoped |
| Welcome email, form confirmations, waitlist | Organization-scoped |
| Sign-in, verification, invitations, password resets | Organization-scoped |
| Inbox replies and campaigns | The inbox's own address, or the club in scope |
"Organization-scoped" does not simply mean the organization Email. See the next section.
### Replies to your messages
Messages you send to members from the [Inbox](/inbox/messages) or an automation carry **two** reply-to addresses, both shown with your gym's name:
* **Your gym's address**, chosen exactly as in the table above.
* **A personal 1Club reply address** for that member and message (`reply+…@inbox.1club.ai`).
When a member hits **Reply**, their answer goes to both. Your mailbox gets it as it always has, and 1Club files it in the Inbox under the conversation it answers, so your team sees it next to the message it replies to. You don't need an [email inbox](/settings/email-inboxes) for this.
* Staff are alerted about the reply **in the app**, not by email, because your mailbox already has it.
* Out-of-office and other automatic answers are dropped, so a campaign doesn't fill the Inbox with them.
* If a colleague answers the member's reply with **Reply all**, their answer isn't filed as the member writing in.
* A message sent on behalf of a specific person, such as a review request sent from an instructor, keeps that person's reply-to.
* Booking confirmations, invoices, and other system emails keep a single reply-to: your gym's address.
## How 1Club picks the From address
1. **Club-scoped mail** uses that club's **Email** if one is set. There is no fallback to the organization Email if it is missing verification; the club address is taken as-is. If the club has no Email, resolution continues as if the mail were organization-scoped.
2. **Organization-scoped mail** builds a candidate list of every club Email plus the organization Email, then picks **the first candidate whose domain is verified**. If none is verified, it falls back to the first candidate in the list, which is a club address rather than your organization Email.
3. **The verified check** then decides placement. If the resolved address sits on a verified email domain, it becomes the **From**. If not, 1Club sends from its platform address and puts your resolved address in **reply-to**.
4. **The display name** is the club's name when the resolved address belongs to a club, otherwise your organization name, otherwise `1club`.
Step 2 means an unverified club address can be chosen ahead of your organization Email for organization-wide mail such as invoices. Verifying the domain your addresses share is what makes this deterministic.
## Verify your email domain
Verification is per **domain**, not per address. Once `yourgym.com` is verified, any address on it (`hello@`, `billing@`, `northside@`) can be used as a From with no further setup.
1. Go to **Settings** > **Organization** > **Domains**.
2. Enter your domain (for example `yourgym.com`) and set **Purpose** to **Email (SendGrid)**. The domain is created as soon as you finish typing it, so there is no separate **Add** button for email domains.
3. 1Club provisions the domain and shows **DNS records to add** - three CNAME records. One is the return-path record on an `em` subdomain, the other two are DKIM signing keys (`s1._domainkey` and `s2._domainkey`).
4. Add all three records at your DNS provider.
5. Wait for DNS to propagate, usually 5 to 30 minutes, then click **Verify**.
The status moves from **Pending verification** to **Verified**. If the records are not found yet, the verify fails with an error and the status stays pending; add the missing records or wait longer and try again.
The record names 1Club shows are fully qualified (`em1234.yourgym.com`). Many registrars want only the relative part in the **Host** field (`em1234`), and append the domain themselves. If your provider does that, strip the trailing `.yourgym.com` before saving, or you will create `em1234.yourgym.com.yourgym.com`.
### What verification does and does not cover
* **Subdomains are not covered.** A verified `yourgym.com` does not authorize `hello@mail.yourgym.com`. Add the subdomain as its own email domain if you want to send from it.
* **SPF and DMARC are not managed by 1Club.** The three CNAMEs set up DKIM signing and the return path. If you publish your own SPF or DMARC policy, maintain it yourself at your DNS provider.
* **A domain belongs to one organization.** If another 1Club organization has already connected the same domain for email, adding it returns a conflict.
* **Deleting a domain removes it at SendGrid too.** Your DNS records are left behind and become inert. Re-adding the domain mints a *new* `em` hostname, so you must replace the old CNAMEs rather than reuse them.
* **Changing an address does not need re-verification.** Swapping `hello@yourgym.com` for `frontdesk@yourgym.com` works immediately. Swapping to a different domain silently drops you back to the platform From until you verify that domain too.
## Trial sending
Organizations on a trial must have a verified email domain before they can send messages or run automations. Without one, sending is blocked and the admin shows a **Verify your domain to unlock this feature** dialog. Paid plans are never subject to this. See [Platform subscription](/settings/platform-subscription).
## Worked examples
### Padel club with a single location
A racket-sports gym runs one location on `acepadel.com`.
* Verify `acepadel.com` under **Organization** > **Domains** with purpose **Email (SendGrid)**.
* Set **Organization** > **Details** > **Email** to `hello@acepadel.com`.
* Set the club's **Email** to `bookings@acepadel.com`.
Booking confirmations now arrive from `Ace Padel ` and invoices from `Ace Padel `.
### Multi-location gym chain
A fitness organization runs three gyms on one domain.
* Verify `yourgym.com` once. It covers every address on the domain.
* Set each club's **Email** to `northside@`, `harbor@`, and `airport@yourgym.com`.
* Set the organization **Email** to `billing@yourgym.com`.
Members booking at Harbor see `Harbor `; the monthly invoice run goes out from `Your Gym `.
### Martial arts school that forgot one club
A jiu-jitsu academy verified `northgatemma.com` and set the organization Email correctly, but one club still has an old `northgate.mma@gmail.com` address.
* Mail for that location sends from the 1Club platform address with the Gmail address as reply-to, because `gmail.com` is not a verified domain.
* Worse, because that club's address is a candidate for organization-wide mail too, it can be picked for invoices if it happens to come first.
* The fix is to change that club's **Email** to an address on `northgatemma.com`. No re-verification is needed.
## Tips and best practices
* **Verify the domain before you fill in any address.** The addresses are inert branding until a verified domain backs them.
* **Keep every club Email on the verified domain.** One stray address on a free mail provider degrades that location's mail and can leak into organization-wide sends.
* **Set the organization Email even if every club has one.** It is the last candidate in the list and the sensible home for billing mail.
* **Send yourself a test booking confirmation after any change.** The From line is the only reliable confirmation that resolution went the way you expected.
* **Do not delete a verified domain to "re-do" it.** You will get a new `em` hostname and have to redo the DNS. Fix the address instead.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| Mail arrives from a 1Club address, not ours | The resolved address sits on an unverified domain | Verify the domain under **Organization** > **Domains**, purpose **Email (SendGrid)** |
| Only *some* mail is branded correctly | A club Email is on a different or unverified domain than the organization Email | Check every club's **Email** under **Settings** > **Clubs** and move them onto the verified domain |
| Invoices come from a club address, not the organization | Organization-scoped mail picks the first verified candidate, and club addresses come first | Verify the domain so the choice is deterministic, or align all addresses on one domain |
| Verify keeps failing | One or more of the three CNAMEs is missing, mistyped, or not propagated | Re-check all three records; watch for a doubled domain suffix in the Host field |
| Verification does not say which record is wrong | 1Club reports only pass or fail for the domain as a whole | Compare all three records side by side against the DNS instructions panel |
| Mail to a member using Apple's Hide My Email is unbranded | Apple's relay only forwards from domains registered with Apple, so 1Club sends from its platform address | Expected. Replies still reach you. See [Signing in](/reference/sign-in) |
| Sending is blocked with an upgrade dialog | You are on a trial without a verified email domain | Verify a domain, or move to a paid plan |
| Adding the domain returns a conflict | Another 1Club organization already connected it for email | Contact support to release it |
| The display name shows `1club` | No club or organization Email resolved | Set **Organization** > **Details** > **Email**, and an **Email** on each club |
## Related
* [Email inboxes](/settings/email-inboxes) - The inbound counterpart: capture mail members send to your gym.
* [Organization](/settings/organization) - Where the organization Email, clubs, and the Domains page live.
* [Organization and clubs](/reference/organization-clubs) - Which settings belong at which scope.
* [Inbox](/inbox/messages) - Sending and replying to members.
* [Platform subscription](/settings/platform-subscription) - Trial restrictions on sending.
* [Signing in](/reference/sign-in) - Apple Hide My Email and how it affects the sender.
# Export
Source: https://docs.1club.ai/settings/export
Download your organization data as CSV, or prep your books for QuickBooks Online.
Export downloads your organization's data as CSV files: contacts, bookings, memberships, invoices, and payments. There's also a QuickBooks bundle that packages customers, invoices, and payments into one ZIP for handing off to your accountant. Only admins can run exports.
## When to use it
Use an export when your data needs to leave 1Club - for your accountant, a spreadsheet, or another reporting tool. You pick one dataset and a date range, preview exactly what you'll get, and download a file that opens cleanly in Excel, Numbers, or Google Sheets.
Typical uses:
* **Handing the period to your accountant** - a yoga studio downloads the QuickBooks bundle for the quarter, so the accountant can reconcile customers and import invoices into QuickBooks Online.
* **Reconciling a month** - a padel gym exports bookings and payments for the same month and cross-checks them side by side.
* **Attendance analysis outside 1Club** - a gym exports bookings by class start time to analyze attendance or reconcile against third-party reporting.
## What you can export
Everyone in your contact list: members, leads, staff, and one-off check-ins. Includes name, contact details, address, type, tags, date of birth, and audit timestamps.
Class and area bookings. Date filter is by **class start time**. Useful for attendance analysis or reconciling with third-party reporting.
Active and historical memberships, with plan, billing frequency, start/end dates, and status. Date filter is by **membership start date**.
Invoices in a QuickBooks-friendly column layout (one row per invoice line item). Date filter is by **invoice date**.
Customer payments with payment method, amount, and linked invoice number where available. Date filter is by **payment date**.
A ZIP containing `customers.csv`, `invoices.csv`, `payments-reference.csv`, and a `README.txt` explaining how to bring the data into QuickBooks Online.
## Running an export
The export wizard walks you through four steps: **Select data**, **Filters** (date range), **Preview** (the row count and a 10-row sample), and **Download**. Each export is scoped to your current organization and to the role of the person running it.
1. Open **Settings** > **Data Tools** > **Export**.
2. Choose what you want to export (one entity per run).
3. Click **Next** and set a **From** and **To** date if you want to narrow the range. Leave both blank to export everything.
4. Click **Next** to generate the preview. You'll see the total row count, the list of columns, and the first 10 rows.
5. Click **Download**. The file is sent straight to your browser; nothing is stored on the server afterwards.
The preview is the same query as the download (just capped at 10 rows), so the row count you see is exactly what you'll get in the file.
## Date range behavior
Each entity uses a different date column for filtering. The table below shows what the **From / To** filter actually checks:
| Export | Date filter is applied to |
| - | - |
| Contacts | Contact creation date |
| Bookings | Class start time |
| Memberships | Membership start date |
| Invoices | Invoice date |
| Payments | Payment date |
| Accounting export | Invoice date for `invoices.csv`, payment date for `payments-reference.csv`, and "any invoice or payment in range" for `customers.csv` |
For the accounting export, an existing member whose contact record was created years ago will still show up in `customers.csv` if they had an invoice or payment in the selected window. That's deliberate: your accountant needs to see every customer who transacted in the period, not just those who joined in it.
## File format
All exports are UTF-8 CSV with a byte-order mark (BOM), so Excel and Numbers will open them with the correct encoding automatically.
* **Dates** are formatted as `YYYY-MM-DD` (UTC).
* **Timestamps** are full ISO 8601 in UTC.
* **Money values** have two decimal places, no currency symbol or thousands separator.
* **Cells that would trigger Excel formulas** (those starting with `=`, `+`, `-`, or `@`) are prefixed with a single quote so they import as text. This is a security measure to prevent malicious values in your data from running formulas in your spreadsheet.
## The accounting export (QuickBooks)
The QuickBooks option produces `quickbooks-export.zip`, intended as **preparation** for QuickBooks Online, not a one-click import.
### What's in the ZIP
* **`README.txt`** - Step-by-step instructions for using the bundle with QBO, plus caveats about file-size limits and sales tax.
* **`customers.csv`** - Your contacts who had at least one invoice or payment in the selected date range. The **Display Name** column is the canonical join key (see below).
* **`invoices.csv`** - One row per invoice line item, in QBO's invoice import format: `InvoiceNo`, `Customer`, `InvoiceDate`, `DueDate`, `ItemProductService`, `ItemDescription`, `ItemQuantity`, `ItemRate`, `ItemAmount`, `ItemTaxCode`, `ItemTaxAmount`, `Currency`.
* **`payments-reference.csv`** - Customer payments for the period. **Reference and audit only** - QuickBooks Online does not natively CSV-import customer payments applied to invoices.
### Recommended workflow
1. Open `customers.csv` and reconcile the **Display Name** values against existing customers in QBO. Create or update customers as needed so the names match exactly.
2. Open `invoices.csv` and confirm every `Customer` value matches a Display Name in QBO. If not, QBO will create duplicate customers on import.
3. In QBO, go to **Settings** > **Import data** > **Invoices** and import `invoices.csv`. If you have more than 100 invoices, you'll need to split the file (see "QBO limits" below).
4. Use `payments-reference.csv` to verify the paid/open status of each invoice after import, or feed it into a bridge tool that supports payment imports.
### QBO limits to know
* **100 invoices per file / 1,000 rows per file** - QuickBooks Online's native invoice CSV import enforces this. If your `invoices.csv` exceeds either, either split the file before importing, or use a bridge tool such as SaasAnt Transactions that doesn't enforce the per-file limit.
* **Sales tax** - The `ItemTaxCode` column is filled from your 1Club tax rate name. QBO's CSV tax import has limitations and varies by region. Test in a QBO sandbox before importing to your production books.
* **Payments are reference-only** - As above, payments don't import natively. The file is there for audit, reconciliation, or feeding a bridge tool.
### The Display Name join key
`customers.csv`, `invoices.csv`, and `payments-reference.csv` all use the same `Display Name` / `Customer` value for the same contact - that's what lets QBO match invoice rows to customers after import. If you change the Display Name in QBO but not in the CSV (or vice versa) before importing invoices, QBO will create duplicate customers.
## Limits
* **500,000 rows per export.** If your filtered result exceeds this, the download is rejected with a message showing the actual count. Narrow the date range and try again. For the accounting export, the limit is applied to customers + invoices + payments combined.
* **One entity per run.** To export both contacts and bookings, run the wizard twice.
* **No saved exports.** Each run is independent; there's no history of past exports on the server.
## Access and permissions
Running exports requires the **admin** role. Managers, instructors, staff, and members cannot access the Export page or the underlying download endpoints.
If you need to give someone outside the admin role access to a recurring export, the current workaround is to run the export yourself and share the file via your usual channel (email, shared drive). Programmatic access via the public API for non-admin exports is not currently supported.
## Tips & best practices
* **Always preview first.** The row count tells you whether your filters are right before you commit to a download you may not need.
* **Use the same date range across multiple exports** when you're reconciling. For example, exporting bookings and payments for the same month makes them easy to cross-check.
* **Open CSVs in Numbers or Google Sheets** if Excel mangles dates. The UTF-8 BOM should prevent encoding issues, but date parsing varies by locale.
* **For accounting exports, run in a QBO sandbox first.** Especially the first time, to confirm Display Names and tax codes behave as expected.
## Troubleshooting
**Issue**: The download is rejected with "Export exceeds the 500,000 row limit."
**Solution**: Narrow the **From** and **To** date range. The cap is per-export, so splitting a year into quarters (or smaller) is the normal workaround for large tenants.
**Issue**: The preview shows zero rows.
**Solution**: Check your date range. The filter is applied to the entity's natural date column (see [Date range behavior](#date-range-behavior)) - exporting Bookings with a date range that's before any class started will return nothing.
**Issue**: QuickBooks imports created duplicate customers.
**Solution**: The Display Name in `invoices.csv` did not match the customer name in QBO. Open `customers.csv`, reconcile the Display Name values against QBO first, then re-import.
**Issue**: Tax amounts look wrong in QBO after importing invoices.
**Solution**: QBO's CSV tax import has region-specific behavior. The export fills `ItemTaxCode` from your 1Club tax rate name; QBO needs a matching tax code on its side. Verify in a QBO sandbox before doing this on your production books.
**Issue**: Excel shows a cell starting with a single quote, like `'=SUM(...)`.
**Solution**: That's intentional. The export prefixes cells that look like spreadsheet formulas with a single quote to prevent formula injection. The quote is displayed in the formula bar but won't appear when the cell is printed or copied as a value.
## Related
* [Import](/settings/overview) - The mirror flow for bringing data into 1Club.
* [Billing overview](/sales/overview) - How invoices and payments work in 1Club, useful context when reading the exported files.
* [Invoices](/sales/invoices) - Invoice fields and statuses you'll see in `invoices-export.csv`.
* [Payments](/sales/payments) - Payment records and how they link to invoices.
# External programs
Source: https://docs.1club.ai/settings/external-programs
Add third-party programs and corporate-wellness benefits as a check-in entrance method, and let them influence booking prices.
An external program is a third-party offering - a partner curriculum, a corporate-wellness benefit, a federation pass, an aggregator like a city-wide multi-gym pass - that a member can use as their entrance method at check-in instead of one of your own memberships, optionally with an extra per-visit fee. You define programs once, and they surface for the front desk at check-in and in dynamic booking pricing.
## When to use it
Use external programs when something other than your own memberships gets a member through the door. Each visit is logged against the program, so you can report on usage and reconcile with the partner, optionally with a per-visit price.
Typical uses:
* **An aggregator pass at a padel gym** - a regional or multi-gym pass sends players to your courts, and the front desk picks the pass at check-in, which records the visit and its per-visit price.
* **Corporate wellness at a yoga studio** - a local employer's benefit covers its staff's yoga classes, and you track which visits arrived through it while the employer pays you separately.
* **Reciprocal access at a climbing gym** - members of a partner federation are waved in through **Direct check-in** with no fee.
* **Partner studio drop-ins** - a sister gym's members use your facility under a partnership.
See [Worked examples](#worked-examples) for how to set up the first three.
## Where external programs fit
A program behaves in one of two ways depending on whether you give it a scope under **Valid for**:
* **Scoped programs** (you pick Classes, Areas, and/or Instructors) become a selectable **entrance method** when a member checks in against a matching booked class, area, or instructor - alongside **Membership** and **Direct payment**. Only **Public** and **Member only** programs appear here, and any **Additional price per visit** is charged.
* **Generic programs** (no scope) can't grant access to a specific booking. Instead they do two things: they appear in the front-desk **Direct check-in** flow (when they carry no per-visit fee), and they factor into **dynamic booking pricing**. This lets a partner or corporate program adjust prices without being tied to one class or area.
You don't have to choose a scope at save time - a program with no scope is a valid, first-class generic program.
## Managing external programs
You define programs in the **External Programs** section of **Settings** > **Billing** > **General**.
### Viewing all programs
1. Go to **Settings** > **Billing** > **General** and scroll to **External Programs**.
2. The grid shows **Name**, **Code**, **Club** (or "All clubs"), **Additional price per visit**, **Visibility**, and **Status** (active or inactive).
3. Click a row to edit, or click **Add external program** to create a new one.
### Creating an external program
1. Click **Add external program**.
2. Fill in the **General** section (see the fields below).
3. Optionally set **Valid for** to scope the program to specific class, area, or instructor types.
4. Optionally set **Operating hours** to restrict when the program is usable.
5. Click **Save**.
### Fields
| Field | What it does |
| - | - |
| **Name** | The display name staff and members see (for example, "Corporate wellness"). |
| **Code** | A short identifier used in reports and partner reconciliation. Lowercase letters, numbers, hyphens, and underscores only. The system auto-fills it from the name as you type; edit it any time before you rely on it. Codes must be unique per club. |
| **Club** | Optional. Leave empty to apply organization-wide, or pick one club to scope the program to a single location. |
| **Additional price per visit** | Optional amount charged each time the program is used as a paid entrance method at a booked resource. Leave at 0 for free access. |
| **Visibility** | **Public**, **Member only**, or **Private**. Controls where the program is exposed on member-facing surfaces (see below). |
| **Active** | Toggle off to disable a program without deleting it. |
### Visibility
* **Public** - visible to everyone, including non-members browsing your portal.
* **Member only** - visible to logged-in members.
* **Private** - hidden from member-facing surfaces; for programs your staff manage manually.
Visibility also gates the entrance-method picker: only **Public** and **Member only** programs are offered when a member checks in against a booked class, area, or instructor. **Private** programs do not appear there.
### Valid for
The **Valid for** section scopes the program to specific entry points. You can select any combination of:
* **Classes** - the program covers check-ins for the class types you list (or all class types if you list none).
* **Areas** - the program covers check-ins for the area types you list.
* **Instructors** - the program covers check-ins with the instructor types you list.
Leaving **Valid for** empty makes the program **generic** (see [Where external programs fit](#where-external-programs-fit)). A generic program can't grant access to a booked resource, but it drives dynamic pricing and the Direct check-in flow.
### Operating hours
Set day-of-week hours during which the program can be used. Outside those hours, a scoped program won't appear in the entrance-method picker. The editor starts from a default schedule that mirrors your club's operating hours - override individual days only when the program's availability differs.
## Using an external program at check-in
There are two paths, matching the two program shapes:
**As an entrance method for a booked resource**
1. When the front desk records a check-in against a class, area, or instructor, they choose the entrance method.
2. **External program** appears as an option when at least one scoped program is active, in scope for that resource, within its operating hours, and visible (**Public** or **Member only**).
3. The staff member picks the program.
4. If the program has an **Additional price per visit**, that amount is charged through the same payment pipeline as a direct-payment check-in, and the visit is recorded against the program.
**As a Direct check-in (no booking)**
At small gyms where staff just wave a member in without a class or area, the **Direct check-in** flow lists the member's eligible memberships and any **generic** external programs that carry no per-visit fee. Pick the program to log the visit against it.
Either way, the visit is logged against the program so you can report on how many times it was used.
## Worked examples
### Aggregator pass at a padel club
A multi-gym pass provider sends players to your padel courts and reimburses you per visit.
* **Name**: Multi-gym pass. **Code**: `multigym-pass`.
* **Additional price per visit**: your reimbursement rate, so revenue reports stay accurate even though the player doesn't pay you directly.
* **Valid for**: Areas, narrowed to your padel area type.
* **Visibility**: Member only, so the pass shows for logged-in players but not the public site.
When a pass holder books a court and checks in, the front desk selects **External program** > **Multi-gym pass** and the per-visit price is recorded.
### Corporate wellness for a yoga studio
A nearby employer covers unlimited yoga for its staff.
* **Name**: Acme wellness. **Code**: `acme-wellness`. **Visibility**: Private (staff manage enrolment manually).
* **Additional price per visit**: 0, since the employer pays you on a separate monthly invoice.
* **Valid for**: Classes, narrowed to your yoga class types.
* **Operating hours**: weekdays only, if the benefit excludes weekends.
### Federation reciprocal access at a climbing gym
Members of a partner climbing federation get reciprocal day access.
* Leave **Valid for** empty to make it a **generic** program, so any federation member can be waved in through **Direct check-in** with no fee.
* Keep **Additional price per visit** at 0 so the program stays eligible for Direct check-in.
## Tips & best practices
* **Match codes to your partner's naming.** If the aggregator calls it `summer-pass-2026`, use that as the **Code** so finance reports line up with the partner's statements.
* **Record a per-visit price when the partner reimburses you.** Even when the member pays nothing at the door, capturing the visit price gives you an accurate revenue picture.
* **Scope tightly with Valid for.** A corporate program that only covers group classes should list only Classes, not Areas - this keeps the entrance-method picker clean.
* **Keep Direct check-in programs free.** A generic program with a per-visit fee is excluded from the Direct check-in list, because that flow only offers no-cost entries.
* **Use Private for staff-managed programs.** It keeps programs that members can never self-select out of member-facing surfaces.
## Troubleshooting
**A scoped external program doesn't appear at check-in**
Check, in order: the **Active** toggle is on; the program's **Club** matches the club where the check-in happens (or is empty); the class, area, or instructor matches the program's **Valid for** scope; the current time is inside **Operating hours**; and **Visibility** is **Public** or **Member only** (not Private).
**A program doesn't show in the Direct check-in list**
Direct check-in only lists **generic** programs (no **Valid for** scope) that carry **no** per-visit fee. If the program has a scope or an **Additional price per visit** above 0, it won't appear there - it's meant to be used as a booked-resource entrance method instead.
**The Additional price per visit isn't charged**
Confirm the price is set above 0 and that the program is being used as an entrance method on a booked resource (not through Direct check-in, which excludes priced programs). The charge only lands when the staff member completes the check-in payment.
**A member sees a program they shouldn't**
Change **Visibility** to **Member only** or **Private**. Public exposes the program to everyone, including non-members on your portal.
**Saving fails with a duplicate-code error**
A program with that **Code** already exists for the same club. Pick a different code, or scope one of the programs to a specific club so the codes no longer collide.
**You want to retire a program but keep historical records**
Turn the **Active** toggle off instead of deleting. Disabled programs stay in reports but can't be selected for new check-ins. Delete only programs that were never used.
## Related
* [Check-in models](/reference/check-in-models) - where external programs fit alongside memberships and direct payments.
* [Payment methods](/settings/payment-methods) - the methods used to collect an external program's per-visit fee.
* [Booking policies](/settings/booking-policies) - when payment-at-check-in requires the member to pick an entrance method.
* [Attendance](/schedule/attendance) - see which entrance method was used for each check-in.
# Data import
Source: https://docs.1club.ai/settings/import
Import members, memberships, plans, classes, bookings, invoices, and charges from CSV under Settings > Data Tools > Import, with AI-assisted column mapping, a validated preview, and a controlled execute step.
Data import brings your existing records into 1Club from CSV files - usually when you move your gym over from another system or a spreadsheet. It runs as a four-step wizard: pick what you are importing, upload the file, review a validated preview, then execute. An AI analyzer can detect the record type and map your columns for you, so a file exported from another system usually imports without hand-editing the headers.
You can import seven record types: members (contacts), memberships, plans, classes, bookings, invoices, and charges. Files must be CSV and up to 10 MB.
## When to use it
Use import to start on 1Club with your history intact instead of re-keying members by hand. Nothing is written until you have seen a validated preview, only valid rows are imported, and re-running a file reuses existing members rather than duplicating them.
Typical uses:
* **Migrating members with their memberships** - a padel and tennis gym imports one members file that also creates each member's session-based membership, with sessions already used, and adds children as household dependents.
* **Plans first, then the people on them** - a martial-arts gym imports its belt-progression plans, then a members file that references those plans by name so each student lands on the right package.
* **Carrying over class credits** - a Pilates studio imports one-time class-pack memberships with the pack size, so their remaining class credits carry over.
* **Bringing in past sales** - a climbing gym imports its past day-pass charges, keyed by the member's email.
See [Worked example: migrating from a legacy system](#worked-example-migrating-from-a-legacy-system) and [Other vertical examples](#other-vertical-examples) for the details.
## The import wizard
Import lives under **Settings** > **Data Tools** > **Import**. The wizard has four steps, shown as a stepper across the top: **Select Entity**, **Upload File**, **Preview**, and **Import**.
### Recommended import order
Because records reference each other, import them in dependency order. The wizard states it on the first step:
1. Members (contacts)
2. Plans
3. Memberships
4. Classes
5. Bookings
6. Invoices
Charges can be imported on their own or created as part of a chained member import. Before importing, make sure your **Clubs**, **Areas**, and **Instructors** already exist, because classes and bookings reference them by name or email.
### Step 1 - Select entity
You have two paths on this step.
**AI-powered import.** Upload a CSV into the AI card. The analyzer reads the headers and the first few rows, then proposes:
* the detected record type, with a confidence percentage,
* a short reasoning line explaining the detection,
* a set of column-to-field mappings, shown as `"Your Column" -> field` chips.
If the detection looks right, continue and the mapping is applied automatically. If it is off, you can still pick the record type manually below.
**Manual selection.** Choose one of the record-type cards (Members, Plans, Memberships, Classes, Bookings, Invoices, Charges). Each card shows a short description of what it imports.
### Step 2 - Upload file
If you uploaded through the AI card, your file carries over and the wizard notes it is already uploaded. Otherwise, upload the CSV here. Use **Download Template** to get a CSV with the exact expected column names for the selected record type; fill it in and upload it back. Only `.csv` files are accepted.
### Step 3 - Preview
The preview validates every row against the selected record type without saving anything. You get three counts: **Total Rows**, **Valid Rows**, and **Invalid Rows**. Filter tabs let you view **All**, **Invalid**, or **Valid** rows, and each invalid row lists its specific validation errors. You can download the previewed rows (with their status and errors) as a CSV to fix them offline.
Invalid rows are never imported. Only valid rows are written when you execute, so a preview with invalid rows will import fewer records than the file contains. Fix the flagged rows and re-upload if you need all of them.
### Step 4 - Import
Executing writes the valid rows to your organization. The results screen reports **Total Rows**, **Imported**, **Skipped**, and **Errors**, and lists the first several row-level errors if any occurred. Use **Import another** to start a fresh import.
For **membership** imports, executing first opens a short dialog asking whether to mark the associated charges as paid. If you tick **Mark as paid**, you choose a payment type: **Cash**, **Check**, **Bank Transfer**, or **Other**.
## Field mapping and templates
Each record type has a fixed set of expected fields. The template download gives you those exact column headers.
| Record type | Key expected columns |
| - | - |
| Members (contacts) | `firstName`, `lastName`, `name`, `email`, `phone`, `address`, `city`, `state`, `country`, `postalCode`, `dateOfBirth`, `tags`, `gender`, `notes`, `club` (plus chained columns, below) |
| Plans | `clubName`, `name`, `description`, `price`, `billingFrequency`, `signupFee`, `maxUses`, `validityPeriod`, `sports`, `isActive` |
| Memberships | `contactName`, `contactEmail`, `planName`, `startDate`, `endDate`, `status`, `price`, `billingFrequency`, `autoRenew`, `signupFee`, `maxUses`, `used`, `usesRemaining` |
| Classes | `clubName`, `instructorEmail`, `name`, `description`, `price`, `maxCapacity`, `startTime`, `endTime`, `sport`, `status`, `isFree` |
| Bookings | `contactEmail`, `clubName`, `areaName`, `className`, `startTime`, `endTime`, `status`, `totalPrice`, `notes` |
| Invoices | `contactEmail`, `invoiceNumber`, `invoiceDate`, `dueDate`, `status`, `subtotal`, `taxAmount`, `totalAmount`, `notes`, `description`, `quantity`, `unitPrice` |
| Charges | `contactEmail`, `amount`, `date`, `description`, `paymentStatus`, `transactionType`, `currency`, `taxAmount`, `totalAmount` |
Useful conventions the importer follows:
* **Member name** - use either `name` (a full name that is split into first and last) or `firstName` plus `lastName`. At least one is required.
* **Custom fields** - for member imports, any column whose header starts with `custom_` is treated as a contact custom field automatically. Do not map those to standard fields.
* **Tags** - `tags` takes a comma-separated list, for example `Open day, Lead`. A tag that does not exist yet is created as a manual tag, so you do not have to set your tags up before importing. To tag a whole upload at once, put the same value in the `tags` column of every row.
* **Dates** - accepted formats are `dd.MM.yyyy`, `dd.MM.yyyy HH:mm:ss`, `yyyy-MM-dd`, `yyyy-MM-dd HH:mm:ss`, and ISO-8601. A row with an unparseable date is flagged in the preview.
* **Charges default to paid**, using your organization currency when none is given.
## Chained member import
A member import is more than contacts. When the extra columns are present on a member row, the same row can also create related records in one pass:
* **A charge** - `transactionAmount`, `transactionDate`, `transactionDescription`, `transactionPaymentStatus`, `transactionType`, `transactionCurrency`, `transactionTaxAmount`, `transactionTotalAmount`.
* **A membership** - `planName`, `membershipStartDate`, `membershipEndDate`, `membershipStatus`, `membershipPrice`, `membershipBillingFrequency`, `membershipAutoRenew`, `membershipSignupFee`, plus session totals `membershipMaxUses`, `membershipUsed`, `membershipUsesRemaining`.
* **A wallet credit** - `wallet`, a decimal amount credited to the member's wallet.
* **Household dependents** - `dependent`, a comma-separated list of dependent names (for example children or minors) added under this member's household.
### How duplicates are handled
Members are matched by email within your organization. If a row's email already exists:
* The existing member is reused, not recreated.
* The charge, wallet credit, notes, tags, and custom fields on that row are skipped, so re-running an import does not double-charge or double-note anyone. This also means an import cannot add tags to members who are already in your organization - tag those from their profile instead.
* A membership or dependents on that row are still attached to the existing member. This is how you deliberately import several rows for the same member (one per membership) or add family members to an existing account.
Memberships themselves are de-duplicated by member, plan, and start date, so the same membership row imported twice is not duplicated.
## Worked example: migrating from a legacy system
Suppose a padel and tennis club is moving off an old spreadsheet-based system and exports a members file, `members.csv`, with columns like `Full Name`, `Email`, `Mobile`, `Join Date`, `Plan`, `Membership Start`, `Total Sessions`, `Sessions Used`, and `Kids`.
1. Go to **Settings** > **Data Tools** > **Import**.
2. Drop `members.csv` into the **AI-powered import** card. The analyzer detects **Members (Contacts)** and proposes a mapping such as `"Full Name" -> name`, `"Mobile" -> phone`, `"Join Date" -> createdAt`, `"Plan" -> planName`, `"Membership Start" -> membershipStartDate`, `"Total Sessions" -> membershipMaxUses`, `"Sessions Used" -> membershipUsed`, and `"Kids" -> dependent`.
3. Confirm and continue to the **Preview**. Two rows are flagged: one has `Join Date` as `31/13/2023` (an impossible date) and one is missing an email. The counts read 48 valid, 2 invalid.
4. Download the invalid rows, fix the date and add the missing email in your source, and re-upload. The preview now reads 50 valid, 0 invalid.
5. Execute. Because these rows carry a plan and a start date, each member is created together with a session-based membership, and any names in the `Kids` column are added as household dependents under the parent. Session usage imports so remaining sessions are correct from day one.
Because members were matched by email, running the same file again would reuse every member rather than creating duplicates, and would only add memberships or dependents that were not already there.
### Other vertical examples
* **Martial arts gym** - import a `plans.csv` first (belt-progression memberships with `maxUses` per grading block), then a members file that references those plans by `planName` so each student lands on the right package.
* **Pilates and yoga studio** - import class-pack memberships with `membershipBillingFrequency` set to `one_time` and `membershipMaxUses` for the pack size, so class credits carry over.
* **Climbing gym** - import `transactions.csv` of past day-pass sales keyed by `contactEmail`, leaving `paymentStatus` blank so they default to paid in your organization currency.
## Tips and best practices
* **Let the AI analyzer go first.** It maps most legacy exports correctly, and you can still override the record type manually.
* **Import in dependency order** (members, plans, memberships, classes, bookings, invoices) so references resolve.
* **Set up clubs, areas, and instructors before importing** classes and bookings, which reference them by name or email.
* **Use the preview as a linter.** Download the invalid rows, fix them in your source file, and re-upload rather than editing inside 1Club.
* **Re-running is safe for members.** Email matching means a second run reuses members and only adds missing memberships or dependents.
* **For member imports, prefix custom columns with `custom_`** so they map to contact custom fields automatically.
* **Tag the batch as you import it.** Add a `tags` column with the same value on every row (for example the source or campaign the list came from) so you can filter and message that group afterwards. Tags only apply to members the import creates, so do this on the first run.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| Upload rejected | The file is not a CSV, or is over 10 MB | Export as CSV and split large files under 10 MB |
| AI analysis fails | The CSV is empty or the type could not be detected | Continue with manual selection and download the matching template |
| Rows flagged as invalid dates | The date is not in a supported format | Use `dd.MM.yyyy`, `yyyy-MM-dd`, a variant with time, or ISO-8601 |
| Member rows rejected for no name | Neither `name` nor `firstName` is present | Provide `name` (full) or `firstName` (and optionally `lastName`) |
| Fewer records imported than expected | Invalid rows are skipped on execute | Fix the rows flagged in the preview and re-import |
| Re-import did not re-add charges | The member already exists (matched by email) | Expected behavior - existing members are reused and their charges and notes are not re-run |
| The `tags` column did not tag anyone | Those members already existed, so the row's tags were skipped | Tags apply only to members the import creates. Add the tags from the member's profile, or use a smart tag rule for an ongoing group |
| Contact import blocked | Bulk contact import requires a paid plan (trials are allowed) | Upgrade your 1Club plan, or import during your trial |
| Membership import blocked | It would exceed your active-member quota | Free up capacity or upgrade your 1Club plan |
| A class or booking row cannot find its club, area, or instructor | The referenced record does not exist yet | Create the club, area, or instructor first, then re-import |
## Related
Export your records back out to CSV.
Contacts, custom fields, and how imported members behave.
The membership catalog and checkout that chained imports create against.
Plans that membership imports reference by name.
# Integrations
Source: https://docs.1club.ai/settings/integrations
Activate and configure payment providers, door access, marketing tools, and other integrations from Settings > Integrations
Integrations link 1Club with external services so your bookings, payments, door access, and marketing work alongside the tools you already use. An owner or admin activates and configures each one, and once it is active it runs in the background. Integrations are a paid-plan feature: on the Free plan they stay locked, and during the trial only a few are available (see [Plan limits](#plan-limits)).
## When to use it
Start with a payment provider if members should pay online, then add what your setup needs: a climbing gym opening its doors for booked members through WebLock, a gym syncing area bookings with Google Calendar, or a yoga studio tracking how many bookings its Instagram "Book now" link brings in. See [Worked examples](#worked-examples) for setups like these.
## The integrations page
You manage every integration from **Settings** > **Integrations**. The page has two lists:
* **Active Integrations** - the integrations your organization uses, with a count. **Social Booking** always comes first. Each row shows a status chip, such as **Active**, **Inactive**, or **Configured**, and a **Configure** button that opens the integration's own page.
* **Available Integrations** - everything you can still add, with a count, grouped under **Payments**, **Access control**, **Marketing**, and **Tools**. Each row has an **Activate** button.
Every row also says what the integration does for your gym and carries a **Learn more** link to its page in these docs. When your organization trades through more than one billing entity, the Stripe, myPOS, and DatecsPay rows also say how many of them the provider is connected for, for example "Connected for 1 of 2 billing entities".
Each integration keeps its own configuration and status, so you can run several at once, for example Stripe for online payments and WebLock for door access.
## Available integrations
Twelve integrations are available to activate, plus the built-in Social Booking tool. They are listed below in the same groups as on the page.
### Payments
Card payments online and at the desk, paid out to your bank, with a test
sandbox and live Stripe Connect onboarding.
Customers pay on a hosted myPOS checkout and return to your app, with test
and live modes.
Card payments through BORICA's secure hosted checkout, with a test gateway
and a live environment.
### Access control
Bookings and check-ins open the right WebLock (Valnes) doors and switch on
area lights, set up per gym and per area.
Bookings and check-ins open the doors of each gym's own Impro installation.
Bookings and check-ins open the right Salto KS locks, with a phone digital
key for members.
### Marketing
Your verified business details and locations import themselves from Google.
Your own analytics and pixels run on the member portal and website.
See which bookings and sales your Facebook and Instagram ads bring.
### Tools
Every area booking appears in your Google Calendar as it happens.
Orders go to Hype POS for fiscalization, and its menu syncs back into 1Club.
Send and receive WhatsApp messages from the Inbox with your own WhatsApp
Business number.
### Built in
Members book straight from your Instagram and Facebook profiles, and you see
which platforms drive bookings.
Social Booking is always active. It needs no activation and no external account, and it stays available on every plan.
## How it works
Every integration follows the same lifecycle, though the details differ per provider:
1. **Activate** - click **Activate** next to an available integration. What happens next depends on the integration:
* **Google Calendar, WebLock, Impro, Salto KS, and Hype POS** are added straight away, a confirmation appears, and their page opens so you can finish the setup.
* **Stripe** starts your Stripe account setup in a new tab and opens its page.
* **myPOS, DatecsPay, Google Business Profile, Google Tag Manager, WhatsApp, and Meta** open their page, where you enter credentials or sign in to the provider's account to activate them.
2. **Configure** - on the integration's own page, fill in credentials, mappings, or sync rules. Each integration has its own settings - see the pages above.
3. **Test** - most integrations let you check the setup before you rely on it, for example **Test connection** for WebLock, Impro, Salto KS, and Hype POS, **Validate configuration** for myPOS, and **Test configuration** for DatecsPay. Stripe, Google Calendar, Google Business Profile, Meta, and WhatsApp check the account while you sign in to it.
4. **Use** - once active, the integration runs in the background. It appears under **Active Integrations** with its status, and you return to its page with **Configure**.
5. **Deactivate** - click **Deactivate** at the top of the integration's own page, not on the list. The confirmation, for example **Deactivate Stripe?**, says what you lose, usually that the integration stops working and its settings are deleted. For a payment provider connected for several billing entities, it also says which one the deactivation applies to. If 1Club can't deactivate it, the reason appears in the dialog. Afterwards Stripe, myPOS, and DatecsPay stay on their page, and the others return you to **Settings** > **Integrations**.
Deactivating does not delete data already sent to the provider, such as events in Google Calendar or charges in Stripe. Door access works differently: WebLock has no **Deactivate** button and is switched off per gym, and Impro and Salto KS withdraw the access they granted before they are removed.
Opening **Settings** > **Integrations** requires the
**settings.read.organization** permission. Activating, configuring, testing,
and deactivating integrations requires **settings.manage.organization**.
## Plan limits
* **Free plan** - every integration except Social Booking is locked. A banner says "Integrations are not available in your plan."
* **Trial** - Google Calendar, Google Business Profile, and Social Booking are available. Payments, messaging, door access, and the rest stay locked, and a banner says so.
* **Paid plans** - everything is available.
A locked integration's **Activate** or **Configure** button is greyed out. An integration you activated before the lock stays listed under **Active Integrations**, but you can't open its page until you upgrade.
## Choosing a payment provider
1Club supports **Stripe**, **myPOS**, and **DatecsPay** as payment providers. You can run Stripe alongside one of the hosted-checkout providers, but most operators pick one based on country and preferences:
* **Stripe** is the broader option - widest country coverage, digital wallets, Stripe Connect onboarding, automatic payouts, and a large dashboard ecosystem.
* **myPOS** is a fit if you already have a myPOS merchant account or operate in a region where myPOS is the established provider. Customers pay on myPOS's hosted checkout page, so card data never touches 1Club.
* **DatecsPay** is a BORICA-backed hosted checkout for merchants who already have a BORICA e-commerce terminal, typically issued by a Bulgarian bank. Like myPOS, customers pay on the provider's secure page. Unlike myPOS it has no shared sandbox - both Test and Live need your own BORICA credentials - and it does not support shareable payment links.
Every provider supports a test mode so you can run the full checkout flow before charging real cards. myPOS and DatecsPay both use hosted checkout for the same "pay by card" step, so only one of them can be active at a time for the same billing entity. You can run Stripe alongside either.
## Worked examples
**Padel gym taking online court payments (Stripe).** After upgrading to a paid plan, a racket-sports gym activates Stripe and runs a few bookings in test mode using Stripe test cards to confirm the portal checkout works. It then switches on **Live mode** and onboards its own Stripe Connect account. Court bookings and coach sessions now collect real payments at checkout.
**Boxing gym in a myPOS region.** A martial-arts gym already banks with myPOS. It activates myPOS, pastes its Configuration Pack from the myPOS Checkout Store, validates the configuration, and starts collecting membership dues through hosted checkout - no second payment processor needed.
**Climbing gym with door access.** A climbing gym activates WebLock and links each of its areas to WebLock door groups and light devices, so a booking or check-in in an area opens that area's doors. It keeps Stripe active in parallel for online payments.
**Yoga studio marketing its schedule.** A yoga studio uses the always-on Social Booking tool to generate a tagged "Book now" link for its Instagram bio, then checks **Analytics** > **Bookings by source** to see how many reformer bookings came from Instagram versus its website.
## Tips & best practices
* Try every payment provider in test mode first. A test payment through the real checkout flow is the only reliable proof that live credentials will work.
* Keep only the integrations you actually use active. Fewer active integrations means fewer places to reconcile and fewer moving parts when something looks off.
* Most integrations are set up once for the whole organization. Door access is the exception: WebLock, Impro, and Salto KS are set up per gym, and a gym can use only one of them at a time. Payment providers are connected per billing entity when you trade through more than one company.
* Upgrade to a paid plan before you plan to take payments. Stripe, myPOS, and DatecsPay can't be activated on the Free plan or during the trial.
## Troubleshooting
* **Every Activate button is greyed out** - your organization is on the Free plan. Upgrade to unlock integrations. Social Booking stays available regardless.
* **Only Google Calendar and Google Business Profile can be activated** - your organization is still in the trial. Upgrade to unlock payments, messaging, door access, and the rest.
* **I can see integrations but can't activate or configure them** - you are missing the **settings.manage.organization** permission. Ask an admin to grant it, or have an admin do the setup.
* **An integration I deactivated still shows external data** - deactivating stops 1Club from creating new activity, but it does not delete data already sent to the provider. Manage that from the provider's own dashboard (for example refunds in Stripe or myPOS).
* **Google Tag Manager is still under Available Integrations even though I added a container ID** - the list catches up when the Google Tag Manager page loads. Click **Activate** next to Google Tag Manager to open its page, or save the container ID again.
## Related
* [Stripe](/settings/integrations/stripe) - card and wallet payments with test and live modes.
* [myPOS](/settings/integrations/mypos) - hosted-checkout card payments and payment links.
* [DatecsPay](/settings/integrations/datecspay) - BORICA-backed hosted-checkout card payments.
* [WebLock](/settings/integrations/weblock) - door access and area lights per gym and area.
* [Impro](/settings/integrations/access-bridge) - door access through each gym's own Impro installation.
* [Salto KS](/settings/integrations/salto) - door access with a phone digital key.
* [Google Business Profile](/settings/integrations/google-business) - import verified locations.
* [Google Tag Manager](/settings/integrations/google-tag-manager) - analytics and tags on your public surfaces.
* [Meta](/settings/integrations/meta) - measure your Facebook and Instagram ads on bookings and revenue.
* [Google Calendar](/settings/integrations/google-calendar) - two-way booking sync.
* [Hype POS](/settings/integrations/hype) - fiscal receipts, kitchen display, and menu sync.
* [WhatsApp](/settings/integrations/whatsapp) - WhatsApp messages in the 1Club inbox.
* [Social Booking](/settings/integrations/social-booking) - tagged "Book now" links and booking attribution.
# Impro
Source: https://docs.1club.ai/settings/integrations/access-bridge
Open the doors of each gym's own Impro access control installation from 1Club bookings and check-ins, with areas, people, and credentials kept in sync automatically.
The Impro integration connects 1Club to the Impro access control system each of your gyms already runs. 1Club keeps the gym's own Impro installation up to date with who may enter which area and when, so a booking or check-in opens the right doors without staff programming cards by hand. An owner or admin sets up each gym once, and an installer connects the areas to their doors in Impro.
## When to use it
Use it when a gym's doors are already controlled by an on-site Impro installation and you want access to follow what members book. 1Club decides who qualifies and for how long, and Impro keeps doing what it does best: reading cards and driving the locks.
Typical uses:
* **Court doors that open only for the booking** - a padel gym syncs each court as an area, and players get in a little before their slot and out a little after, with no front desk.
* **A climbing gym with zoned access** - the bouldering hall and the gym floor are separate areas, so a member booked into one doesn't get into the other.
* **Several gyms, each with its own installation** - each gym uses its own Impro installation with its own key, and access at one never leaks into another.
## How it works
* **Areas become doors.** You sync the gym's areas to Impro. An installer then connects each area to its doors in Impro.
* **Access follows the schedule.** A booking or a check-in opens the area for its own time window plus the access buffer.
* **People stay in step.** A booking, check-in, or cancellation reaches Impro within moments. A complete sync of the people who currently qualify for access, their credentials, and their areas also runs every five minutes, and you can run it by hand.
* **It runs on your network.** 1Club's server reaches your Impro installation directly. The browser never calls Impro, and the API key is stored encrypted.
### The access window
The **Access buffer (minutes)** sets how early a member can get in before a booking starts, and how long they keep access after it ends. It can be 15-120 minutes.
A check-in grants access immediately, without waiting for the buffer before the start, and keeps it until the end of the booking plus the buffer.
## Before you start
* Impro is a **paid-plan** feature. On the Free plan and during the trial, its **Activate** and **Configure** buttons stay greyed out until you upgrade.
* Configuring Impro requires the **settings.manage.organization** permission. Viewing its settings requires **settings.read.organization**.
* Each gym needs its own Impro installation, its **Installation URL**, and an **API key** issued for that installation.
* Each gym needs at least one active area. Area names must be distinct within the gym, because each area becomes a named area in Impro. See [Areas](/settings/areas).
* A gym can use only one door system at a time. If a gym already uses another provider, such as [WebLock](/settings/integrations/weblock) or [Salto KS](/settings/integrations/salto), turn that one off for the gym first. Until you do, the Impro page says which provider the gym uses and **Enable Impro for this club** can't be turned on.
## Activate and set up a gym
1. Go to **Settings** > **Integrations** and click **Activate** next to Impro, under **Access control**. 1Club activates Impro and opens its page. Once it is active, you reach the page from **Configure** under **Active Integrations**.
2. On the Impro page, choose the gym under **Club**.
3. Enter the gym's **Installation URL**.
4. Enter the **API key**. Use the key issued for this installation. Once a key is saved, leave the field blank to keep it.
5. Set the **Access buffer (minutes)** (15-120).
6. Click **Test connection**. The test uses the URL and key on screen, so you can try them before you save. It reports whether the connection works, or that it connected but some checks need attention.
7. Turn on **Enable Impro for this club** and click **Save**. **Save** stays greyed out until the URL and key are filled in and the buffer is a whole number from 15 to 120.
Repeat for every gym that has its own Impro installation.
## Sync areas
Under **Areas**, each area shows **Synced** or **Needs sync**.
1. Click **Sync areas** to create the gym's areas in Impro. The button is available once the gym's connection is saved, and only while there are no unsaved changes.
2. Ask your installer to connect each area to its doors in Impro.
Sync again whenever you add or rename an area. A new or renamed area opens no doors until it has been synced.
## Sync people
Under **People**, 1Club shows **Pending access updates** while some changes still have to reach Impro. The people who currently qualify for access, their credentials, and their areas sync every five minutes on their own. Click **Sync people** to run the same sync for this gym straight away. It is available while Impro is on for the gym and there are no unsaved changes. While it runs, the page shows how many people have been processed.
If some updates fail, 1Club retries them automatically. A warning asks you to check the installation's connection and configuration in the meantime.
## Credential technologies
**Credential technologies** sets what 1Club writes to Impro for each card, PIN, and QR code. Save the connection and test it first: only then does 1Club know what the installation can write.
For access cards and PIN codes, pick a technology under **Technology for Access card** and **Technology for PIN code** from the ones the installation advertises, or leave it on **Installation default**. QR codes need no choice. If the technology uses a site code alongside the card number, a **Facility code** field appears. Enter it as digits. When the readers require one, you can't save until it is filled in. The table below the pickers shows each technology the installation offers, with the credential it serves, its format, length, facility code, and Impro TagType.
The technologies must match the reader configuration in Impro, including the facility code. A card written with the wrong technology, or without the facility code its readers expect, does not open the door.
Some installations don't publish their credential technologies. In that case 1Club sends credentials without one, exactly as before, and the page says so. If an installation stops advertising a technology you picked, the page asks you to choose another one or clear it.
The credentials themselves are the members' access cards, PIN codes, and QR codes in 1Club. See [Access cards](/members/member-cards).
## Turn Impro off for a gym or change its installation
Turning off **Enable Impro for this club** and saving withdraws access rather than just editing a setting. 1Club switches Impro off for the gym and starts access cleanup at the installation that still holds the access.
To move a gym to a different installation URL or key:
1. Turn off **Enable Impro for this club**, enter the new **Installation URL** or **API key**, and click **Save**. The page confirms that Impro is off for the gym while access is cleared, and the new values stay on screen while cleanup runs.
2. Watch **Pending access updates** under **People**.
3. Once no updates are pending, click **Save** again to apply the new connection.
You can't change the URL or key while a people sync is running. Wait for it to finish first.
## Deactivate
Deactivating removes Impro from every gym in your organization at once.
1. Click **Deactivate** at the top of the Impro page.
2. The confirmation, **Deactivate Impro?**, says that Impro stops working and its settings are deleted, so you'd need to set it up again to use it. Click **Deactivate** to confirm.
1Club first switches Impro off for every gym. It then withdraws the access the installations still hold, because their URL and key are the only way to reach them. Once nothing is held, it removes every gym's installation and returns you to **Settings** > **Integrations**.
If an installation can't be reached, or a people sync is still running or has failed, deactivation is refused and the dialog says why. Impro stays off for every gym in the meantime. Make the installation reachable so 1Club can withdraw the access, wait for cleanup to finish, then deactivate again.
## Troubleshooting
* **The page asks you to create an area first** - the gym has no active area. Use **Manage areas** to add one.
* **The page says the gym uses another provider** - only one door system can run per gym. Turn the other provider off for this gym, then turn on **Enable Impro for this club**.
* **An area opens no doors** - check that it shows **Synced**. If it shows **Needs sync**, click **Sync areas**, then make sure your installer has connected it to its doors in Impro.
* **A member's card doesn't open the door** - check **Credential technologies** against the reader configuration in Impro, including the facility code. A warning appears when some credentials could not be written to the installation.
* **Updates keep failing** - check that 1Club's server can still reach the **Installation URL** and that the **API key** is current. Use **Test connection** to confirm.
## Related
* [Integrations](/settings/integrations) - every integration and how activation works.
* [Salto KS](/settings/integrations/salto) - cloud door access with a phone digital key.
* [WebLock](/settings/integrations/weblock) - door access through WebLock (Valnes).
* [Areas](/settings/areas) - the areas that become doors in Impro.
* [Access cards](/members/member-cards) - the cards, PINs, and QR codes members present at the door.
# DatecsPay
Source: https://docs.1club.ai/settings/integrations/datecspay
Accept card payments through DatecsPay (BORICA) hosted checkout, with a BORICA-backed test gateway and a live production environment
DatecsPay is a card payment provider you can use instead of Stripe or myPOS, built on BORICA's hosted checkout. When a customer pays for a booking, class, membership, or outstanding balance, they are redirected to BORICA's secure page to enter their card details (with EMV 3-D Secure), then return to 1Club once the payment completes. An owner or admin sets it up once with your gym's own BORICA credentials.
If your organization trades through more than one company, each company
connects its own BORICA terminal. Pick the company under **Billing entity** at
the top of the DatecsPay page before you save or deactivate anything. The line
under the picker reminds you that payments for that company's gyms settle to
the account you are setting up. The picker only appears when you have more
than one billing entity. See [Billing entities](/settings/billing-entities).
## When to use it
DatecsPay suits a gym whose bank or acquirer has provisioned a BORICA terminal. Card data never touches 1Club servers, and every request is signed with your own RSA key. Payment status comes back on its own: through the cardholder's browser on return, a signed BORICA notification, and a reconciliation sweep that catches anyone who closes the tab early.
Unlike Stripe and myPOS, DatecsPay has no free shared sandbox. Both the **Test** and **Live** environments run against BORICA with your own credentials. Only the terminal and gateway differ between them.
Typical uses:
* **A gym already banking with BORICA** - take card payments online through the BORICA terminal your bank issued, with no Stripe or myPOS account.
* **Rehearsing checkout before going live** - run the full checkout against BORICA's test gateway with your test terminal, where no real money moves, then switch to **Live**.
* **A member who closes the tab mid-payment** - the membership still settles, because the reconciliation job confirms the charge with BORICA within a few minutes, with no staff action.
## Before you start
* **A paid plan.** DatecsPay is a paid-plan integration. On the Free plan and during the free trial, **Activate** is greyed out until you upgrade.
* **BORICA credentials for the environment you want.** Both Test and Live need a **Terminal ID (TID)**, a **Merchant ID (MID)**, your **RSA private key**, and **BORICA's public certificate**. BORICA issues a separate set for each environment, so keep the two sets apart.
* **A free hosted-checkout slot.** Each company can run only one hosted-checkout provider at a time. If myPOS hosted checkout is active for the company, deactivate myPOS first, or untick its **Online** capability if you only use it for terminals.
Configuring DatecsPay requires the **settings.manage.organization**
permission. Viewing its settings requires **settings.read.organization**.
## Where to get your DatecsPay credentials
DatecsPay credentials come from **BORICA** (through your bank or acquirer), not from 1Club. When your DatecsPay/BORICA terminal is provisioned you receive:
| Credential | What it is |
| - | - |
| **Terminal ID (TID)** | The identifier of your BORICA terminal. You get a separate TID for the test gateway and for production. |
| **Merchant ID (MID)** | Your BORICA merchant identifier. |
| **RSA private key (PEM)** | The private half of your signing key pair, used to sign requests to BORICA. You generate the key pair and register the public half with BORICA. |
| **BORICA public certificate (PEM)** | BORICA's certificate, used to verify the responses coming back from the gateway. The test and production certificates are different. |
Generate your key pair and register it with BORICA before you configure 1Club, and download the matching BORICA certificate for the environment you are setting up.
## Activate DatecsPay
1. Go to **Settings** > **Integrations**.
2. Under **Payments**, click **Activate** next to DatecsPay. 1Club opens the DatecsPay page. Nothing is saved until you click **Save**.
3. Choose an **Environment**:
* **Test** - runs against BORICA's test gateway.
* **Live** - processes real payments against BORICA production.
BORICA gives you different credentials for each environment, so enter the set that matches the one you choose.
4. Enter your **Terminal ID (TID)** and **Merchant ID (MID)**.
5. Check the **Merchant name** and **Merchant website**. 1Club pre-fills these from your organization's name and website, but you can edit them if BORICA registered a different name or URL.
6. Paste your **Merchant private key (PEM)** and **BORICA public certificate (PEM)**. If your key file is protected with a passphrase, enter it in **Private key passphrase**. 1Club uses it once to decrypt the key and never stores it.
7. Click **Save**. The page shows **Active** and the environment, **Test** or **Live**, next to the title.
8. Click **Test configuration** to check that the stored key and certificate are well-formed. The button is available once DatecsPay is saved.
The private key and certificate are stored encrypted and shown back to you as
masked fields (`•••••• (saved - leave blank to keep)`). When you edit the
configuration later, leave them blank to keep the existing values. Only paste
a value if you are replacing it.
Your country and time-zone offset are not part of this form. 1Club derives them automatically from your organization settings and sends them to BORICA at checkout.
## Configuration fields
| Field | Required | Notes |
| - | - | - |
| **Environment** | Yes | **Test** or **Live**. Your BORICA credentials differ between the two. |
| **Terminal ID (TID)** | Yes | Your BORICA terminal identifier; differs per environment. |
| **Merchant ID (MID)** | Yes | Your BORICA merchant identifier. |
| **Merchant name** | No | Pre-filled from your organization name. Editable. |
| **Merchant website** | No | Pre-filled from your organization website. Editable. |
| **Merchant private key (PEM)** | On first save, and when you change environment | Your RSA private key used to sign requests to BORICA. Stored encrypted. Leave blank when editing to keep the current key. |
| **Private key passphrase** | No | Only if your key file is passphrase-protected. Used once to decrypt the key and never stored. |
| **BORICA public certificate (PEM)** | On first save, and when you change environment | BORICA's certificate used to verify gateway responses. Differs between test and production. Leave blank when editing to keep the current certificate. |
Capabilities are limited to **online** hosted checkout, which is always on. There is no in-person terminal option.
## Test vs. live environment
* **Test** runs against BORICA's test gateway with your test terminal. No real money moves, so you can rehearse the full checkout flow.
* **Live** runs against BORICA production with your live terminal. Real payments are processed and settled to your account.
There is no credential-free sandbox. Both environments need a full credential set, and the TID, key, and certificate are different for each.
DatecsPay keeps one credential set per company, for the environment you saved. When you switch **Environment**, the page clears the **Terminal ID (TID)**, key, passphrase, and certificate fields. To save, enter the TID, private key, and BORICA certificate issued for the new environment. If you leave the key or certificate blank, the page asks for the one issued for that environment. Your saved set for the old environment is replaced, so keep a copy if you plan to switch back.
## Test configuration
The **Test configuration** button checks that your saved credentials are complete and well-formed: that the private key and BORICA certificate are valid and the gateway for your environment resolves. It records the time of the last successful check. On success it shows "DatecsPay configuration looks valid".
This check runs locally and does **not** contact BORICA or move any money. It
catches a malformed key or a missing field, but it can't confirm that BORICA
recognizes your TID or merchant. The first real proof that credentials are
correct is a successful test payment through hosted checkout.
## What syncs
* Payments created in 1Club open a BORICA hosted-checkout page that the customer is redirected to.
* After payment, the status returns to 1Club through up to three paths, so a payment settles even if one path fails:
1. **Browser return** - BORICA posts the signed result back through the cardholder's browser when they return to 1Club, and 1Club verifies the signature and settles the payment.
2. **Signed notification** - BORICA can also notify 1Club server to server. The notification's signature is verified against your BORICA certificate before it is processed.
3. **Reconciliation** - a background job runs every 5 minutes and confirms any payment still pending (for example because the customer closed the tab before returning) with a direct status check to BORICA.
* The payment status and BORICA transaction references are recorded on the 1Club payment, and any linked charges and bookings are settled automatically.
* Every path is matched to the same payment by a unique reference, so a redelivered notification or an overlapping reconciliation never double-charges or double-settles.
## Payment links
DatecsPay has no shareable payment link. The **Send myPOS payment link** action on a charge works with myPOS only, so on DatecsPay a member pays through the checkout in the portal, the app, or your website rather than from a link you send them. If sending a pay-by-link is central to how you collect, myPOS is the better fit.
## Refunds
1Club does not push refunds to BORICA. Refund a DatecsPay payment from your BORICA/acquirer side, then reconcile the charge in 1Club.
## Deactivate
1. Pick the company under **Billing entity**, if you have more than one.
2. Click **Deactivate** at the top of the DatecsPay page.
3. Confirm in the **Deactivate DatecsPay?** dialog. It says DatecsPay stops working and its settings are deleted. With more than one billing entity, it also says the change applies to the selected company only.
You stay on the DatecsPay page afterwards, with the form reset. Your stored credentials for that company are cleared, so activating again means entering them again. If 1Club can't deactivate DatecsPay, the reason shows in the dialog.
## Troubleshooting
* **Activate is greyed out** - your organization is on the Free plan or the free trial. Upgrade to a paid plan to activate payments.
* **Test configuration fails** - confirm the **Merchant private key (PEM)** and **BORICA public certificate (PEM)** are valid PEM and that you pasted the certificate for the environment you selected (test and production certificates differ).
* **Save asks for the key and certificate again** - you changed **Environment**. BORICA issues a separate set per environment, so paste the key and certificate for the environment you selected.
* **Customers see a signature error on the checkout page** - the public key registered with BORICA doesn't match the private key on file, or the TID belongs to a different environment. Re-check the key pair registered with BORICA and confirm the TID matches your selected environment.
* **Payment succeeded on BORICA but the charge stays unpaid in 1Club** - this usually resolves on its own: the reconciliation job confirms pending checkouts within a few minutes. If it persists beyond that, re-check that your BORICA certificate is current.
* **Live payments fail but test works** - confirm you are in the **Live** environment and entered live credentials (a production TID and the production BORICA certificate, not the test ones).
* **"myPOS is already enabled"** - only one hosted-checkout provider can be active per company. Deactivate myPOS for that company, or untick its **Online** capability, then save DatecsPay again.
## Worked examples
**Bulgarian gym already banking with BORICA.** A fitness gym whose bank issued it a BORICA e-commerce terminal generates an RSA key pair, registers the public key with BORICA, and downloads the production certificate. It activates DatecsPay, selects **Live**, enters its TID and MID, pastes the private key and certificate, saves, and runs **Test configuration**. One real court booking confirms hosted checkout settles the charge.
**A member who closes the tab mid-payment.** A member pays for a membership on BORICA's page but closes the browser before returning to 1Club, so the payment briefly shows as pending. Within a few minutes the reconciliation job checks the status with BORICA, confirms the charge, and settles the membership, with no staff action needed.
## Tips & best practices
* Keep your test and production credential sets clearly separated. The TID, key, and BORICA certificate differ per environment, and mixing them is the most common cause of signature errors.
* Remember that **Test configuration** is a local check only. It can't confirm BORICA recognizes your terminal, so always follow it with a real test payment.
* Leave the masked private key and certificate fields blank when editing later. Only paste a value when you are deliberately replacing it.
* Decide up front whether each company takes online card payments through DatecsPay or myPOS. Only one hosted-checkout provider can run per company.
## Related
* [Integrations](/settings/integrations) - the full integration catalog and how activation works.
* [Billing entities](/settings/billing-entities) - the companies you trade through, each with its own payment accounts.
* [myPOS](/settings/integrations/mypos) - the other hosted-checkout provider (myPOS **Online** can't run alongside DatecsPay for the same company).
* [Stripe](/settings/integrations/stripe) - card and wallet payments with test and live modes.
* [Charges](/sales/charges) - where payable charges live.
* [Payments](/sales/payments) - the payment records DatecsPay settles.
# Google Business Profile
Source: https://docs.1club.ai/settings/integrations/google-business
Import your Google Business Profile locations into 1Club as gyms, with name, address, phone, website, opening hours and map pin filled in.
The Google Business Profile integration imports your business locations from Google into 1Club as gyms. An owner or admin connects the Google account that manages the Business Profile, then picks which locations to import. Each one becomes a gym with its details filled in from Google.
## When to use it
It saves retyping every address when your locations already live on Google. Data flows one way, from Google into 1Club, and only when you run an import.
Typical uses:
* **Onboarding a multi-site operator** - a padel and tennis operator with four locations on Google imports them all at once, and each becomes its own gym, ready for areas and pricing.
* **Reusing a polished single listing** - a boutique pilates studio imports its one Google listing, so its gym record matches Google from day one.
* **Adding a new location later** - after adding a location on Google, run the import again for it. Locations you already imported fill in the existing gym instead of creating a second one.
## Before you start
* Google Business Profile isn't available on the Free plan. It works during the trial and on paid plans.
* Connecting and importing require the **settings.manage.organization** permission. Viewing the page and the location list requires **settings.read.organization**.
* You need the Google account that owns or manages your Business Profile, with at least one location on it.
## Connect Google Business Profile
1. Go to **Settings** > **Integrations** and click **Activate** on the Google Business Profile card, under **Marketing**. The Google Business Profile page opens.
2. Click **Connect Google Business Profile**.
3. Sign in to Google and allow 1Club to access your Business Profile.
4. You return to the page with the **Google Business Profile status** card showing **Active**.
Until you connect, the status shows **Not activated**.
## Import locations
After you connect, the **Import multiple locations** card lists the locations on the first business account of the connected Google account. Each one shows its name, address, phone, website, description and categories.
1. Tick the locations you want to import, or click **Select All**.
2. Click **Import** (the button shows how many locations, for example **Import 3 Locations**).
When the import finishes, a **Locations imported** message confirms it, and a list shows each gym with its address. You can import up to 50 locations at a time.
Each location is imported on its own, so one bad listing doesn't stop the rest. If some fail, a **Some locations were not imported** message says how many. Check those locations in Google Business Profile and try again.
### What a new gym gets
A location imported for the first time becomes a new, active gym with:
* Its name, description, address and country.
* Its phone number and website.
* Its opening hours, when the listing has them.
* Its map pin, taken from the listing.
* Its sports, when the listing's category matches one 1Club knows.
1Club also sets the gym's time zone from its address and turns on its daily summary email. Photos aren't imported.
### Importing a location again
A location you already imported, or one whose Google place is already connected to a gym, is not created twice. Instead, the import fills in only the fields of that gym that are still empty. Anything you edited in 1Club is never overwritten. The map pin is the one exception: Google's pin replaces a pin 1Club guessed from the address, but never one a person placed. A gym updated this way is marked **(updated)** in the list.
If the gym is listed on the [marketplace](/marketing/marketplace), the filled-in details wait for review before the public listing changes. The list marks these with **changes awaiting review**.
## What syncs
This is a one-way import from Google into 1Club, on demand. Changes you make to a gym in 1Club don't flow back to Google. Updates you make on Google don't reach the gym until you import that location again, and then only into fields that are still empty.
## Worked examples
### Racket-sports operator onboarding four gyms
A padel and tennis operator running four gyms already keeps every address, phone number and website on Google. Instead of creating four gyms by hand, connect Google Business Profile, click **Select All** and import. Each location becomes a gym with the right address, contact details and opening hours, ready for you to add areas (courts) and pricing.
### Single-studio owner reusing their Google listing
A boutique pilates studio owner has one Google listing with the business name, address and website they want customers to see. Connect, import the single location, and the gym record takes that data, so the marketplace listing and member portal match Google from day one.
## Tips and best practices
* Import first, then configure. The import fills in identity, contact and opening hours. You still add areas, pricing and branding per gym afterwards.
* Connect with the Google account that actually manages the Business Profile. An account with no access to the profile shows no locations.
* Import a new Google location rather than creating its gym by hand, so the gym is linked to its Google place.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| "No verified locations found in your Google Business Profile." | The first business account of the connected Google account has no locations 1Club can see | Check the profile in [business.google.com](https://business.google.com), or connect with the account that manages it |
| "Access was denied. Please try again." | You declined the Google consent screen | Click **Connect Google Business Profile** again and allow access |
| "Invalid OAuth state. Please try again." | The sign-in took too long or came from a stale tab | Start again from the Google Business Profile page |
| Connecting fails with a plan message | Your organization is on the Free plan | Upgrade the plan, then connect again |
| An import fails saying Google rejected the stored credentials | Google no longer accepts the saved connection | Deactivate, then connect again |
| An import fails saying Google denied access to the Business Profile API | The profile is no longer shared with the connected account | Share the profile with that account again, or reconnect with an account that manages it |
| Wrong Google account connected | You signed in with an account that doesn't manage the Business Profile | Deactivate, then connect with the right account |
## Deactivate
1. Click **Deactivate** at the top of the Google Business Profile page. It is shown once you have connected.
2. Confirm in the **Deactivate Google Business Profile?** dialog. It warns that the integration stops working and its settings are deleted.
1Club deletes the stored Google connection and takes you back to **Settings** > **Integrations**. If the deactivation is refused, the reason shows inside the dialog. Gyms you already imported stay in 1Club unchanged.
Deactivating doesn't revoke access on Google's side. To do that, remove 1Club from the third-party access list in your Google Account.
## Related
* [Integrations](/settings/integrations) - the full integration catalog and how activation works.
* [Organization and clubs](/reference/organization-clubs) - where each imported gym's details live.
* [Areas](/settings/areas) - set up bookable spaces inside each imported gym.
* [Marketplace](/marketing/marketplace) - where imported gym details help power public listings.
# Google Calendar
Source: https://docs.1club.ai/settings/integrations/google-calendar
Sync 1Club bookings with one or more Google calendars, mapped per area or per instructor, in either or both directions.
The Google Calendar integration copies 1Club bookings into Google Calendar, and Google Calendar events into 1Club bookings. An owner or admin authorizes one Google account, then maps each area or instructor to one of that account's calendars and chooses whether bookings go to Google, come in from Google, or both. Staff and instructors then see the schedule in the calendar they already use.
## When to use it
Each mapping is independent, so you can send a studio area to a shared calendar and sync a personal trainer's own calendar at the same time. There is no limit on how many mappings you create.
Typical uses:
* **Court bookings in the team calendar** - map each court to its own calendar, outbound only, so staff see bookings in Google while stray Google events never turn into court bookings.
* **A personal trainer who also books outside 1Club** - sync the instructor's own calendar both ways, so a block they add in Google comes back as a booking on their schedule and members can't book them during that time.
* **A room schedule for someone outside the team** - map a studio area outbound to a calendar shared with the building manager, and click **Sync now** after publishing a new timetable.
## How it works
* **Sync runs when you click Sync now.** Each mapping has a **Sync now** button. Nothing syncs on its own in the background, so click it after schedule changes.
* **Outbound** - every booking for the mapped area or instructor is created in the Google calendar, or updated if it is already there.
* **Inbound** - Google events in the next 30 days that 1Club hasn't seen yet become bookings for the mapped area or instructor.
* **Nothing is deleted on the other side.** Cancelling a booking or deleting a Google event never removes its copy.
## Before you start
* Google Calendar isn't available on the Free plan. It works during the trial and on paid plans.
* Authorizing Google, creating, editing and deleting mappings, and syncing require the **settings.manage.organization** permission. Viewing the page requires **settings.read.organization**.
* You need a Google account that can see the calendars you want to sync. Use an account your team controls, not one that leaves with an employee.
* To sync with a Google Workspace resource calendar (a room or court), add that calendar to the authorizing account's calendar list first. The dropdown only shows calendars in that list.
## Activate and authorize Google
1. Go to **Settings** > **Integrations** and click **Activate** on the Google Calendar card, under **Tools**. The Google Calendar page opens.
2. Click **Authorize Google Calendar**.
3. Sign in to Google and allow 1Club to view and manage your calendars.
4. You return to the Google Calendar page. The **Google Calendar status** card shows **Active**, a chip with how many calendars were found, and an **Open Google Calendar** button.
Until you authorize, the status shows **Not activated**.
## Add a mapping
Each mapping links one area or one instructor to one Google calendar.
1. Click **Add mapping**.
2. In the **Create calendar mapping** dialog, choose an **Area (optional)** or an **Instructor (optional)**. Choosing one clears the other, so a mapping targets exactly one of them.
3. Choose the **Google Calendar** to sync with. The account's primary calendar is marked **(Primary)**.
4. Choose a **Sync direction**:
* **Two-way** (the default) - bookings go to Google, and new Google events come back as bookings.
* **Outbound only (bookings → Google)** - bookings go to Google. Google events are ignored.
* **Inbound only (Google → bookings)** - new Google events come in as bookings. Bookings are not sent to Google.
5. Click **Add**.
The mapping appears as a card titled with the area or instructor name. It shows the gym (for an area) and the calendar name, the sync direction, and **Last sync** once it has synced.
To change a mapping, click **Edit**, change the fields and click **Update**. To remove one, click **Delete** and confirm. Deleting a mapping leaves the events already in Google in place.
An area or instructor can be mapped to the same calendar only once.
## What syncs
Click **Sync now** on a mapping to run it. When it finishes, a **Sync complete** message says how many calendar entries were sent and received.
### Bookings to Google
* The event title is the area or instructor name plus the member's name, for example `Court 1 - Jane Doe`.
* The event description lists the area, instructor, gym, member name and email, any booking notes, and the booking ID.
* The member is added to the event as a guest when they have an email address.
* The event uses the booking's start and end time.
* A booking that isn't confirmed, for example a cancelled one, shows as a tentative event. The event isn't removed.
### Google events to 1Club
* Each new event in the next 30 days becomes a confirmed booking for the mapped area or instructor, at no charge.
* The event's first guest becomes the booking's contact. 1Club matches them by email, or creates a new contact. An event with no guests is booked under the staff member who clicked **Sync now**.
* The event description becomes the booking notes.
* An event is only imported once. Later changes to it in Google don't update the booking, and deleting it doesn't cancel the booking.
## Worked examples
### Padel gym: publish court schedules to the team calendar
A four-court padel gym wants front-desk and coaching staff to see court bookings in the Google Workspace calendar they already use. Create one mapping per court, each pointing at that court's resource calendar, with **Outbound only (bookings → Google)**. After each **Sync now**, bookings taken in 1Club appear in those calendars, and events someone adds in Google never become court bookings.
### Personal trainer: two-way sync to avoid double-booking
An independent strength coach takes 1Club bookings but also schedules outside clients in their own Google calendar. Map the instructor to that calendar with **Two-way**. When you click **Sync now**, a block they added in Google for the next 30 days comes into 1Club as a booking on their schedule, so members can't book them during that time. Sessions booked in 1Club go to their Google calendar.
### Yoga studio: share the room schedule with a landlord
A yoga studio that sublets its room reports the schedule to the building manager. Map the studio area to a calendar shared with the manager, with **Outbound only (bookings → Google)**, and click **Sync now** after publishing the new term's timetable.
## Tips and best practices
* Use **Outbound only** whenever the Google calendar is only for viewing. It stops stray Google events from creating real bookings.
* Keep **Two-way** for calendars a person actively manages, usually an instructor's, where blocking time in Google should block time in 1Club.
* Click **Sync now** after timetable changes and on a regular routine. Nothing syncs until you do.
* Add guests to Google events you want imported, so the booking lands on the right contact.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| "Access was denied. Please try again." | You cancelled or declined the Google consent screen | Click **Authorize Google Calendar** again and allow access |
| "Invalid OAuth state. Please try again." | The sign-in took too long or came from a stale tab | Start again from the Google Calendar page |
| "OAuth authentication failed. Please try again." | Google didn't complete the sign-in | Try again. If it keeps failing, contact support |
| Authorizing fails with a plan message | Your organization is on the Free plan | Upgrade the plan, then authorize again |
| A calendar is missing from the dropdown | It isn't in the authorizing account's calendar list | Add it to that account in Google Calendar, or authorize with the right account |
| New bookings or events aren't showing on the other side | The mapping hasn't been synced since the change | Click **Sync now** on the mapping |
| Google events aren't becoming bookings | The mapping is **Outbound only**, or the event is more than 30 days away | Edit the mapping to **Two-way** or **Inbound only**, or sync again closer to the date |
| An imported booking is on the wrong contact | The event had no guests, or its first guest is someone else | Add the member as the event's first guest before syncing |
| A booking shows up twice in Google | Two mappings send the same area or instructor to different calendars | Delete the mapping you don't need |
## Deactivate
1. Click **Deactivate** at the top of the Google Calendar page.
2. Confirm in the **Deactivate Google Calendar?** dialog. It warns that the integration stops working and its settings are deleted.
1Club deletes the stored Google connection and takes you back to **Settings** > **Integrations**. If the deactivation is refused, the reason shows inside the dialog.
Events already created in Google stay there until you delete them in Google Calendar. Your calendar mappings are kept, so they come back if you activate and authorize again. Deactivating doesn't revoke access on Google's side. To do that, remove 1Club from the third-party access list in your Google Account.
## Related
* [Bookings](/schedule/bookings) - where the bookings that sync are created and managed.
* [Areas](/settings/areas) - the areas you map to calendars.
* [Instructors](/staff/instructors) - the instructors you map to calendars.
* [Integrations](/settings/integrations) - the full integration catalog and how activation works.
# Google Tag Manager
Source: https://docs.1club.ai/settings/integrations/google-tag-manager
Load a Google Tag Manager container on your member portal and CMS website with no code, then track page views, sign-ups, bookings, and purchases through your own tags.
This integration loads a Google Tag Manager (GTM) container on your 1Club public-facing surfaces - the member portal and the CMS website - without any code changes. An owner or admin pastes your container ID once and 1Club injects the GTM snippet on every page load. From there, whoever runs your marketing controls what fires from your GTM workspace (GA4, Meta Pixel, conversion pixels, custom tags, and so on).
## When to use it
Use it when you want your own analytics and ad tags on the pages where members sign up, book, and pay. 1Club pushes standard events (page views, sign-up, login, booking, payment, form submit) to the dataLayer, so you can wire them to GA4 or any tag without a redeploy. Consent stays in your hands too: the container works with Google Consent Mode configured in GTM.
Typical uses:
* **Measuring which ads produce bookings** - forward the `booking_created` event to GA4 and the Meta Pixel, so booking conversions are attributed to campaign and surface.
* **Separating website leads from portal sign-ups** - trigger on `sign_up`, remap `form_submitted` to GA4's `generate_lead`, and split the reports with `event_source`.
* **Reporting portal purchases as revenue** - forward `payment_success` to GA4's `purchase` with `transaction_id`, `value`, and `currency`, so checkout revenue appears in GA4's monetization reports.
## Before you start
You need a GTM container ID in the format `GTM-XXXXXXX`. Find it on the workspace overview screen in [tagmanager.google.com](https://tagmanager.google.com), next to your container name.
If you want to use Google Analytics 4, configure the GA4 Configuration tag inside your GTM container. 1Club does not accept a standalone GA4 measurement ID - everything goes through GTM.
Google Tag Manager is a **paid-plan** feature. On the Free plan and during the trial you can't save a container ID until you upgrade.
Saving the container ID requires the **settings.manage.organization** permission. Viewing it requires **settings.read.organization**.
## Activate Google Tag Manager
1. Go to **Settings** > **Integrations** and click **Activate** on the Google Tag Manager card, under **Marketing**. The Google Tag Manager page opens.
2. Paste your container ID into **GTM Container ID**. Spaces are trimmed and letters are made uppercase. The ID must be `GTM-` followed by letters or digits.
3. Click **Save**.
A "Google Tag Manager settings saved." message confirms it, and Google Tag Manager moves to **Active Integrations** on the Integrations page. It becomes active only once a container ID is saved.
The container ID is stored on your organization. The next page load on your member portal and CMS website includes the GTM snippet. No redeploy is required.
To change the container, enter the new ID and click **Save**. If you clear the field and save, the snippet stops loading and Google Tag Manager returns to the available integrations.
## Where the tag loads
1Club injects two things on every public page render:
* The standard GTM script (`https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXX`), loaded with Next.js `afterInteractive` strategy so it does not block first paint.
* The noscript iframe fallback for visitors with JavaScript disabled.
Both the member portal (`{org}.portal.1club.ai` or your custom portal domain) and the CMS website (your branded site) read the container ID from your organization settings and load the same container.
## What gets tracked
1Club only loads the container. What it tracks is entirely up to your GTM workspace. Typical setups include:
* Page views on the marketing site and member portal.
* Conversion events for sign-up, booking confirmation, purchase, or contact-form lead.
* Custom dataLayer events you trigger from GTM tags.
### Events pushed by 1Club
The member portal and CMS website push a small set of standard events to `window.dataLayer` automatically. Create a **Custom Event** trigger in GTM with the matching name and forward to GA4 (or any other tag).
| Event name | Fires when | Parameters |
| - | - | - |
| `page_view` | Every client-side route change in portal and website | `page_path`, `page_title` |
| `login` | A member signs in | `event_source` (`portal` or `website`) |
| `sign_up` | A new member account is created | `event_source` |
| `sign_up_pending_confirmation` | A member signs up on the portal and still has to confirm their email | `event_source` |
| `sign_out` | A member logs out of the portal | `event_source` |
| `booking_created` | A booking is confirmed | `booking_id`, `event_source` |
| `payment_success` | An online payment returns to the payment success page | `booking_id` (if any), `invoice_id` (if any), `event_source` |
| `form_submitted` | The contact form on a CMS site submits successfully | `form_type` (always `contact` for the contact form), `form_category` (the category the visitor picked, if any), `form_location` (the page path the form was on), `event_source` |
| `app_download_click` | A visitor clicks an app store badge in the website footer | `store`, `placement`, `event_source` |
All non-`page_view` events also carry `event_source` so you can split GA4 reports by surface.
### Suggested GA4 mapping
For drop-in GA4 coverage, map the events above to GA4's recommended names where they line up, and pass them through as-is otherwise:
* `sign_up` and `login` already match GA4's recommended events - forward with **Event Name** `{{Event}}` and GA4 surfaces them in the standard user-engagement reports.
* `payment_success` maps to GA4's `purchase` if you also pass `transaction_id`, `value`, and `currency`. If you don't have those yet, forward as `payment_success` and report on it as a custom event.
* `form_submitted` is a good fit for GA4's recommended `generate_lead` event. Either forward as `form_submitted` or remap to `generate_lead` in your GA4 Event tag - whichever you prefer for reporting.
## Cookie consent
The GTM snippet runs on every page load. If you operate under GDPR, ePrivacy, or similar regulations, configure consent in your GTM container using Google's Consent Mode and gate marketing or analytics tags on the appropriate consent state. 1Club's cookie banner records the visitor's preferences but does not configure GTM tags for you.
## Worked examples
### Padel gym measuring booking conversions
A padel gym runs Google and Meta ads and wants to know which spend produces court bookings. Save the GTM container ID in 1Club, then in GTM create a **Custom Event** trigger on `booking_created` and forward it to a GA4 event and the Meta Pixel. Because 1Club pushes `booking_created` with `booking_id` and `event_source` automatically, the gym sees booking conversions attributed to campaign and surface without touching any code.
### Yoga studio tracking free-trial sign-ups
A yoga studio's goal is portal sign-ups from its website. In GTM, add a trigger on `sign_up` and remap `form_submitted` to GA4's `generate_lead` for contact-form leads. The `event_source` parameter lets the studio split reports between website leads and portal sign-ups in GA4.
### Gym reporting membership purchases as revenue
A fitness gym forwards `payment_success` to GA4's `purchase`, adding `transaction_id`, `value`, and `currency` in the GA4 Event tag. Revenue from portal checkout then shows up in GA4's monetization reports alongside the `invoice_id` 1Club already passes.
## Tips and best practices
* Keep all tags in GTM, not scattered across tools. 1Club only loads the container; everything else (GA4, pixels, remarketing) lives in your GTM workspace so you can change it without a code change.
* Publish your GTM workspace after every change. 1Club loading the container does nothing until you publish the version that contains your tags and triggers.
* Use the built-in `event_source` parameter to separate portal and website performance in the same GA4 property.
* Test with [Tag Assistant](https://tagassistant.google.com) on both the portal and the CMS website after saving, since they load the same container independently.
* If you operate under GDPR or ePrivacy, wire your marketing and analytics tags to Google Consent Mode in GTM before you start advertising.
## Deactivate
1. Click **Deactivate** at the top of the Google Tag Manager page.
2. Confirm in the **Deactivate Google Tag Manager?** dialog. It warns that the integration stops working and its settings are deleted.
1Club removes the container ID and takes you back to **Settings** > **Integrations**. If the deactivation is refused, the reason shows inside the dialog.
The GTM snippet stops loading on the next page load. Visitors who already have a page open keep the script until they navigate away or refresh.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| Tag not firing at all | The ID format is wrong or the form wasn't saved | Confirm the ID starts with `GTM-` (uppercase, letters and digits only) and that you saved, then check with [Tag Assistant](https://tagassistant.google.com) |
| Container loads but no events in GA4 | The GA4 tag or its triggers aren't set up, or the workspace wasn't published | Confirm the GA4 tag fires on the right triggers in GTM and that you published the latest version |
| Tag loads on portal but not website (or vice versa) | The page is serving a cached HTML response or the public org payload wasn't refreshed | Reload the page once to pick up the latest container ID; both surfaces read the same setting |
| "Enter a container ID like GTM-XXXXXXX." | The container ID doesn't match `GTM-` followed by letters and digits | Copy the exact ID from the GTM workspace overview |
| Save blocked with a plan message | Your organization is on the Free plan or still in the trial | Upgrade the plan, then save |
## Related
* [Integrations](/settings/integrations) - the full integration catalog and how activation works.
* [Website](/marketing/website) - the CMS website surface where the container loads.
# Hype
Source: https://docs.1club.ai/settings/integrations/hype
Connect the Hype POS through the Hype Exchange Service to fiscalize the sales you take in 1Club, print them on Hype's kitchen and counter screens, and sync your Hype menu (categories and products) back into 1Club
Hype is a point-of-sale and kitchen system. The Hype integration links 1Club to your Hype account through the Hype Exchange Service (HES). You take payment in 1Club as usual, and every paid order is sent to Hype for a compliant fiscal receipt and the kitchen display. Your Hype menu (categories and products) syncs back into 1Club. An owner or admin activates it once, with an API token from Hype support.
## When to use it
Hype is for gyms that run a cafe, bar, or kitchen alongside bookings and memberships and need one fiscalization path for everything they sell. Every 1Club order line - bookings, memberships, signup fees, and products - is billed against one **Service item** in Hype's catalog, so Hype can fiscalize it without a matching catalog item. The receipt still prints the real line name.
Typical uses:
* **A cafe next to the courts** - court bookings and cafe sales both go to Hype for compliant receipts, and the cafe's drinks and snacks sync into 1Club as products.
* **One desk for passes and coffee** - staff ring up a day pass and a coffee in 1Club and take the payment by cash or card reader. Hype receives the sale already paid, with the payment methods that paid it, and fiscalizes it.
* **Memberships on the same fiscal receipts as the shop** - a yearly membership and its signup fee fiscalize through the service item, and the receipt prints "Membership - Yearly plan" rather than the placeholder name.
## Before you start
* Hype is a **paid-plan** feature. On the Free plan and during the trial, its **Activate** and **Configure** buttons stay greyed out until you upgrade.
* Configuring Hype requires the **settings.manage.organization** permission. Viewing its settings and status requires **settings.read.organization**.
* You need a Hype account and an integration provisioned by **Hype support**. When they provision it, they give you an **API token** for the Hype Exchange Service. Without a provisioned integration there is nothing for the token to link to.
Hype support starts the initial sync when they provision the integration. You
can't start or restart it from 1Club. 1Club only works through the tasks Hype
queues and shows the status Hype reports.
## Activate Hype
1. Go to **Settings** > **Integrations** and click **Activate** on the **Hype POS** card, under **Tools**. The **Hype POS** page opens.
2. Enter your **API token** - the token Hype support gave you.
3. Leave **HES base URL** at its default, `https://es.hype-software.com` (production). Only change it if Hype told you to use another host on `hype-software.com`, such as the dev environment `https://es.dev.hype-software.com`.
4. Click **Save**.
5. Click **Test connection**. 1Club reads the status Hype reports for your integration and shows it on the status chip. Automatic syncing runs while the status is **Syncing** or **Active**.
The API token is stored encrypted and is never shown back to you. Once a token is saved, the field shows a masked placeholder, and leaving it blank when you save keeps the current token.
## Configuration fields
| Field | Notes |
| - | - |
| **API token** | The Hype Exchange Service token Hype support gives you when they provision the integration. Stored encrypted. Leave it blank when you save to keep the current token. |
| **HES base URL** | Defaults to `https://es.hype-software.com`. Must be an `https://` address on `hype-software.com`. Use `https://es.dev.hype-software.com` only if Hype directs you to the dev environment. |
| **Service item** | The Hype product every 1Club sale is recorded under for fiscal receipts. You can only pick it once products have synced from Hype. See [Set the service item](#set-the-service-item). |
| **Send open tabs to Hype as they grow** | Off by default. Sends each round on an open tab to Hype as it is added. See [Send open tabs to Hype as they grow](#send-open-tabs-to-hype-as-they-grow). |
Changes to these fields take effect when you click **Save**.
## Connection status
The status chip on the **Hype POS Configuration** card shows what Hype reports:
* **Not activated** - Hype is not activated yet.
* **Inactive** - activated, but Hype reports the integration isn't running, or 1Club hasn't read its status yet. Click **Test connection** to read it.
* **Syncing** - Hype's initial sync is running. While it runs, the page refreshes on its own every couple of seconds.
* **Active** - Hype reports the integration is ready. Paid orders go to Hype only while the status is **Active**.
* **Error** - Hype aborted the sync or reported an error, or Hype rejected the API token. The most recent error is shown under the form, and syncing pauses. If the token was rejected, enter a new one and click **Save** to resume.
The last successful sync is shown under the form as **Last sync**.
## Test connection
**Test connection** checks that your saved token works against the HES base URL and updates the status chip with what Hype reports. A toast says **Token works** or **Token rejected by Hype**, and the **Hype status** panel shows, for each integration Hype has linked to the token:
* **Integration** - the Hype-side integration ID.
* **Status** - Hype's lifecycle status for it.
* **Pipeline** - the sync pipeline status and, where available, the step (for example "step 4/11").
* **Failed queue jobs** - any jobs Hype couldn't process, with the entity, method, and Hype's error message. These usually point at the exact missing mapping.
If the token works but no integration is linked yet, the panel says so. Only Hype can fix this: contact Hype support and ask them to provision an integration for your account.
If the token is rejected, the status changes to **Error** and syncing pauses until you save a new token. If the token now belongs to a different Hype integration than before, 1Club clears the **Service item**, because it belongs to the old catalog. Pick it again.
## Sync now
Hype drives the sync by queueing tasks (create this category, create this product, this order changed) that 1Club works through. 1Club checks the queues every 15 seconds while the status is **Syncing** or **Active**. **Sync now** works through every queue straight away and then refreshes the status from Hype. Use it after Hype support tells you provisioning has advanced, or when you want new menu items to appear without waiting.
## What syncs
**From Hype into 1Club (the menu):**
* **Categories** sync into 1Club product categories.
* **Products** sync into 1Club products.
* Both appear in the **Synced mappings** panel, on the **Categories** and **Products** tabs, each with the time it synced.
**From 1Club to Hype (sales):**
* An order goes to Hype once it is closed and fully paid. Each line - product, booking, membership, or signup fee - references your **Service item**, but carries the real name of what was sold (for example "Membership - Yearly plan" or "Booking - Court 2") and its price including tax, so the Hype receipt shows what was actually sold.
* The order also carries the payment methods that paid it. 1Club shares its payment methods with Hype during the sync, and Hype matches each one to its own payment types.
* Discounts on the order go to Hype as an order-level discount.
* If you cancel an order in 1Club that Hype already received, 1Club marks it **Cancelled** in Hype.
* If sending a paid order fails, for example during a Hype outage, 1Club tries again for paid orders closed in the last hour.
**From Hype back to 1Club:**
* Order status changes in Hype don't change anything in 1Club. The money is collected in 1Club, so 1Club stays the record of what was paid.
* If Hype cancels a sale, the payment in 1Club is left as it is. If the sale is void, refund it in 1Club.
## Set the service item
Hype fiscalizes against its own catalog, but 1Club sells bookings, memberships, and products that don't exist in Hype's catalog. The **Service item** solves this. It is a placeholder product in Hype that every 1Club sale is recorded under for the fiscal receipt. Hype still receives the real line name and price, so the receipt shows what was actually sold.
1. Make sure products have synced from Hype. If the picker is empty and greyed out, click **Sync now**.
2. Choose the placeholder product in the **Service item** dropdown.
3. Click **Save**.
### If you have no placeholder product yet
The picker only offers products that exist in Hype. If none of them is a good placeholder, or nothing has synced yet:
1. In Hype's POS, add a product to use as the placeholder (for example "1Club sale").
2. On the **Hype POS** page in 1Club, click **Sync now** so the new product syncs.
3. Pick it in the **Service item** dropdown and click **Save**.
Until a service item is set, no order reaches Hype. That means no fiscal
receipt and no kitchen ticket, even while the status reads **Active**. The
page shows an "Orders are not being sent to Hype" warning until you pick one
and save. 1Club only catches up on paid orders from the last hour, so older
orders never reach Hype.
## Send open tabs to Hype as they grow
By default, a tab rung up at the till reaches Hype once, when it is closed and paid. Turn on **Send open tabs to Hype as they grow** and click **Save** to send each round to Hype as soon as it is added, so the kitchen sees it straight away. Other open orders, such as an online checkout waiting for its card payment, still go to Hype only when they are closed and paid.
Before you turn this on, open Hype's back office and match Hype's accepted
order status to 1Club's **Accepted** status. 1Club relies on that status to
know when Hype stops accepting changes to an order, so it can send later
rounds as a new order instead.
## Deactivate
1. On the **Hype POS** page, click **Deactivate** at the top of the page.
2. Confirm in the **Deactivate Hype POS?** dialog. It warns that the integration stops working and its settings are deleted.
1Club removes the API token and settings, and the links between your synced categories and products and their Hype items. The categories and products themselves stay in 1Club. You return to **Settings** > **Integrations**, and Hype moves back to the available integrations. If deactivation fails, the reason is shown inside the dialog.
## Worked examples
**Padel gym with a cafe.** A padel gym runs a cafe next to its courts. Hype support provisions the integration and sends an API token. The gym activates Hype, saves the token, clicks **Test connection**, and waits for the menu to sync. It clicks **Sync now** to speed it up, and its drinks and snacks appear under **Synced mappings** > **Products**. It picks one of those products as the **Service item**. Court bookings and cafe sales now both go to Hype for compliant receipts.
**Climbing gym with one desk.** A climbing gym sells day passes and coffee at one desk. Staff add both to an order in 1Club and take the payment on the card reader. When the order closes paid, Hype receives it with the card payment attached and fiscalizes it. There is no second step in Hype.
**Martial-arts gym selling memberships through Hype.** A jiu-jitsu gym wants membership signups on the same fiscal receipts as its shop. Because every line routes through the **Service item**, the yearly membership and the signup fee both fiscalize, and the Hype receipt still prints "Membership - Yearly plan" rather than the placeholder name.
## Tips and best practices
* Set the **Service item** as soon as products have synced. Until one is chosen, orders never reach Hype.
* Use **Test connection** to read Hype's pipeline step and failed jobs before assuming a sync is stuck. The error messages point at the exact missing mapping.
* Click **Sync now** after Hype support tells you provisioning advanced, rather than waiting for the next automatic check.
* Leave the **API token** field blank when you edit other settings. Only paste a value when you are deliberately replacing the token.
* Readiness is whatever Hype reports. If the status shows **Inactive** or **Syncing** for a long time, that is Hype's pipeline, not a 1Club setting you can change.
## Troubleshooting
* **The Service item dropdown is empty** - products haven't synced from Hype yet. Click **Sync now**, or add a placeholder product in Hype's POS and sync again (see [If you have no placeholder product yet](#if-you-have-no-placeholder-product-yet)).
* **Orders aren't appearing in Hype** - check that a **Service item** is set, the status is **Active**, and the order is closed and fully paid. Open orders wait until they close, unless they are till tabs and **Send open tabs to Hype as they grow** is on.
* **The status stays Inactive after you saved the token** - click **Test connection** so 1Club reads the status from Hype. If Hype still reports it inactive, contact Hype support.
* **The status shows Error** - read the error under the form. If Hype rejected the token, enter a new one and click **Save**.
* **Test connection says the token works but no integration is linked** - the token works, but Hype hasn't provisioned an integration for it. Contact Hype support.
* **The status is stuck on Syncing** - this reflects Hype's provisioning pipeline. Use **Test connection** to read the pipeline step and any failed queue jobs, and send those details to Hype support.
* **The Service item was cleared** - the token now belongs to a different Hype integration. Pick the placeholder product from the new catalog and click **Save**.
* **A sale was cancelled in Hype** - the payment in 1Club is unchanged. If the sale is void, refund it in 1Club.
## Related
* [Integrations](/settings/integrations) - the full integration catalog and how activation works.
* [Orders](/sales/orders) - the orders that go to Hype for fiscalization.
* [Products](/sales/products) - where the categories and products synced from Hype appear.
* [Payments](/sales/payments) - taking payment and refunding it in 1Club.
* [Payment methods](/settings/payment-methods) - the payment methods 1Club shares with Hype.
# Meta
Source: https://docs.1club.ai/settings/integrations/meta
Connect your Meta Business account so your Facebook and Instagram ads are measured on the bookings and revenue they actually produce, not just clicks.
The Meta integration connects your gym's Meta Business account to 1Club, so bookings and sales are reported back to Facebook and Instagram as conversions. It is for the owner, or whoever runs your Facebook and Instagram ads. You connect it in one step through Meta Business Login, Meta's own sign-in popup, where you pick your business, your dataset and your ad account - there is no pixel ID to hunt down, no access token to copy out of Events Manager, and no tag manager.
## Why connect Meta
Meta on its own reports how many people clicked. With 1Club connected, it also sees how many of them booked and what they paid, so your ad results show bookings and revenue, and Meta can find more people like the ones who bought.
The **Meta pixel** records page views and checkouts on your portal and website, and 1Club's servers report the bookings and sales themselves, with the real amount. So conversions still count for visitors who block tracking scripts, and sales a browser never sees are reported too, such as a pass sold at the front desk.
Typical uses:
* **Putting ad spend where the bookings are** - a padel gym running Instagram ads for off-peak courts sees bookings and revenue per ad set instead of link clicks, and shifts budget to the ad set that produced the bookings rather than the one with the cheapest clicks.
* **Advertising for the first time** - a yoga studio with no dataset creates one during setup, and can see from its first ad which ad brought people who actually bought a class pass.
* **Seeing where the sale actually closes** - a gym that takes most bookings at the desk sees online bookings reported as website conversions and desk bookings as in-person ones, and points its ads at a free trial rather than an online checkout.
## What a dataset is
Meta calls the thing that collects your conversions a **dataset**. You may know it by its old name, the **pixel** - they are the same thing, and Meta renamed it. One dataset holds all the activity for one business: page views, bookings, purchases.
If you have ever run a Facebook or Instagram ad with a website, you almost certainly already have one. If you have never advertised, you won't, and 1Club can create it for you during setup.
## Before you start
* Meta is a **paid-plan** feature. On the Free plan and during the trial, its **Activate** and **Configure** buttons stay greyed out until you upgrade.
* Connecting and changing the settings requires the **settings.manage.organization** permission. Viewing them requires **settings.read.organization**.
* You need a Meta Business account, and permission inside it to manage the ad account and dataset you want to use. If someone else runs your ads, they may need to do this step or grant you access first.
The Meta connection must be enabled on the 1Club server for your deployment.
If it isn't, the page shows "Meta isn't available on this server yet." instead
of the connect button. Contact 1Club support if you expect it to be available.
## Connect Meta
1. Go to **Settings** > **Integrations** and click **Activate** on the **Meta** card, under **Marketing**. The **Meta** page opens.
2. Under **Connect Meta**, click **Connect with Meta**.
3. A Meta popup opens. Sign in, then choose the business, ad account, dataset and Facebook Page you want to share with 1Club, and confirm.
4. When the popup finishes, 1Club stores the connection, shows a **Meta connected** message, and opens the **Meta assets** card with what you shared.
5. For each kind of asset you shared exactly one of, 1Club selects it for you. If a dataset is selected, the status becomes **Active** straight away. If you shared several, pick the one you want in **Meta assets**.
Until a dataset is selected, the page shows "Pick a dataset to start. Nothing is tracked until you do." and the status stays **Inactive**.
If you close the popup or decline, you see **Connection cancelled** and
nothing is saved. If Meta reports a problem, you see **Couldn't connect Meta**
with the reason. Click **Connect with Meta** again to retry.
### If you have no dataset yet
When the business you connected has no dataset, the **Dataset (pixel)** picker is greyed out and a **New dataset name** box appears below it. Type a name - your gym's name is the usual choice, and it is what you will see in Meta Events Manager - and click **Create dataset**. 1Club creates it inside your business and selects it.
## Connection status
The chip on the **Connect Meta** card shows one of:
* **Not activated** - no Meta connection exists yet.
* **Inactive** - a connection exists but isn't reporting. Either no dataset is selected yet, or access was removed on Meta's side. Pick a dataset, or click **Reconnect**.
* **Active** - a dataset is selected and conversions are being reported.
Once connected, the button changes to **Reconnect**, which reruns the Meta popup so you can share more assets or refresh access. Reconnecting keeps your dataset and ad account choices if you still share them, and keeps your switch settings. 1Club also checks the connection every day, and sets the status to **Inactive** if Meta no longer accepts it.
## Meta assets
The **Meta assets** card shows your business name and lets you change what the connection points at. You can only pick from what you shared in the popup. If something is missing from a list, reconnect and share it.
* **Dataset (pixel)** - where your page views, bookings, and purchases are reported. Nothing is tracked without one.
* **Ad account** - used for audiences and campaigns. Not needed for tracking, but worth selecting now so you don't have to reconnect later.
* **Facebook Page** - the Page your ads run from.
Each picker has a **None** option to clear a choice. A choice is saved as soon as you make it, and a **Meta settings saved** message confirms it.
**Refresh assets**, next to **Reconnect**, reads the lists from Meta again. Use it after you create a dataset or ad account on Meta's side, or if you think the connection was changed there. If a selected dataset or ad account is no longer shared, 1Club clears it.
## How conversions are reported
1Club reports in two halves, and each does the part it is good at.
**From the browser - the Meta pixel.** Loads on your member portal and website and records:
* **Page view** - every page a visitor opens.
* **Reaching checkout** - a visitor starting to pay for a booking.
**From 1Club's servers - the Conversions API.** Reports what actually happened:
* **Booking** - when a booking is confirmed, with the amount charged. A booking held while the member pays is reported only once the payment goes through.
* **Purchase** - when an order is completed, with the order total.
This split matters. The pixel is blocked for many visitors, and it never knows what anything cost. Reporting bookings and purchases from 1Club's servers means those conversions still count, carry the real revenue, and include sales that never touched a browser, such as a pass rung up at the desk.
Because the two halves report different things, you won't see bookings twice.
Page views and checkouts come from the browser, and bookings and purchases
come from 1Club.
### Online and in-person sales are labelled differently
Meta asks every conversion where it happened, and requires the answer to be accurate. 1Club labels each one honestly:
* A booking made in the member portal, on your website, in the mobile app, or through the marketplace is reported as a **website** conversion.
* A booking taken at the front desk or on a kiosk is reported as an **in-person** conversion.
* A purchase takes the label of its bookings. A sale with no booking, such as a membership or a product rung up at the till, is reported as **other**, because 1Club can't tell where it happened. So is a booking that came in through the API or another integration.
This is deliberate. Reporting till sales as website conversions would inflate your online ad results and teach Meta to optimize toward the wrong thing.
## Cookie consent
The Meta pixel is advertising tracking, so it is covered by your cookie banner. It **only loads for visitors who accept marketing cookies**, and it never loads before they answer. If a visitor accepts partway through their visit, the pixel starts on that page.
The **Marketing** category on your banner controls this. Analytics consent is separate - a visitor can allow you to measure site usage while still refusing ad tracking, and 1Club honors that.
Server-side reporting is a different matter under privacy rules and isn't governed by the browser banner. Turn it off with the **Conversions API** switch if your policy requires it.
## Switching tracking on and off
Two independent switches in the **Meta assets** card, both on by default. A change is saved as soon as you flip the switch.
* **Browser pixel** - loads the Meta pixel on your portal and website. Turn it off to stop all browser-side tracking while keeping server-side reporting.
* **Conversions API** - reports bookings and purchases from 1Club's servers. Turn it off to stop server-side reporting while keeping the pixel.
Turning both off keeps the connection but reports nothing.
## Testing your setup
Meta Events Manager has a **Test events** tool that shows conversions arriving in real time.
1. In Meta Events Manager, open your dataset and go to **Test events**. Copy the test code it shows (it looks like `TEST12345`).
2. Paste it into **Test event code** on the 1Club **Meta** page, then click outside the field. The code is saved when you leave the field.
3. With **Conversions API** on, make a test booking. It appears in Meta's Test events view within a few seconds.
4. **Clear the field when you are done**, and click outside it to save. It is only for testing.
## Deactivate
1. On the **Meta** page, click **Deactivate** at the top of the page.
2. Confirm in the **Deactivate Meta?** dialog. It warns that the integration stops working and its settings are deleted.
1Club stops reporting conversions, the pixel stops loading on your portal and website, and 1Club hands its access back to Meta. You return to **Settings** > **Integrations**. If deactivation fails, the reason is shown inside the dialog.
Your dataset and its history stay in your Meta account. Deactivating removes 1Club's access, not your data.
## Worked examples
**Padel gym running Instagram ads for off-peak courts.** The gym connects Meta and selects the dataset it already used for a previous website. Within a week, its Instagram campaign in Ads Manager shows 34 bookings worth 1,190 BGN instead of just "412 link clicks". It shifts budget to the ad set that produced the bookings rather than the one with the cheapest clicks.
**Yoga studio that has never advertised.** The owner connects Meta, finds the dataset list empty, and uses **Create dataset** with the studio's name. She runs her first Instagram ad the following week and can see from day one which ad brought people who actually bought a class pass.
**Gym taking most bookings at the desk.** The gym connects Meta and sees online bookings reported as website conversions and desk bookings as in-person ones. The split makes it obvious that ads drive discovery, but the sale closes in the building - so it starts pointing ads at a free trial rather than an online checkout.
## Tips and best practices
* **Select the ad account when you connect**, even if you aren't running ads yet. It costs nothing and saves reconnecting later.
* **Give it a few days.** Meta needs a number of conversions before it can optimize on them. Don't judge a campaign on its first day of accurate data.
* **Leave the Conversions API on.** It is the half that keeps working when the pixel is blocked, and the only one that sees front-desk sales.
* **Use one dataset per business.** Splitting your bookings across several datasets divides the signal and makes every campaign learn more slowly.
* **Clear the test event code** once you have finished testing.
## Troubleshooting
* **"Meta isn't available on this server yet."** - the Meta connection isn't enabled for your deployment. Contact 1Club support.
* **The Activate button is greyed out** - Meta is a paid-plan feature. Upgrade from the Free plan or the trial.
* **"Meta connect isn't ready yet"** - the Meta popup hasn't finished loading. Wait a moment and click **Connect with Meta** again.
* **"Pick a dataset to start. Nothing is tracked until you do."** - you connected but no dataset is selected. Pick one in **Meta assets**, or create one.
* **The dataset list is empty** - the business you shared has no dataset. Use **Create dataset**, or reconnect and share a business that has one.
* **"Reconnect Meta before creating a dataset."** - 1Club doesn't know which business to create it in, usually because you shared several. Click **Reconnect** and share only the business you want.
* **The ad account or Page list is empty** - you didn't share them in the popup. Click **Reconnect** and select them this time.
* **"That Meta asset was not shared with 1club"** - the item you picked isn't shared any more. Click **Refresh assets**, or reconnect and share it again.
* **"Your Meta connection was revoked"**, or the status went to **Inactive** on its own - access was removed on Meta's side, or Meta no longer accepts the connection. Click **Reconnect**.
* **No conversions in Events Manager** - check that the status is **Active**, a dataset is selected, and the **Conversions API** switch is on. Use the test event code to watch events arrive live.
* **Page views are missing but bookings appear** - that is the cookie banner doing its job. Visitors who decline marketing cookies get no pixel, so browser events are always lower than server events.
## Related
* [Integrations](/settings/integrations) - the full integration catalog and how activation works.
* [Social booking](/settings/integrations/social-booking) - tagged "Book now" links that show which social platforms drive bookings.
* [Google Tag Manager](/settings/integrations/google-tag-manager) - load your own analytics tags on the portal and website.
* [Automations](/marketing/automations) - follow up with the members your ads bring in.
# myPOS
Source: https://docs.1club.ai/settings/integrations/mypos
Accept card payments through myPOS hosted checkout and in-person myPOS terminals, with a shared sandbox test mode and a live mode backed by your own myPOS Checkout Store
myPOS is a card payment provider you can use alongside or instead of Stripe. Online, customers are redirected to myPOS's hosted checkout page to enter their card details, then return to 1Club once the payment completes. In person, myPOS card terminals take payments at your front desk. An owner or admin sets it up, and staff can also email a member a myPOS payment link for an outstanding charge.
If your organization trades through more than one company, each company
connects its own myPOS account. Pick the company under **Billing entity** at
the top of the myPOS page before you save or deactivate anything. The line
under the picker reminds you that payments for that company's gyms settle to
the account you are setting up. The picker only appears when you have more
than one billing entity. See [Billing entities](/settings/billing-entities).
## When to use it
myPOS suits a gym that already has, or wants, a myPOS merchant account. Card data never touches 1Club servers. All processing happens on myPOS's PCI-certified infrastructure, and the payment status returns automatically through a signed myPOS notification (IPN), settling linked charges and bookings.
You can try hosted checkout end to end in **Test** mode against a shared sandbox without any credentials, then switch to **Live** mode with your own myPOS Checkout Store when you are ready to take real money.
Typical uses:
* **A gym that already banks with myPOS** - paste a **Configuration Pack** from your Checkout Store and take court bookings through hosted checkout, with no Stripe account needed.
* **Collecting an overdue fee** - when a member's card on file is declined, send a **myPOS payment link** for the overdue charge, and the member pays on their own phone within the 7-day window.
* **A myPOS terminal at the front desk** - take card payments in person on a myPOS device, while your online payments keep running through Stripe.
## Before you start
* myPOS is a **paid-plan** integration. On the Free plan and during the free trial, **Activate** is greyed out until you upgrade.
* Configuring myPOS requires the **settings.manage.organization** permission. Viewing its settings requires **settings.read.organization**.
* **Test** mode needs no credentials. 1Club ships shared sandbox credentials, so you can run the full hosted checkout with myPOS test cards before signing up for anything.
* **Live** hosted checkout needs a myPOS merchant account with an **Online (Checkout) Store**. You configure 1Club with credentials from that store. See [Where to get your myPOS credentials](#where-to-get-your-mypos-credentials) below.
* Each company can run only one hosted-checkout provider at a time. If DatecsPay is active for the company, deactivate it first, or use myPOS for terminals only.
## Where to get your myPOS credentials
Live credentials come from your **myPOS account**, not from 1Club. In the myPOS dashboard you create an **Online Store** (also called a **Checkout Store**), and that store is where every value below originates. If you don't have a store yet, follow myPOS's [Create a Checkout Store](https://developers.mypos.com/online-payments/initial-setup-and-testing/create-checkout-store) guide first.
There are two ways to supply the credentials to 1Club. Use the Configuration Pack if your myPOS store offers it. It is faster and avoids copy-paste mistakes.
### Option A - Configuration Pack (recommended)
myPOS **Easy Setup** generates a single Base64 string called a **Configuration Pack** that bundles all five credentials together. In your myPOS Checkout Store, open the integration / **Easy Setup** screen, generate the pack, and copy the whole string. You paste it into one field in 1Club and it fills in everything else, so you don't need the individual values below.
### Option B - manual credentials
If you prefer (or your store predates Easy Setup), enter the five values by hand. All of them come from your Checkout Store in the myPOS dashboard:
| Field | What it is | Where to find it in myPOS |
| - | - | - |
| **Store ID (SID)** | The numeric identifier of your Checkout Store. | General information for your Checkout Store. |
| **Client number (Wallet number)** | The myPOS wallet that receives the funds. | Your myPOS account / wallet details. |
| **Key index** | The index of the signing key pair (a positive number, usually `1`). | Shown next to the key pair when you generate it. |
| **RSA private key (PEM)** | The private half of your signing key pair, used to sign requests to myPOS. | Generated in myPOS and **shown only once** - store it securely. |
| **myPOS public certificate (PEM)** | The certificate 1Club uses to verify notifications coming back from myPOS. It is unique to this Checkout Store. | Download it from your **Checkout Store's Integration tab**. |
If your private key file is protected with a passphrase, also enter it in **Private key passphrase**. 1Club uses it once to decrypt the key and never stores it.
The private key is displayed only once when you generate the key pair in
myPOS. If you lose it, generate a new key pair (which produces a new key index
and certificate) and update all three values here.
For full details on stores, keys, and certificates, see the [myPOS Developers portal](https://developers.mypos.com/).
### Terminal credentials
The Checkout Store credentials above cover hosted checkout only. In-person card terminals need a separate pair of credentials, because 1Club sends terminal charges to your device through the **myPOS API Gateway**. To get them, create an application in your myPOS account and copy its **Merchant client ID** and **Merchant client secret**. You only need these if you turn on the **Terminal** capability (see [Capabilities](#capabilities)).
## Activate myPOS
1. Go to **Settings** > **Integrations**.
2. Under **Payments**, click **Activate** next to myPOS. 1Club opens the myPOS page. Nothing is saved until you click **Save**.
3. Choose a **Mode**:
* **Test** - uses the shared myPOS sandbox. No credentials needed.
* **Live** - uses your own myPOS Checkout Store.
4. Under **Capabilities**, leave **Online** ticked for hosted checkout and choose a **Checkout form**.
5. In **Live** mode, the credential fields appear under **Online**. Supply your credentials one of two ways:
* Paste your Configuration Pack into **Configuration Pack (recommended)**. This fills in all five values and disables the manual fields.
* Or, under **Or enter credentials manually**, fill in **Store ID (SID)**, **Client number (Wallet number)**, **Key index**, **RSA private key (PEM)**, and **myPOS public certificate (PEM)**. Fill in **Private key passphrase** only if your key file has one.
6. To take in-person payments, tick **Terminal** and enter your **Merchant client ID** and **Merchant client secret**.
7. Click **Save**. The page shows **Active** and the mode, **Test** or **Live**, next to the title.
8. Click **Validate configuration** to check your settings (see [Validate configuration](#validate-configuration)). The button is available once myPOS is saved.
The private key, public certificate, and merchant client secret are stored
encrypted and never shown back to you. When you edit the configuration later,
leave them blank to keep the existing values. Only paste a value if you are
replacing it.
## Configuration fields
| Field | Shown when | Notes |
| - | - | - |
| **Mode** | Always | **Test** or **Live**. |
| **Capabilities** | Always | **Online** and **Terminal**. See [Capabilities](#capabilities). |
| **Checkout form** | **Online** is on | **Minimal - card details only**, or **Full - collect name, address & contact details**. Defaults to **Minimal**. |
| **Configuration Pack (recommended)** | **Live** and **Online** | Base64 string from myPOS Easy Setup. Supplies all five credentials and disables the manual fields. |
| **Store ID (SID)** | **Live** and **Online** | Your Checkout Store identifier. |
| **Client number (Wallet number)** | **Live** and **Online** | The wallet that receives funds. |
| **Key index** | **Live** and **Online** | Positive integer; the index of your signing key pair. |
| **RSA private key (PEM)** | **Live** and **Online** | Stored encrypted. Leave blank when editing to keep the current key. |
| **Private key passphrase** | **Live** and **Online** | Only if your key file is passphrase-protected. Used once to decrypt the key and never stored. |
| **myPOS public certificate (PEM)** | **Live** and **Online** | Downloaded from your Checkout Store's Integration tab. Unique to that store. Leave blank when editing to keep the current certificate. |
| **Merchant client ID** | **Terminal** is on | From the application you created in your myPOS account. |
| **Merchant client secret** | **Terminal** is on | Stored encrypted and never shown again. Leave blank when editing to keep the saved secret. |
## Test mode vs. live mode
* **Test** uses shared myPOS sandbox credentials. No real money moves and no Configuration Pack is needed. Use myPOS [test cards](https://developers.mypos.com/online-payments/initial-setup-and-testing/testing-in-sandbox/test-data) to simulate payments.
* **Live** uses your own myPOS Checkout Store. Real payments are processed and settled to your wallet.
Saving in **Test** mode discards your live Checkout Store credentials. When you switch back to **Live**, paste your Configuration Pack, or the manual credentials, again. Your terminal credentials are kept either way.
While a payment is still in progress on a myPOS card reader, 1Club refuses to change the mode or the terminal credentials. Finish or cancel the payment on the reader, then save again.
## Validate configuration
The **Validate configuration** button checks that your saved settings are complete and well-formed: the private key is valid, the certificate is present, and the required server settings exist. It records the time of the last successful validation. On success it shows "myPOS configuration is complete."
For hosted checkout, validation runs locally and does **not** contact myPOS or
move any money. It catches a malformed key or a missing field, but it can't
confirm that myPOS recognizes your SID or wallet. The first real proof that
live credentials are correct is a successful test payment through hosted
checkout.
When **Terminal** is on, validation also checks your **Merchant client ID** and **Merchant client secret** with a real call to myPOS. On success it shows how many terminals myPOS has on the account. This call counts toward the same refusal limit as terminal payments, so validate only after you have double-checked the credentials (see [Terminal credential lockout](#terminal-credential-lockout)).
## Terminal credential lockout
myPOS locks your myPOS account after **three refused attempts** with the same terminal credentials. Only myPOS can unlock it. To make sure 1Club can never be the cause, 1Club stops trying after **two** refusals.
When that happens, the myPOS page shows a warning that myPOS refused these credentials and one more refusal locks your account. 1Club makes no further terminal attempts with those credentials. Each **Validate configuration** run with **Terminal** on counts as an attempt, the same as a payment.
To recover:
1. Check the **Merchant client ID** and **Merchant client secret** against the application in your myPOS account.
2. If they were wrong, correct them and click **Save**. Saving clears the counter, so the next attempt goes ahead.
3. If they were already right, contact myPOS first and ask them to confirm your account is not locked.
4. Only after myPOS confirms the account is fine, click **Allow another attempt** on the warning and confirm.
**Allow another attempt** clears the counter, and so does saving the same
credentials again. If myPOS refuses the credentials again, that attempt could
be the third refusal that locks your myPOS account. Never use either to "try
again and see".
## Capabilities
The **Capabilities** section controls which payment surfaces the integration serves:
* **Online - hosted checkout for members on the web** - hosted checkout for the portal, kiosk, and admin-initiated charges. On by default. Under it, **Checkout form** sets how much the myPOS checkout page asks for, and in **Live** mode your Checkout Store credentials go here.
* **Terminal - in-person payment terminals at clubs** - myPOS card terminals at your gyms. Off by default. Turning it on reveals the **Merchant client ID** and **Merchant client secret** fields (see [Terminal credentials](#terminal-credentials)). Add and manage the devices themselves under **Settings** > **Sales** > **Payment terminals**, which the **Manage** button opens, alongside any other providers you use.
You can untick **Online** and use myPOS for terminals only. A terminal-only setup needs no Checkout Store credentials, even in **Live** mode, and can run next to Stripe or DatecsPay taking your online payments.
### One hosted-checkout provider per company
myPOS **Online** and DatecsPay present the same "pay by card" checkout to members, so only one of them can be active for a company. Saving myPOS with **Online** on while DatecsPay is active for that company fails with "DatecsPay is already enabled". Deactivate DatecsPay for that company first. Stripe is not affected by this rule.
## Send a myPOS payment link
For an outstanding charge, you can send the member a link to pay through myPOS instead of charging a saved card.
1. Go to **Sales** > **Finance** > **Charges** and find the charge, or open it.
2. From its actions, choose **Send myPOS payment link**. This is available only while myPOS is active and the charge is still payable (pending, overdue, or partially paid).
3. Check the amount and the member's name and email, then click **Send link**.
The member receives an email with a link that opens myPOS hosted checkout for the exact amount due. Each link is valid for **7 days**. Sending payment links requires the **billing.manage.organization** permission.
## What syncs
* Payments created in 1Club open a myPOS hosted checkout URL that the customer is redirected to.
* After payment, myPOS notifies 1Club through a signed webhook (IPN). The payment status, myPOS transaction reference, and order ID are recorded on the 1Club payment, and any linked charges and bookings are settled automatically.
* If the IPN is redelivered, 1Club matches it to the same payment, so duplicate notifications don't double-charge or double-settle.
## Refunds
1Club does not push refunds to myPOS. Refund a myPOS payment from your **myPOS account**, then reconcile the charge in 1Club.
## Deactivate
1. Pick the company under **Billing entity**, if you have more than one.
2. Click **Deactivate** at the top of the myPOS page.
3. Confirm in the **Deactivate myPOS?** dialog. It says myPOS stops taking payments, and that hosted checkouts already open keep working until they expire. With more than one billing entity, it also says the change applies to the selected company only.
You stay on the myPOS page afterwards, with the form reset. Your saved credentials for that company are cleared, so activating again means entering them again.
1Club refuses to deactivate myPOS while a payment is still in progress on a myPOS card reader, because the payment couldn't be finished without the credentials. The reason shows in the dialog. Finish or cancel the payment on the reader, then deactivate.
## Troubleshooting
* **Activate is greyed out** - your organization is on the Free plan or the free trial. Upgrade to a paid plan to activate payments.
* **Validation fails** - confirm the **Store ID (SID)**, **Client number**, and **Key index** match your myPOS Checkout Store, and that the **RSA private key** is the one paired with the certificate for that key index. If you used a Configuration Pack, regenerate it in myPOS and paste the fresh pack.
* **"DatecsPay is already enabled"** - only one hosted-checkout provider can be active per company. Deactivate DatecsPay for that company, or untick **Online** if you only need myPOS terminals.
* **Customers see a signature error on the checkout page** - the public certificate registered with myPOS doesn't match the private key on file. Regenerate the key pair in myPOS, download the new public certificate from the Integration tab, and paste the new private key and certificate here.
* **Payment succeeded on myPOS but the charge stays unpaid in 1Club** - the IPN webhook didn't reach 1Club. Confirm the notification URL is allowed in your myPOS store settings, then re-trigger the notification from myPOS.
* **myPOS refused the terminal credentials and 1Club stopped trying** - see [Terminal credential lockout](#terminal-credential-lockout). Fix the client ID or secret, and use **Allow another attempt** only after myPOS confirms your account is not locked.
* **Save or Deactivate is refused because a payment is in progress on a card reader** - finish or cancel that payment on the reader, then try again.
* **Live payments fail but test mode works** - double-check you are in **Live** mode and that you entered live credentials (a Configuration Pack from your real store, not the sandbox).
## Worked examples
**Squash gym that already banks with myPOS.** A racket-sports gym with an existing myPOS merchant account activates myPOS, selects **Live**, opens its Checkout Store's **Easy Setup** screen, generates a **Configuration Pack**, and pastes it into 1Club. It saves, validates the configuration, then runs one real court booking to confirm hosted checkout settles the charge. No Stripe account needed.
**Boxing gym collecting an overdue membership fee.** A martial-arts gym has a member whose card on file was declined. Instead of chasing them at the front desk, staff open the overdue charge under **Sales** > **Finance** > **Charges**, choose **Send myPOS payment link**, and the member pays on their own phone within the 7-day window. The IPN settles the charge automatically.
**Yoga studio rehearsing before launch.** A yoga studio without myPOS credentials yet activates myPOS in **Test** mode, walks through the full checkout with myPOS test cards, and confirms receipts and booking confirmations look right. When its myPOS store is ready it switches to **Live** and pastes its real Configuration Pack.
## Tips & best practices
* Prefer the **Configuration Pack** over manual entry. It bundles all five credentials as one Base64 string, so there is nothing to mistype.
* Remember that **Validate configuration** is a local check for hosted checkout. It can't confirm myPOS recognizes your store, so always follow it with a real test payment.
* Leave the private key, certificate, and client secret fields blank when editing later. Only paste a value when you are deliberately replacing it.
* Keep your Configuration Pack at hand. You need it again whenever you switch from **Test** back to **Live**.
* Keep the notification (IPN) URL allowed in your myPOS store settings, otherwise payments succeed on myPOS but never settle in 1Club.
## Related
* [Integrations](/settings/integrations) - the full integration catalog and how activation works.
* [Billing entities](/settings/billing-entities) - the companies you trade through, each with its own payment accounts.
* [Stripe](/settings/integrations/stripe) - card and digital wallet payments with Stripe Connect.
* [DatecsPay](/settings/integrations/datecspay) - the other hosted-checkout provider, which can't run alongside myPOS **Online** for the same company.
* [Charges](/sales/charges) - where payable charges live and where you send a payment link.
* [Payments](/sales/payments) - the payment records myPOS settles.
# Salto KS
Source: https://docs.1club.ai/settings/integrations/salto
Open Salto KS locks from 1Club bookings and check-ins, with each area as a Salto access group and a phone digital key members activate with their 1Club account.
The Salto KS integration connects 1Club to Salto KS, Salto's cloud access control system. Each area in a gym becomes a Salto access group holding the locks you choose, and a booking or check-in puts the member in that group for its time window. Members open the lock with their phone, using a digital key they activate by signing in with their 1Club account. An owner or admin enters the Salto account once, then sets up each gym.
## When to use it
Use it when your gym's doors run on Salto KS locks and you want access to follow what members book, with no cards to hand out.
Typical uses:
* **Studio doors that open only for class time** - a yoga studio's room opens for each booked member shortly before class and closes again after, from the member's phone.
* **Courts with no front desk** - a padel gym's court gates open for the players who booked the slot, and the gym's opening hours stay enforced even if the internet goes down.
* **A large membership on a small Salto subscription** - only the members inside their booking window take a Salto seat, so thousands of members can share a subscription sized for the people actually in the building.
## How it works
* **Areas become access groups.** **Sync areas** creates a Salto access group for each area, holding the locks you choose.
* **Access follows the schedule.** A booking or a check-in puts the member in the area's group for its own window plus the access buffer.
* **Doors open from the phone.** Members activate a digital key in the 1Club mobile app by signing in with their 1Club account, then open the lock over Bluetooth.
* **Seats follow who is inside.** A member takes a Salto subscription seat only while their access is open.
### The access window
The **Access buffer (minutes)** sets how early a member can get in before a booking starts, and how long they keep access after it ends. It can be 15-120 minutes.
A check-in grants access immediately and keeps it until the end of the booking plus the buffer.
### Opening hours backstop
With **Limit access to opening hours** on, 1Club sends the gym's opening hours to Salto. The doors then refuse anyone outside those hours, even if 1Club can't be reached. It is on by default. The hours are sent when you click **Sync areas**, so sync again after you change the gym's opening hours or this switch.
### Seats
Salto counts everyone who has access right now against your subscription. Members outside their booking window don't take a seat, so a seat comes free as soon as a member's window closes. The **Status** panel shows how many seats are in use.
1Club only manages the Salto users it created itself. Other integrators' users on the same Salto site are left alone.
## Before you start
* Salto KS is a **paid-plan** feature. On the Free plan and during the trial, its **Activate** and **Configure** buttons stay greyed out until you upgrade.
* Configuring Salto KS requires the **settings.manage.organization** permission. Viewing its settings requires **settings.read.organization**.
* You need the Salto KS account details Salto issued for 1Club: client ID and secret, a system user, and the SSO client ID for the digital key.
* Each gym needs at least one active area. See [Areas](/settings/areas).
* A gym can use only one door system at a time. If a gym already uses another provider, such as [Impro](/settings/integrations/access-bridge) or [WebLock](/settings/integrations/weblock), turn that one off for the gym first.
* Members need a login email on their 1Club account. Salto links each member by that email, so a member without one can't be given access.
## Activate and enter the Salto account
The Salto account is entered once for the whole organization. The member digital key uses the same account.
1. Go to **Settings** > **Integrations** and click **Activate** next to Salto KS, under **Access control**. 1Club adds Salto KS and opens its page. Once it is added, you reach the page from **Configure** under **Active Integrations**.
2. Under **Salto KS account**, choose the **Salto environment**:
* **Acceptance (testing)** - stay here until Salto moves you to production.
* **Production** - for live use once Salto has moved you over.
The environment fills in both the **API address** and the **Identity server address**. If the addresses don't match either environment, the field shows **Custom addresses**.
3. Enter the **Client ID** and **Client secret**.
4. Enter the **System user email** and **System user password**.
5. Enter the **SSO client ID**. This is the client Salto issued so members can activate their digital key by signing in with their 1Club account.
6. Click **Save account**. The button is available once you have changed a field.
**Save account** saves the details and tests them right away. The integration becomes active only when the test passes, and until then members are never offered a digital key. If the test fails, the saved details stay in place and the page shows **Salto KS connection failed.** with the reason.
Once a secret or password is saved, leave its field blank to keep it.
## Set up a gym
1. Choose the gym under **Club**.
2. Under **Connection**, choose the **Site**: the Salto site whose locks are this gym's doors. The list fills in once the account is saved.
3. Set the **Access buffer (minutes)** (15-120).
4. Leave **Limit access to opening hours** on unless you have a reason to turn it off.
5. Click **Save**. **Save** stays greyed out until a site is chosen. The **Locks** lists load once the site is saved.
6. Under **Areas**, choose the **Locks** each area opens.
7. Turn on **Enable Salto KS for this club** and click **Save**.
8. Click **Sync areas**.
Each area shows **Access group ready** once it has been synced, or **Not synced** until then. An area opens no doors until you sync areas.
Lock choices reach Salto only when you sync areas. Click **Sync areas** again whenever you change an area's locks, add or rename an area, or change the gym's opening hours.
If the gym already has a Salto site for members' digital keys, door access must use the same site.
Use **Test connection** to check the gym's connection, and **Sync now** to push access to Salto straight away instead of waiting for the next automatic pass. **Test connection**, **Sync areas**, and **Sync now** are available once the gym is saved, and only while there are no unsaved changes.
## Status
The **Status** panel shows:
* **Pending access updates** - changes 1Club still has to send to Salto.
* **Subscription seats in use** - for example "12 of 50 subscription seats in use". The count turns red when every seat is taken.
* Any members whose access couldn't be updated, with the reason.
If Salto refuses a member because the subscription has no free seat, free a seat or ask your installer to enlarge the subscription.
## Change a gym's site or turn Salto off
Turning off **Enable Salto KS for this club** and saving withdraws the gym's access in Salto. Access granted at one site can only be withdrawn at that site, so to move a gym to a different site, turn Salto off for the gym first and let the access cleanup finish.
## Deactivate
Deactivating removes the Salto account and every gym's Salto KS setup. Because the Salto account is the only way to reach any of your sites, access has to be withdrawn first:
1. Turn off **Enable Salto KS for this club** for every gym, and save each one.
2. Wait until **Pending access updates** reaches zero, so Salto no longer holds access for any member.
3. Click **Deactivate** at the top of the Salto KS page.
4. The confirmation, **Deactivate Salto KS?**, says that Salto KS stops working and its settings are deleted, so you'd need to set it up again to use it. Click **Deactivate** to confirm.
Once it is deactivated, you return to **Settings** > **Integrations**.
Deactivation is refused while any gym still has Salto KS on, or while Salto still holds access for a member. The dialog says which one applies.
## Troubleshooting
* **The page asks you to create an area first** - the gym has no active area. Use **Manage areas** to add one.
* **The Site list is empty** - the Salto account isn't saved yet, or its test failed. Check the account details and click **Save account** again.
* **Members aren't offered a digital key** - the account test hasn't passed yet. Fix the details under **Salto KS account** and save again.
* **An area opens no doors** - check that it shows **Access group ready**. If it shows **Not synced**, click **Sync areas**.
* **Turning a gym on is refused** - either the Salto account isn't saved, or the gym already uses another door system. Save the account, or turn the other provider off for this gym.
* **A member can't be given access** - make sure the member has a login email on their 1Club account.
## Related
* [Integrations](/settings/integrations) - every integration and how activation works.
* [Impro](/settings/integrations/access-bridge) - door access through each gym's own Impro installation.
* [WebLock](/settings/integrations/weblock) - door access through WebLock (Valnes).
* [Areas](/settings/areas) - the areas that become Salto access groups.
* [Organization and clubs](/reference/organization-clubs) - where each gym's opening hours and details live.
# Social booking
Source: https://docs.1club.ai/settings/integrations/social-booking
Turn your Instagram, Facebook, and other social profiles into a booking channel with pre-tagged "Book now" links, so every booking is attributed to the platform that drove it
Social booking turns your social profiles and ads into a booking channel. It gives you a **booking link generator** that builds a pre-tagged "Book now" link to your member portal or website: you paste it into your bio, posts, or ads, and members follow it to book. Unlike other integrations there is nothing to activate or connect: it is built in, always on, and works on every plan, including the Free plan and the trial. It has no **Deactivate** button.
## When to use it
Use it when you want to know which platforms actually drive bookings. The link generator adds UTM tags to your existing portal or website URL - it does not create a new page. When a member follows a tagged link and books, 1Club records the platform, placement, and campaign it came from, with no manual tracking. Platforms include Instagram, Facebook, YouTube, TikTok, WhatsApp, Google, X, LinkedIn, Pinterest, or a custom source.
Typical uses:
* **Organic post versus paid ad** - a Pilates studio puts one tagged link in its Instagram bio and another in a paid Instagram ad, then compares how many bookings came from each.
* **A partner promotion** - a climbing gym gives a local outdoor shop a link with its own custom source, and bookings from that link are attributed to the partner.
* **Deciding where to spend next time** - a padel gym posts the same open-day offer to Facebook, Instagram, and TikTok, each with its own tagged link and a shared campaign name, then compares bookings by platform.
## Before you start
The link points at one of two destinations:
* **Member portal** - offered when the [member portal](/settings/member-portal) is turned on. It uses your verified custom portal domain, or your default portal address.
* **Website** - always offered. It uses your verified custom website domain, or your default website address. Make sure your [website](/marketing/website) is published before you share a link to it.
Generating links needs access to **Settings** > **Integrations** (the **settings.read.organization** permission).
## Generate a booking link
1. Go to **Settings** > **Integrations**. Social Booking is always listed under **Active Integrations** as **Configured**. Click **Configure**.
2. In the **Booking link generator**, choose a **Destination** (**Member portal** or **Website**). The choice only appears when the member portal is turned on. Otherwise the website is used.
3. Choose the **Platform** you will share the link on. It starts on **Instagram**.
4. Choose the **Placement**:
* **Profile / bio / post** (the default) - an organic link in your bio, a post, or a story.
* **Paid ad** - a link used in a paid social ad.
5. Optionally enter a **Campaign (optional)** name to group everything for one promotion (for example `summer`).
6. The finished link appears under **Your link**. Click **Copy link** and paste it wherever you are sharing it.
## The generated tags
The generator composes standard UTM tags onto your destination URL so analytics can attribute the booking:
| Field | What it sets | Notes |
| - | - | - |
| **Destination** | The base link | Your member portal or website URL. |
| **Platform** | `utm_source` | The chosen platform (for example `instagram`). |
| **Placement** | `utm_medium` | `organic_social` for profile/bio/post links, `paid_social` for paid ads. |
| **Custom source** | `utm_source` | When you pick **Other**, type your own source (a newsletter, partner, or QR code); the link is tagged as a plain referral rather than social. |
| **Campaign** | `utm_campaign` | Optional grouping name. |
## Custom sources
Pick **Other** as the platform when the link is not really social - a newsletter, a partner site, or a printed QR code. The **Placement** field is replaced by **Custom source**, where you type the source name yourself. The link is tagged as a referral (`utm_medium=referral`) instead of organic or paid social.
## Where attribution shows up
When a visitor arrives through a tagged link, the portal or website remembers the tags in their browser for 30 days. If they book within that time, the booking is attributed to that source. If they follow several tagged links, the most recent one wins. You can see which platforms drive bookings in the [Bookings by source](/analytics/schedule#bookings-by-source) report (**Analytics** > **Bookings** > **By source**), where the `utm_source` on each booking is broken out for attribution. This closes the loop between what you post and the bookings it produces.
## Worked examples
**Pilates studio promoting a new reformer block.** A studio adds a **Book now** link to its Instagram bio tagged with **Instagram**, **Profile / bio / post**, and the campaign `reformer-launch`. It also runs a paid Instagram ad using a second link with the **Paid ad** placement. In analytics it can tell how many bookings came from the organic bio link versus the paid ad.
**Climbing gym running a partner promotion.** A climbing gym partners with a local outdoor shop. It generates a link with **Other** as the platform, types `outdoor-shop` as the custom source, and gives it to the partner to share. Bookings from that link show up attributed to `outdoor-shop`.
**Racket-sports gym comparing channels.** A padel gym posts the same open-day offer to Facebook, Instagram, and TikTok, each with its own tagged link and the shared campaign name `open-day`. After the weekend it compares booking counts by platform to decide where to spend next time.
## Tips and best practices
* Use a consistent **Campaign** name across every link for one promotion so the results group together.
* Keep separate links for organic posts and paid ads (the **Placement** tag) so you can tell earned reach from ad spend apart.
* Point the link at whichever destination members actually book on - the portal if that is where booking happens, the website otherwise.
* Generate a fresh link per channel rather than reusing one everywhere, so each platform's contribution is measurable.
## Troubleshooting
* **There is no Destination choice** - the member portal is turned off, so links always point at the website. Turn the [member portal](/settings/member-portal) on to offer it as a destination.
* **The website link opens an empty site** - your website isn't published yet. Publish it, or point the link at the member portal.
* **The link is not tagged** - if you leave the source empty (for example choosing **Other** without typing a custom source), the generator falls back to the plain destination URL. Fill in the fields to get a tagged link.
* **Bookings are not attributed** - confirm you shared the generated (tagged) link, not the plain portal or website address. Attribution only works when the member arrives through the tagged link and books within 30 days on the same browser.
## Related
* [Integrations](/settings/integrations) - the full integration catalog.
* [Website](/marketing/website) - one of the destinations a booking link can point at.
* [Bookings](/schedule/bookings) - the bookings that get attributed to a source.
* [Schedule analytics](/analytics/schedule) - the Bookings by source report where you review bookings by UTM source.
# Stripe
Source: https://docs.1club.ai/settings/integrations/stripe
Accept card and digital wallet payments through Stripe, starting in a test sandbox and going live with your own Stripe Connect account
Stripe powers card and digital wallet payments across 1Club: member checkout in the portal and kiosk, admin-initiated charges, payment links, and recurring billing. An owner or admin sets it up once, and members pay through it wherever they check out.
If your organization trades through more than one company, each company
connects its own Stripe account. Pick the company under **Billing entity** at
the top of the Stripe page before you activate, go live, or deactivate. The
line under the picker reminds you that payments for that company's gyms settle
to the account you are setting up. The picker only appears when you have more
than one billing entity. See [Billing entities](/settings/billing-entities).
## When to use it
Stripe lets you rehearse payments in a sandbox, then take real money through your own Stripe account. You start in **test mode** against the 1Club sandbox, then turn on **Live mode** to onboard your own Stripe Connect account. Payment status flows back to 1Club automatically through webhooks, with no webhook setup on your side.
Typical uses:
* **Validating checkout before going live** - book a couple of courts in the portal with Stripe test cards, check the checkout, receipt, and booking confirmation, then turn on **Live mode** and complete onboarding.
* **Memberships and drop-ins** - members pay recurring dues at checkout and drop-in visitors pay per session, while you watch the **Payouts** capability to confirm money is landing in your bank account.
* **Two companies, two payouts** - a group runs its gyms through two companies, and each company connects its own Stripe account so its sales pay out to its own bank account.
## Before you start
* Stripe is a **paid-plan** integration. On the Free plan and during the free trial, **Activate** is greyed out until you upgrade.
* Configuring Stripe requires the **settings.manage.organization** permission.
* You don't need a Stripe account to try test mode. 1Club ships a sandbox you can use immediately.
* To go live you need a Stripe account (you can create one during onboarding) with the business details, bank account, and identity documents Stripe requires for your country.
## Activate Stripe (test mode)
Activating Stripe puts you straight into test mode against the 1Club sandbox. No Stripe account is created yet.
1. Go to **Settings** > **Integrations**.
2. Under **Payments**, click **Activate** next to Stripe. 1Club opens the Stripe page.
3. The **Stripe status** card shows **Active** and says test payments use the 1Club sandbox. No real cards are charged.
4. Run a checkout in the member portal or kiosk using Stripe's [test card numbers](https://stripe.com/docs/testing) to confirm the flow end to end.
**Activate** on the Integrations list sets up Stripe for your default company. To activate it for another company, open the Stripe page, pick the company under **Billing entity**, and click **Activate** in the **Stripe status** card.
## Go live (Stripe Connect onboarding)
Real payments require your own Stripe Connect account. The **Live mode** toggle drives the whole transition:
1. Turn the **Live mode** toggle on.
2. If you have no Stripe account yet, 1Club creates a Stripe Connect (Express) account and opens Stripe's onboarding in a new tab. You stay in test mode until onboarding is complete.
3. Complete Stripe's onboarding form: business details, representative, bank account, and any identity documents Stripe asks for.
4. Return to 1Club. The page refreshes your account status automatically after a couple of seconds.
5. Once Stripe reports that card payments are active and nothing blocking is due, turn **Live mode** on again. This time it switches, and real charges start processing.
If you close the onboarding tab early, click **Complete setup** in the **Stripe status** card to reopen the same flow. Until you have a Stripe account, the page reminds you that turning on live mode is how you set one up.
Stripe Connect accounts always live on the live side of Stripe. Test mode only
changes which Stripe keys process your payments (the platform sandbox), not
which account exists. That is why you can activate Stripe and take test
payments long before you create a Connect account.
## Test mode vs. live mode
The **Live mode** toggle on the Stripe page controls where payments go:
* **Test mode (off)** uses the 1Club sandbox on the platform's Stripe account. Real cards are not charged. Use Stripe's test card numbers to simulate payments. No Connect account is required.
* **Live mode (on)** uses your own Stripe Connect account. Customers are charged for real and payouts go to your bank account.
A banner at the bottom of the page always says which mode you are in.
If your Stripe account still needs information, turning on live mode is refused with "Complete Stripe onboarding before enabling live mode." and a **Complete setup** button. Switching back to test mode is always allowed.
Saved cards belong to the Stripe account they were saved on. After you switch modes, cards saved in the other mode can't be charged until you switch back.
Live mode is restricted to organizations on a paid plan. If your plan doesn't
allow live payments, switching to live mode fails. Upgrade to a paid plan, or
contact support, to accept live payments.
## Stripe status
The **Stripe status** card at the top of the page shows a status chip:
* **Not activated** - Stripe isn't set up for this company. Click **Activate**.
* **Active** - you are taking payments, either in test mode with the sandbox or through a Stripe account that is fully set up.
* **Pending** or **Restricted** - you have a Stripe account, but Stripe still needs information or has limited it. Click **Complete setup**.
Once you have a Stripe account, the card also shows your **Account ID: acct\_...** and these buttons:
* **Complete setup** - reopens Stripe's onboarding. Shown until the account status is **Active**.
* **View dashboard** - opens the Stripe Dashboard in a new tab.
* **Refresh status** - pulls the latest account status from Stripe right away. The page also re-reads it every 30 seconds while it is open.
### Account capabilities
Once you have a Stripe account, the card lists what Stripe allows the account to do under **Account capabilities**, each with a status chip such as **Active**, **Pending**, **Restricted**, or **Inactive**:
* **Charges** - whether Stripe will accept card payments on this account.
* **Payouts** - whether Stripe will send money from your Stripe balance to your bank.
* **Transfers** - whether the account can receive the money 1Club passes on to it from your sales.
* **Setup** - **Completed** when Stripe needs nothing more from you, including documents it will only ask for later. Otherwise **Incomplete**.
Below them, **Other capabilities** lists everything else Stripe has turned on or requested for your account, such as extra payment methods, grouped by status.
## Setup progress and requirements
If Stripe still needs information before you can take payments, a **Setup progress** card appears below the status card:
* A warning says Stripe needs more information and asks you to select **Complete setup**.
* A progress bar tracks the two money capabilities, **Charges** and **Payouts**, with a caption such as "1 of 2 capabilities enabled".
* If Stripe has disabled the account, a **Disabled reason** line says why.
* **Currently due** lists items that block payments until you submit them.
* **Pending verification** lists documents you have already submitted that Stripe is still reviewing.
Once the account can take payments but Stripe will still want more documents later, a separate **Pending verification** card appears instead. It says payments work but Stripe will ask for more documents, lists them, and has a **Complete verification** button that reopens onboarding.
## Payment terminals
To take payments in person, set up card readers under **Settings** > **Sales** > **Payment terminals**. The Stripe page links there with a **Manage** button. Terminals from every provider you use are managed in one place.
## Deactivate
1. Pick the company under **Billing entity**, if you have more than one.
2. Click **Deactivate** at the top of the Stripe page.
3. Confirm in the **Deactivate Stripe?** dialog. It says Stripe stops working and its settings are deleted. With more than one billing entity, it also says the change applies to the selected company only, and your other companies stay connected.
You stay on the Stripe page afterwards. If 1Club can't deactivate Stripe, the reason shows in the dialog.
Deactivating stops 1Club from creating new charges on that Stripe account. Your Stripe account itself is not closed. Existing payments and payouts are unaffected on Stripe's side, so manage refunds and disputes from the Stripe Dashboard. If you activate Stripe again later, you start over in test mode, and going live runs Stripe onboarding again.
## What syncs
* Charges and refunds made in 1Club appear in your Stripe Dashboard.
* Payment status updates from Stripe (succeeded, failed, refunded, disputed) flow back to 1Club through webhooks, with no setup needed on your side.
* Payouts go from Stripe to the bank account you configured during onboarding. Schedule and currency are controlled in the Stripe Dashboard.
## Worked examples
**Padel gym validating checkout before going live.** A racket-sports gym activates Stripe, leaves **Live mode** off, and books a couple of courts in the portal using Stripe test cards. Once the checkout, receipt, and booking confirmation all look right, it turns on **Live mode**, completes Stripe onboarding with its bank details, turns **Live mode** on again, and starts collecting real court fees.
**Martial-arts gym selling memberships and drop-ins.** A jiu-jitsu gym runs live Stripe. Members pay recurring dues at checkout and drop-in visitors pay per session. The gym watches **Payouts** show **Active** to confirm money is landing in its bank account, and opens **View dashboard** to handle a disputed charge.
**A group trading through two companies.** A fitness group runs its city-centre gyms through one company and its suburban gyms through another. On the Stripe page it picks the first company under **Billing entity**, activates Stripe and goes live, then picks the second company and does the same. The Integrations list shows Stripe as connected for 2 of 2 billing entities, and each company's sales pay out to its own bank account.
## Tips & best practices
* Do all your rehearsal in test mode. A successful test-card payment through the real portal checkout is the cleanest proof your setup works before real money is involved.
* Clear **Currently due** items promptly. They block payments, so the sooner they are submitted the sooner you can go live.
* Treat items in the **Pending verification** card as a checklist. They don't block payments today, but leaving them unresolved risks a later Stripe restriction.
* After returning from Stripe onboarding, give the page a moment, or click **Refresh status** to pull the latest capabilities.
## Troubleshooting
* **Activate is greyed out** - your organization is on the Free plan or the free trial. Upgrade to a paid plan to activate payments.
* **"Stripe needs more information before you can take payments"** - open the **Setup progress** card and submit everything under **Currently due** through **Complete setup**.
* **Live mode doesn't turn on** - either your Stripe account is not ready (click **Complete setup** to finish onboarding) or your plan doesn't allow live payments (upgrade to a paid plan).
* **Payments succeed in test mode but fail live** - confirm **Live mode** is on and that **Charges** shows **Active**.
* **Charge succeeded in Stripe but not visible in 1Club** - payment status arrives through Stripe's webhooks, usually within seconds. If it doesn't appear, contact support with the Stripe PaymentIntent ID.
## Related
* [Integrations](/settings/integrations) - the full integration catalog and how activation works.
* [Billing entities](/settings/billing-entities) - the companies you trade through, each with its own payment accounts.
* [myPOS](/settings/integrations/mypos) - an alternative card payment provider with hosted checkout and in-person terminals.
* [DatecsPay](/settings/integrations/datecspay) - hosted card checkout through BORICA.
* [Charges](/sales/charges) - where individual charges are recorded.
* [Invoices](/sales/invoices) - invoices you can attach a Stripe payment link to.
# WebLock
Source: https://docs.1club.ai/settings/integrations/weblock
Open WebLock (Valnes) doors and switch on area lights from 1Club bookings and check-ins, set up per gym and per area.
The WebLock integration connects 1Club to the Valnes WebLock access control system. You link each area in a gym to WebLock door groups, and optionally to light devices, so a booking or check-in in that area opens the right doors and switches on the lights for its time window. An owner or admin sets it up for each gym once. Members then get in with the PIN or access card on their 1Club account.
## When to use it
Use it when your doors run on WebLock and access should follow what members book, without staff handing out PINs or programming fobs.
Typical uses:
* **A court door and its lights tied to the booking** - a padel gym links each court to its door group and its light device, so players get in a little before their slot and the court lights come on just before it starts.
* **A studio that opens only for class** - a Pilates studio links the studio area to the studio door group, so a member's PIN opens it only around the class they booked.
* **Several gyms on one WebLock business** - a group with two locations runs both on one WebLock business, and each gym's areas open only that gym's doors.
## How access is decided
Access in WebLock comes from **area access**: a member may enter an area while they have access to it in 1Club. That access comes from:
* **A booking** in the area. Cancelling the booking withdraws it.
* **A check-in** in the area, which gives access from the moment of check-in.
* **Access added by hand** by staff for a member, an area, and a time window.
A membership on its own does not open any door. What opens a door is access to an area.
For each member with access, 1Club keeps WebLock in step:
* **A WebLock user** is created for the member the first time they get access.
* **Their credentials** are sent to WebLock: the PINs and access cards on their 1Club account. QR codes are not sent.
* **Door groups.** The member is added to every door group linked to the area, for the access window: the booking or check-in time, widened by the **Access buffer** before and after. Overlapping windows are merged into one.
* **Light devices.** Each light device linked to the area gets a light rule for the same time, widened by the **Light buffer** instead. A window that crosses midnight is split into one rule per day.
When the access ends or is withdrawn, 1Club removes the member from the door groups and deletes their light rules. Once a member has no access left at all, their credentials are removed from WebLock too.
A member needs a PIN or an access card to get in. If they have no credential yet and PIN is an enabled identification method for the gym, 1Club creates a PIN for them automatically. A member with no PIN or access card gets no access in WebLock. See [Access cards](/members/member-cards) and [Access control](/settings/access-control).
Booking access is held back while the member owes a document the gym requires for entry, such as a waiver. It reaches the door once they sign it, or once staff let them in that day. Check-in access always applies.
Changes reach WebLock within moments. A complete pass also runs every five minutes, and **Sync now** runs one straight away.
## Before you start
WebLock is a paid-plan integration. On the Free plan and during the trial, its **Activate** and **Configure** buttons stay greyed out until you upgrade.
* Configuring WebLock requires the **settings.manage.organization** permission. Viewing it requires **settings.read.organization**.
* You need a WebLock (Valnes) account with a **Business ID**, and the door groups and light devices already set up in the WebLock console.
* Each gym needs at least one active area. See [Areas](/settings/areas).
* A gym can use only one door system at a time. If a gym already uses [Impro](/settings/integrations/access-bridge) or [Salto KS](/settings/integrations/salto), turn that one off for the gym first.
Valnes API credentials are held at the platform level, so you do not enter a username or password in 1Club - only your Business ID.
## Set up a gym
1. Go to **Settings** > **Integrations** and click **Activate** next to WebLock, under **Access control**. 1Club activates WebLock and opens its page. Once it is active, you reach the page from **Configure** under **Active Integrations**.
2. On the WebLock page, choose the gym under **Club**. If the gym has no active area, the page asks you to create one and links to **Manage areas**.
3. Under **Connection**, enter your **Business ID** and click **Save**. **Save** stays greyed out until a Business ID is entered. The door group and light device lists load from WebLock once the Business ID is saved.
4. Set the two buffers:
* **Access buffer (minutes)** (0-120, default 15) - how long before a booking starts the door opens, and how long after it ends it stays open.
* **Light buffer (minutes)** (0-120, default 5) - how long before and after a booking the area's lights are on.
5. Under **Areas**, choose for each area:
* **Door groups** - the WebLock door groups that access to this area opens. You can pick several. An area with no door group opens no doors, and the page says so under the area.
* **Light devices (optional)** - the devices whose lights follow access to this area.
6. Turn on **Enable door access** and click **Save**.
7. Click **Test connection** to confirm 1Club can reach WebLock with the Business ID, then **Sync now** to send access straight away.
**Test connection** and **Sync now** are available once the gym is saved, and only while there are no unsaved changes.
### Switching from the earlier setup
If a gym was set up before WebLock worked per area, the page shows a warning until you save it on the current setup. 1Club pre-fills each area's door group and light device from the gym's earlier settings where it safely can. Check every area before you save.
Configure every area before you switch. After the switch, a membership alone no longer opens the door: access comes from area access, such as a booking in that area. If several gyms share the same WebLock business, save every one of them on the current setup before you sync. 1Club refuses to sync a shared business until all of its gyms are switched.
## Several gyms on one WebLock business
Gyms can share a WebLock business. 1Club syncs a shared business as a whole, so a member with access at two gyms is one WebLock user, with the door groups of both.
To move a gym to a different Business ID, turn off **Enable door access** for it first and let the access cleanup finish, including for any other gyms sharing that business. 1Club refuses the change until then, because access granted in one business can only be withdrawn in that business.
## Worked examples
### Padel gym: court access plus lights on booking
A padel gym wants a booking to open the court gate and switch that court's lights on. For each court area, it picks the court's door group under **Door groups** and the court's light under **Light devices**. It sets the **Access buffer** to 15 minutes so players can get in a little early, and the **Light buffer** to 5 minutes so the lights come on just before the slot starts and go off shortly after it ends.
### Pilates studio: the studio door only for class
A Pilates studio links its studio area to the studio door group and leaves light devices empty. A member who books a class gets in with their PIN from 15 minutes before the class until 15 minutes after. If they cancel, the access is withdrawn and their PIN no longer opens the door.
### Two gyms on one business
A climbing group runs two gyms on one WebLock business. It sets up both gyms on the WebLock page with the same Business ID, saves both, then syncs. A member who books at both gyms is one WebLock user, and each booking opens only its own gym's door groups.
## Tips and best practices
* Save the **Business ID** before you start linking areas. The door group and light device lists only load once it is saved.
* Give every area that should open a door at least one door group. An area without one opens nothing.
* Keep the **Access buffer** larger than the **Light buffer**: you usually want the door to open a little before the lights come on.
* Turn on PIN as an identification method for the gym, so members without an access card get a PIN automatically.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| **Test connection** fails | The Business ID is wrong, or your WebLock account has no API access | Confirm the Business ID with your WebLock administrator, save it, and retry |
| The door group or light device lists are empty | The Business ID isn't saved or is wrong, or nothing is set up in the WebLock console | Save the correct Business ID, and check the WebLock console |
| An area opens no doors | No door group is selected for it | Pick at least one **Door group** for the area and save |
| A member can't open the door | They have no PIN or access card, they owe a required entry document, or they are outside their access window | Check their credential on [Access cards](/members/member-cards) and their documents, or raise the **Access buffer** |
| Lights don't come on for a booking | No light device is linked to the area, or the **Light buffer** is shorter than expected | Pick a **Light device** for the area and check the **Light buffer** |
| Syncing is refused for a shared business | Another gym on the same WebLock business is still on the earlier setup | Open that gym on the WebLock page and save it |
| Changing the Business ID is refused | The gym still has door access on, or cleanup hasn't finished | Turn off **Enable door access**, wait for cleanup, then change it |
| **Activate** or **Configure** is greyed out | Your organization is on the Free plan or still in the trial | Upgrade the plan, then configure |
## Deactivate
The WebLock page has no **Deactivate** button, because WebLock is switched off per gym. To stop WebLock at a gym:
1. On the WebLock page, choose the gym under **Club**.
2. Turn off **Enable door access** and click **Save**.
1Club then withdraws the access it granted through WebLock for that gym. Repeat for every gym that uses WebLock. WebLock stays listed under **Active Integrations**, so you can turn it back on for a gym at any time.
## Related
* [Physical access control](/settings/access-control-settings) - access grants and how door systems read them.
* [Access control](/settings/access-control) - per-gym check-in modes and identification methods.
* [Access cards](/members/member-cards) - the PINs and access cards members use at the door.
* [Areas](/settings/areas) - the areas you link to door groups and light devices.
* [Impro](/settings/integrations/access-bridge) and [Salto KS](/settings/integrations/salto) - the other door systems.
* [Integrations overview](/settings/integrations) - every integration and how activation works.
# WhatsApp
Source: https://docs.1club.ai/settings/integrations/whatsapp
Connect your own WhatsApp Business number so members can message you on WhatsApp, staff reply from the 1Club inbox, and booking notifications go out on the channel members actually read.
The WhatsApp integration connects your gym's own WhatsApp Business number to 1Club. Once connected, members can message you on WhatsApp and staff reply from the [inbox](/inbox/messages), booking notifications go out over WhatsApp automatically, and [automations](/marketing/automations) can reach members on the channel they check most. You connect it through Meta's embedded signup - a guided Facebook popup where you pick or create a number and authorize 1Club - so there are no tokens or API keys to copy by hand.
## Why connect WhatsApp
Members read WhatsApp far more reliably than email, so booking confirmations, reminders, changes, and cancellations land in the app members already have open, with no extra setup per message. 1Club handles the parts WhatsApp is strict about: it creates the message templates Meta requires in your WhatsApp Business account when you connect, tracks opt-in, honors STOP automatically, and only sends business-initiated messages to members who have agreed. That protects your number's quality rating with Meta. The connection is stored securely, and 1Club never shows the access token back to you.
Typical uses:
* **Answering trial enquiries** - prospects message your number about a beginner class, the front desk replies from the inbox inside the 24-hour window and books them in, and because they are now an opted-in contact, their booking confirmation goes out over WhatsApp too.
* **Telling members about a schedule change** - when a class moves rooms and more than 24 hours have passed since most members last messaged, 1Club sends the approved booking-change template to affected members automatically, and staff handle the replies.
* **One number for the whole front desk** - every conversation lands in the shared inbox, where any staff member can reply, rather than on one person's phone.
## Before you start
* WhatsApp is a **paid-plan** feature. On the Free plan and during the trial, its **Activate** and **Configure** buttons stay greyed out until you upgrade.
* Connecting and managing templates requires the **settings.manage.organization** permission. Viewing the status requires **settings.read.organization**.
* You need a Facebook Business account and a phone number you can verify for WhatsApp. You can create a new number during the embedded signup.
WhatsApp embedded signup must be enabled on the 1Club server for your
deployment. If it isn't, the page shows "WhatsApp isn't available on this
server yet." instead of the connect button. Contact 1Club support if you
expect it to be available.
## Connect WhatsApp
1. Go to **Settings** > **Integrations** and click **Activate** on the **WhatsApp** card, under **Tools**. The **WhatsApp** page opens.
2. Under **Connect WhatsApp**, click **Connect with WhatsApp**.
3. A Facebook popup opens. Sign in to your Facebook Business account and follow Meta's steps to select or create a WhatsApp Business account and phone number, then authorize 1Club.
4. When you finish the popup, 1Club completes the connection: it subscribes to your WhatsApp Business account so inbound messages reach your inbox, registers the phone number if it isn't registered yet, and creates the standard message templates in your account.
5. A **WhatsApp connected** message appears, the status chip changes to **Active**, and the **Message templates** card appears below.
If you close the popup or deny access, you see **Connection cancelled** and
nothing changes. If Meta reports a problem, you see **Couldn't connect
WhatsApp** with Meta's reason. Click **Connect with WhatsApp** again to retry.
## Connection status
The status chip on the **Connect WhatsApp** card shows one of:
* **Not activated** - no WhatsApp connection exists yet.
* **Inactive** - a connection exists but isn't active. Use **Connect with WhatsApp** to authorize the number again.
* **Active** - your number is connected and can send and receive messages from the inbox.
Once connected, the button changes to **Reconnect**, which reruns the embedded signup so you can refresh the connection or switch to a different number. 1Club renews the connection's access on its own, so you rarely need to reconnect unless you are changing numbers.
## Two-way messaging and the 24-hour window
* Members who message your WhatsApp Business number arrive in the [inbox](/inbox/messages). If the sender isn't already a contact, 1Club creates one from their WhatsApp profile name and number, so the conversation is attached to a contact record.
* WhatsApp allows **free-form replies only within a 24-hour customer-service window** that opens each time someone messages you. Inside that window, staff can reply with any text - no template needed.
* Outside the window, WhatsApp only allows approved templates. 1Club handles this for you: when you send from the inbox, through an automation, or with an AI-drafted reply outside the window, it sends the approved general-message template carrying your text.
## Automatic booking notifications
Once WhatsApp is connected, these member notifications are delivered over WhatsApp, using 1Club's approved templates, to members who are reachable on WhatsApp and have opted in:
* **Booking confirmation** - sent when a booking is made.
* **Booking reminder** - sent ahead of the class or session.
* **Booking change** - sent when a booking's details change, such as its time or place.
* **Booking cancellation** - sent when a booking is cancelled.
More templates (waitlist spot available, membership expiration reminder, review request, and a general message) are used by the waitlist, [automations](/marketing/automations), and other flows. Members choose which notification channels they receive in their notification settings, and turning on the WhatsApp channel counts as opting in.
## Message templates
WhatsApp requires any message you *start* (rather than reply to within the 24-hour window) to use a template Meta has reviewed and approved. That covers booking notifications and automations. **1Club creates and manages this template set for you** - you don't write them, and you can't edit them in 1Club.
When you connect, 1Club creates its standard template set in your WhatsApp Business account: booking confirmation, cancellation, reminder, and change, waitlist spot available, membership expiration reminder, review request, and a general message. Each one is created in English, plus each language your gym uses. Meta then reviews each template and language separately, usually within a few hours. A notification uses a template only once Meta has approved it.
The **Message templates** card is a read-only status view. It lists each template with its **Notification**, **Template** name, **Language**, and **Status**:
* **Approved** - ready to send.
* **Pending Meta review** - created, waiting for Meta's approval.
* **Rejected by Meta** - Meta didn't approve it, so it isn't used.
* **Not set up yet** - the template doesn't exist in your WhatsApp account. Use **Re-provision** to create it.
* **Doesn't match 1club's template** - a template with the same name exists in your WhatsApp account, but its content differs from the one 1Club manages, so 1Club won't use it. Delete it in WhatsApp Manager, then click **Re-provision** to create 1Club's version.
1Club refreshes these statuses from Meta once a day. Two buttons let you act straight away:
* **Sync status** - refreshes the approval statuses from Meta now. If Meta doesn't return a template's content, 1Club can't check it and leaves it as it was, so try again shortly. If the refresh itself fails, the statuses shown may be out of date until you run **Sync status** again.
* **Re-provision** - creates every template from 1Club's standard set that is missing from your WhatsApp account, then refreshes the statuses. Each new template goes to Meta for review before it can be used. Run it after you add a language to your gym, so the templates for that language are created. It is safe to run repeatedly, and it never changes a template that already exists under the same name.
Because **Re-provision** only creates missing templates, it doesn't resubmit a template Meta rejected or replace one that doesn't match. Delete that template in WhatsApp Manager first, then click **Re-provision** to create it again.
These WhatsApp templates are separate from [Templates](/settings/templates)
under **Settings** > **Communication** > **Templates**, which holds your custom and
system message content for email, SMS, and inbox sends. The WhatsApp templates
here are the Meta-approved formats that let business-initiated messages go out
after the 24-hour window, and they aren't editable.
## Consent and opt-out
WhatsApp penalizes businesses that message people who didn't agree to it, so 1Club gates every **business-initiated** send (notifications, automations, and messages sent outside the 24-hour window) on the member's consent, and honors opt-out automatically.
**How a member opts in:**
* They message your WhatsApp Business number. Any inbound message counts as an opt-in under WhatsApp's rules.
* They send a re-subscribe keyword such as **START**.
* They turn on the WhatsApp channel in their notification settings.
* They agree to WhatsApp messages when they enter their own phone number while signing up.
**How a member opts out:**
* They send a standard opt-out keyword as their whole message - **STOP**, **UNSUBSCRIBE**, **CANCEL**, and similar. 1Club records the opt-out and blocks all business-initiated sends to them, even inside the 24-hour window, until they opt back in with **START**. An ordinary message after an opt-out doesn't opt them back in.
Replies inside the 24-hour window are never blocked by this gate. If a member messaged you, you can always reply.
### Who is reachable on WhatsApp
1Club learns which members are actually on WhatsApp from real signals: an inbound message from them, or a delivered or read outbound message. Contacts confirmed reachable show a small **WhatsApp badge** in the inbox and contact lists, so staff can see at a glance who can be reached on the channel. If Meta reports that a number isn't on WhatsApp, 1Club clears the signal. Business-initiated notifications only go to members who are both reachable and opted in.
## Automations and AI replies
* **Automations** can send on WhatsApp as one of their message channels. Outside the 24-hour window they use the approved general-message template automatically.
* **AI-drafted inbox replies** ([Generate with AI](/inbox/messages)) work on WhatsApp threads like any other channel, and the same window and template rules apply.
## Deactivate
1. On the **WhatsApp** page, click **Deactivate** at the top of the page.
2. Confirm in the **Deactivate WhatsApp?** dialog. It warns that the integration stops working and its settings are deleted.
1Club removes the connection. New inbound messages no longer reach your inbox, and staff can't send from your number until you connect again. The message templates stay in your WhatsApp Business account. You return to **Settings** > **Integrations**. If deactivation fails, the reason is shown inside the dialog.
## Worked examples
**Martial-arts gym fielding trial enquiries.** A muay thai gym connects its WhatsApp Business number and shares it on its social profiles. Prospects message asking about the beginner class. Because that message opens the 24-hour window, the front desk replies from the inbox and books them in. The prospect is now an opted-in contact, so their booking confirmation goes out over WhatsApp too.
**Yoga studio confirming a schedule change.** A studio's morning reformer class moves rooms. More than 24 hours have passed since most members last messaged, so 1Club sends the approved booking-change template to affected members automatically, and staff handle the replies as they come in.
**Padel gym running one number across the team.** A padel gym connects a single WhatsApp Business number, so several front-desk staff pick up member conversations from the shared inbox rather than messages landing on one person's phone.
## Tips and best practices
* Reply inside the 24-hour window whenever you can. Free-form replies feel more personal and don't depend on template approval.
* Share the same WhatsApp Business number on your website and social profiles, so messages land in the inbox and members opt in naturally.
* After connecting, give Meta time to approve the templates (usually a few hours). Use **Sync status** to check. Business-initiated sends only work once the relevant template is **Approved**.
* Use **Reconnect** rather than deactivating and connecting again when you switch numbers or refresh access.
* After you add a language to your gym, click **Re-provision** so members who use it get templates in their language.
## Troubleshooting
* **"WhatsApp isn't available on this server yet."** - embedded signup isn't enabled for your deployment. Contact 1Club support.
* **The Activate button is greyed out** - WhatsApp is a paid-plan feature. Upgrade from the Free plan or the trial.
* **"WhatsApp connect isn't ready yet"** - the Facebook popup hasn't finished loading. Wait a moment and click **Connect with WhatsApp** again.
* **"Authorized, but the popup didn't return your WhatsApp account details"** - the Facebook popup finished without sending back the account information. Click **Connect with WhatsApp** and complete the flow again.
* **Connection cancelled** - you closed or denied the popup. Click **Connect with WhatsApp** to retry.
* **Members message but nothing reaches the inbox** - check that the status is **Active**. If it shows **Inactive**, click **Connect with WhatsApp** to authorize the number again.
* **Notifications aren't going out over WhatsApp** - check that the relevant template shows **Approved** (click **Sync status**), that the member is reachable (WhatsApp badge) and opted in, and that they haven't sent STOP.
* **A template shows Rejected by Meta** - **Re-provision** doesn't resubmit it. Delete it in WhatsApp Manager, then click **Re-provision** to create it again.
* **A template shows Doesn't match 1club's template** - the template in your WhatsApp account isn't the one 1Club manages, so it won't be used. Delete it in WhatsApp Manager, then click **Re-provision**.
* **"Setup isn't finished, so templates can't be managed. Reconnect WhatsApp."** - the connection is missing its WhatsApp Business account details. Click **Reconnect** to finish setting it up.
## Related
* [Integrations](/settings/integrations) - the full integration catalog and how activation works.
* [Inbox](/inbox/messages) - where inbound WhatsApp messages arrive and staff reply.
* [Compose](/inbox/compose) - sending messages, including on the WhatsApp channel.
* [Automations](/marketing/automations) - reaching members on WhatsApp from automations.
* [Templates](/settings/templates) - custom and system message content (separate from the WhatsApp templates above).
# Kiosk app
Source: https://docs.1club.ai/settings/kiosk
Set up the 1club self-service kiosk for member check-in, class check-in, area and plan booking, and browsing plans and products on a tablet at your gym, plus the check-in-only Check-in App.
The kiosk app is a self-service screen you run on a tablet or touchscreen at your gym's entrance or front desk. Members identify themselves to check in, staff work a class roster, and visitors browse and book classes, areas, and plans without waiting for a staff member. An owner or admin turns it on per gym, a staff member signs the device in once, and it stays signed in to that one gym.
## When to use it
The kiosk takes routine arrivals off the front desk. A member never signs in on the shared device: booking and subscription flows finish on their own phone, and cases that can't be completed unattended stop with a front-desk-required result, so staff only handle the exceptions. Each capability is a toggle you control per gym, so the home screen shows only the tiles that location needs.
Typical uses:
* **Self check-in at a busy entrance** - members scan a QR code, tap an access card, or enter their PIN, and the kiosk checks them in against their booking or membership. Staff step in only when, for example, a balance is unpaid or a plan has no uses left.
* **Class check-in in the studio** - staff open a class's check-in screen and tap each booked attendee's name on the roster.
* **Booking a court on the spot** - at a padel gym, a member picks a court and time slot from an area tile, then scans the displayed QR code to finish the booking on their phone.
* **Plans on show in the lobby** - members browse plans with pricing and session details, and scan a QR code to subscribe from their phone.
## What the kiosk does
The kiosk home screen shows a tile for each enabled capability:
* **Check in** - a member identifies themselves and the kiosk records their check-in.
* **Classes** - browse the day's schedule and check in to a class.
* **Plans** - browse plans and subscribe on-site.
* **Products** - browse your product catalog.
* **Area tiles** - one tile per area type you choose to show, leading to area booking.
A member never signs in on the kiosk. Booking and subscription flows finish on the member's own phone by scanning a QR code the kiosk displays, which deep-links into their member portal.
## Setting up a device
The kiosk lives at your organization's kiosk address (for example, `your-org.kiosk.1club.ai`). When only one gym has the kiosk enabled, the address resolves to it automatically; with several enabled, the URL includes the gym's slug.
Go to **Settings** > **Member Apps** > **Kiosk App**
(`/settings/member-experience/kiosk`) and turn on **Enable Kiosk** for the
gym. Use **Copy link** to grab that gym's kiosk URL.
Open the kiosk URL in the device's browser. Chrome kiosk mode or iPad Guided
Access keep the device locked to the kiosk.
Sign in with a staff account. Only users with admin-portal access for that
organization can set up a kiosk: admin, manager, front desk, instructor, or
the owner. The device stays signed in afterward.
When more than one gym has the kiosk enabled, choose the gym after signing
in. A single enabled gym skips the picker.
To switch the device to a different gym later, long-press the logo on the kiosk to re-enter setup.
The person who signs in only provisions the device. Members and visitors use
the kiosk without any account of their own.
## Member check-in
From the **Check in** tile (or a class's check-in screen), a member identifies themselves. The kiosk previews what the check-in would use, shows a confirmation screen with the member's name and the entry it will record, and commits it only after staff or the member confirms. An abandoned confirmation clears itself after 30 seconds.
### Identification methods
Which methods appear depends on your [Access control](/settings/access-control) identification methods and the kiosk toggles below.
| Method | How it works | Enabled by |
| :- | :- | :- |
| **QR code** | The kiosk camera or a connected scanner reads the member's QR code. | Access control identification method **QR Code** |
| **Access card** | The member taps or scans a physical card. | Access control identification method **Access Card** |
| **PIN code** | The member enters the last 4 digits of their PIN on the on-screen keypad. | Access control identification method **PIN Code** |
| **Phone QR** | The kiosk displays a static QR that the member scans with their phone to open their portal. | Kiosk toggle **Enable check-in with QR** |
| **Search** | Staff search by name or phone and pick the member. | Kiosk toggle **Enable search to check in** |
| **List** | On a class check-in screen, staff see the class's booked, not-yet-checked-in attendees and tap a name. | Kiosk toggle **Enable class roster check-in** |
See [Access cards](/members/member-cards) for the QR, card, and PIN credentials members present.
### What a check-in resolves to
The kiosk mirrors the admin check-in rules and resolves the member to one of these entries:
* **Booking** - the member has a confirmed booking whose check-in window is open at this gym.
* **Membership** - no booking, but the member holds an eligible membership for general entry. If the plan limits sessions, the confirmation screen shows uses remaining and the visit decrements it.
* **Free class walk-in** - the member walks in to a free class.
* **Membership class walk-in** - the member walks in to a class covered by an eligible membership.
If the member is already checked in for that booking or class, the kiosk shows a warning instead of recording a duplicate.
### When the front desk is required
Some cases cannot be completed unattended and the kiosk stops the member with a front-desk-required result. Staff handle these from the admin. This happens when:
* The member has an active membership, but it has an unpaid balance, a scope that does not apply, or no uses left.
* The only way in is a pay-per-visit (direct payment) or an [external program](/settings/external-programs), which needs operator validation.
* A booking has an outstanding balance that must be paid first.
For the full matrix of when a member gets in, what payment is enforced, and how walk-ins are handled, see [Check-in models](/reference/check-in-models).
## Self-enrolling a credential
When credential enrollment is enabled in [Access control](/settings/access-control), a member can register their own access card at the kiosk. The member first authenticates (for example by scanning their phone QR), then taps or scans the new card to enroll it. If enrollment is turned off, the kiosk tells the member it is not available.
## Browsing and booking
### Classes
The **Classes** tile shows the schedule by day. A member taps a class to see its details, then starts check-in from there.
### Areas and courts
Each area type you choose to show appears as its own tile on the home screen. A member picks the area and time slot, reviews the booking summary, and scans the displayed QR code with their phone to complete the booking in their portal.
### Plans
The **Plans** tile lists the plans available at that gym with pricing and session details. A member taps a plan, reviews it, and scans the displayed QR code to subscribe from their phone.
### Products
The **Products** tile displays your product catalog, grouped by category with prices, for members to browse on-screen.
## Kiosk settings
Configure the kiosk per gym under **Settings** > **Member Apps** > **Kiosk App** (`/settings/member-experience/kiosk`). A live preview on the left reflects each change. Enable **Enable Kiosk** for a gym first to reveal the rest:
| Setting | What it controls |
| :- | :- |
| **Enable check-in on home screen** | Show the **Check in** tile. On by default. |
| **Enable check-in with QR** | Show the static phone QR members scan to open their portal. Off by default. |
| **Enable search to check in** | Let staff search by name or phone to check a member in. Off by default. |
| **Enable class roster check-in** | Show the **List** of booked attendees on a class check-in screen. Off by default. |
| **Show classes** | Show the **Classes** tile. On by default. |
| **Show plans** | Show the **Plans** tile. On by default. |
| **Show products** | Show the **Products** tile. Off by default. |
| **Area types to show** | The area types that appear as home-screen tiles. Empty means none. |
| **Instructor types to show** | The instructor types the kiosk surfaces. Empty means none. |
| **Background image or video** | Media shown full-screen behind the home screen, with a darkening overlay. |
The QR, access card, and PIN identification tabs come from your [Access
control](/settings/access-control) settings, not from this page. A gym with
only PIN or card enabled still shows the **Check in** tile so staff can reach
the check-in screen.
## Check-in App
The **Check-in App** is a lighter, check-in-only screen for a tablet at the door. Members find themselves by name, QR code, or PIN and check in, with no booking or browsing tiles. Use it instead of the kiosk when all you need at the entrance is self check-in. Each app is turned on separately, so a gym can run either or both.
Configure it per gym under **Settings** > **Member Apps** > **Check-in App**. The **Quick setup guide** at the top shows your **Organization short name**, which you type into the app when you set up a new device. Then:
1. Turn on the gym under **Enable Check-in App**, then click **Copy** to grab its check-in URL.
2. Open the URL on the device's browser (Chrome kiosk mode or iPad Guided Access work well).
3. Sign in once with your admin account. The device stays on the check-in screen.
Once a gym is on, its settings appear below it:
| Setting | What it controls |
| :- | :- |
| **Check-in modes** | What members can check in to: **Classes**, **Open Gym**, or both. With both, members first pick Gym or Classes. At least one is required. |
| **Reset after inactivity (seconds)** | How long the screen waits with no interaction before it returns to the start. 10 to 600 seconds. |
| **Show splash screen** | A branded attract screen between visitors, with an optional **Background image or video**. |
| **Show full member names** | When off, names are shortened for privacy, for example "John K.". |
| **Identification methods** | Name search is always on. Turn on **Check in with PIN** and **Check in with QR code** (members hold their 1club app QR code up to the tablet camera) as needed. |
| **Method order** | With both PIN and QR on, drag to choose which members see first. |
| **User journey** | With classes offered, drag to choose whether members identify themselves or pick a class first. |
A live preview beside the settings shows the app for an enabled gym.
## Related
* [Check-in models](/reference/check-in-models) - how members get in, and when payment is enforced
* [Access control](/settings/access-control) - identification methods, check-in mode, and credential enrollment
* [Access cards](/members/member-cards) - the QR codes, cards, and PINs members present at the kiosk
* [Attendance](/schedule/attendance) - review the check-ins the kiosk records
# Member portal
Source: https://docs.1club.ai/settings/member-portal
Configure the member-facing portal under Settings > Member Apps > Member Portal - turn the portal on, choose which pages members see, group and publish pages, and confirm the portal web address.
The member portal is the self-service web app your members sign in to. You decide whether the portal is on, which pages appear in its navigation, how those pages are grouped, and which of them the public can browse without signing in.
The portal is served on your organization's subdomain, `{slug}.portal.1club.ai`, or on a custom portal hostname if you have verified one. The slug itself is set on the [Organization](/settings/organization) page, not here.
## When to use it
Shape the portal around what your members actually do, so they find their bookings, plans, and messages without asking the front desk. You can also open selected pages to visitors, so prospects see what you offer before they create an account.
Typical uses:
* **A public class list for an ad** - a padel gym marks its **Classes** page public, so an ad link shows the weekly classes to anyone and booking prompts a sign-in.
* **Grouping programs** - a martial-arts gym puts its jiu-jitsu and muay-thai pages in one "Programs" folder in the portal navigation.
* **Two booking calendars** - a yoga studio adds a second **Calendar** page so reformer sessions and mat classes each get their own calendar.
* **Self-service help** - a climbing gym turns on the **AI assistant** so members can ask what beginner classes are on this week and get booking help without messaging a person.
See [Worked examples](#worked-examples) for the steps behind each of these.
## Where the portal address comes from
The portal URL is built from your organization **Slug**:
```
https://{slug}.portal.1club.ai
```
If you have added and verified a domain with purpose **Portal subdomain** under **Settings** > **Organization** > **Domains**, the portal is served on that hostname instead. The web address is read-only on this page.
If your organization has no slug yet, the portal cannot generate a URL. The page shows an **Organization Slug Required** warning with a **Go to Organization Settings** button. Set the slug on the [Organization](/settings/organization) Details page first. This is the same subdomain that [forms](/marketing/forms) use to build their public links.
## General settings
The portal settings live under **Settings** > **Member Apps** > **Member Portal**. A live preview on the same screen shows the portal exactly as members will see it as you edit, and an **Open** button views it in a new tab.
### Enable member portal
The master switch. When off, members cannot reach the portal at all and the preview is replaced with a disabled notice. Turn it on once your slug is set and you have decided which pages to show. Turning the portal off does not delete any member data; it only closes the front door.
### AI assistant
Adds an AI assistant launcher inside the portal. Members can ask it to help with bookings, memberships, and general club information. This toggle is only editable while the portal is enabled.
### Legal and cookie consent
Turns on legal documents and the cookie-consent experience for the portal. Enabling it seeds a default set of legal documents for your organization if they are missing (the seeding is safe to run repeatedly, so it never duplicates documents you already have). This portal legal toggle is independent of the website's own legal setting.
## Portal pages
The **Portal Pages** panel is where you build the portal's navigation. Each row is a page that members see in their sidebar. You control the set, the order, the labels, the icons, and which pages are public.
### Adding and removing pages
* Use **Add** to pick from pages that are not yet enabled.
* Reorder enabled pages to change the order they appear in the portal navigation.
* Some page types can be added more than once. For those, the Add menu offers **Add another ...** so you can create a second instance (for example, more than one booking calendar). Extra instances get their own slug and label.
### Page types you can enable
Core pages:
* **Home** - the member's home screen.
* **Classes** - browse and book classes.
* **Plans** - view available plans.
* **Inbox** - the member's message thread with your gym.
* **My Account** - profile, memberships, and account settings.
Optional pages:
* **Posts** - announcements and updates.
* **FAQs** - a frequently-asked-questions page.
* **How to guides** - help content.
* **Calendar** - a booking calendar. Supports multiple instances.
* **Book** - a search-and-book entry point.
The old combined **Schedule** page is marked deprecated in the picker. New portals should use the unbundled pages (Classes, Calendar, Book) instead of the single Schedule page.
In addition to the built-in pages, the picker automatically offers one page per **bookable area type** and one page per **instructor type** you have configured, so members can browse, for example, "Courts" or "Coaches" directly. These are driven by your [area types](/settings/areas) and [instructor types](/staff/instructors); add or rename them there and they appear here.
### Per-page settings
Open a page's settings to control how it appears:
* **Icon** - the navigation icon for the page.
* **Label** - a custom navigation label. Leave it empty to use the default, and use the translate option to provide per-locale labels.
* **Public** - whether unauthenticated visitors can see this page (see below).
* **Group** - which group (folder) the page belongs to.
### Page groups
Use **Groups** to organize pages into labelled folders in the portal navigation. This keeps a longer navigation tidy, for example grouping several area-type pages under a "Facilities" folder. Groups have their own label and can be translated per locale.
### Public pages
A public page is visible to visitors who have not signed in. This lets prospects browse your classes, plans, instructors, or areas before creating an account, while still requiring sign-in to actually book or reach their account. Mark a page public from its per-page settings. Only the pages you explicitly mark are exposed; everything else stays behind sign-in.
## How saving works
Changes to the enable toggle, AI assistant, pages, groups, and public flags are staged locally and a floating save bar appears when there are unsaved changes. Click **Save** to apply them. The **Legal and cookie consent** toggle is the exception: it saves immediately when you flip it.
## Worked examples
### Padel club opens a public class browser
A padel and tennis club wants prospects to see the weekly class list from an ad link without signing up. On **Member portal** they enable the portal, add the **Classes** page, open its settings, and mark it **Public**. They leave **My Account** and **Inbox** private. Now the ad link opens the portal's class list for anyone, and booking prompts a sign-in.
### Martial arts gym groups its programs
A jiu-jitsu and muay-thai gym has separate instructor types for each discipline. Those show up as pages in the picker. They enable both, open **Groups**, create a "Programs" group, and assign both pages to it so members see a single "Programs" folder in the portal navigation.
### Yoga studio adds a second booking calendar
A yoga and pilates studio wants one calendar for reformer sessions and one for mat classes. They add a **Calendar** page, then use **Add another Calendar** to create a second instance, give each a custom label and slug, and reorder them.
### Climbing gym turns on the AI assistant
A climbing gym enables the portal, then flips on **AI assistant** so members can ask "what beginner classes are on this week?" and get booking help without messaging a human. Because the toggle only works while the portal is enabled, they enable the portal first.
## Tips and best practices
* **Set the slug before enabling the portal.** Without it there is no URL and the page blocks with a warning. The slug is on the [Organization](/settings/organization) page.
* **Preview before you announce.** The live frame renders the real portal, so use it to confirm the navigation looks right before you share the link.
* **Keep public pages minimal.** Expose only what helps a prospect decide (classes, plans), not member-only pages.
* **Prefer the unbundled pages** over the deprecated Schedule page for new portals.
* **Use groups once you have many pages**, especially when area-type and instructor-type pages multiply the navigation.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| Portal preview shows a "disabled" notice | The portal is turned off | Flip **Enable member portal** on and save |
| "Organization Slug Required" warning, no portal URL | The organization has no slug | Set the **Slug** on the [Organization](/settings/organization) Details page |
| AI assistant toggle is greyed out | The portal is disabled | Enable the portal first, then set the AI assistant toggle |
| A page members expect is missing | It is not in the enabled list | Add it from the **Add** menu in Portal Pages and save |
| Prospects cannot see classes without signing in | The page is not marked public | Open the page's settings and turn on **Public** |
| Custom portal domain not used | The domain is not verified | Verify a **Portal subdomain** domain under **Settings** > **Organization** > **Domains** |
| A page's custom label is not showing in another language | No translation set for that locale | Use the translate option in the page's label settings |
## Related
Set the slug that becomes your portal subdomain, and verify a custom portal domain.
Public onboarding forms served on the same portal subdomain.
Area types that surface as bookable pages in the portal picker.
Instructor types that surface as pages in the portal picker.
# Notifications
Source: https://docs.1club.ai/settings/notifications
Choose which notification categories you receive and on which channels, set the defaults for each role under Settings > Organization > Notifications, and understand how staff alerts route to the right people.
Notification settings control which categories of notifications you personally receive and on which channels they reach you. A **notification** is the system's record of something that happened, addressed to a person ("a member booked a court", "a renewal payment failed"). It is delivered through one or more **channels** - App, email, or SMS - and which channels fire for you, per category, is exactly what this page governs.
These preferences are per user: each staff member sets their own, starting from the defaults an admin sets for their role (see [Role defaults](#role-defaults)). They do not change what the system generates, only how it reaches you.
## When to use it
Tune these so the alerts that matter reach you on the channel you actually check, and the rest stay quiet. A studio owner might keep new memberships and signups on while leaving every class booking off, an instructor keeps the alerts about their own classes, and a manager who prefers email switches categories to **Email** only. See [Worked examples](#worked-examples) for these setups.
## Managing your preferences
Open the account menu at the bottom of the admin menu and choose **My Notifications**. Each row is one category with:
1. **The category label** on the left.
2. **A switch** to enable or disable that category for you.
3. **A Channels selector** (shown only when the category is enabled) where you pick the delivery channels.
Changes save immediately when you toggle a switch or change channels - there is no separate save button. A confirmation toast appears on success.
## Notification categories
The categories fall into two groups: your own personal activity, and staff alerts about what members do.
### Personal categories (always available)
These concern your own account and are offered to every staff member:
| Category | Label | What it covers |
| - | - | - |
| `message` | My inbox | New messages sent to you. |
| `task` | My tasks | Task-related notifications assigned to you. |
| `booking` | My bookings | Activity on your own bookings. |
| `billing` | My billing | Your own billing notifications. |
### Staff-alert categories (shown when relevant to you)
These notify staff about member actions. You only see a category if it can actually route to you (see [Which categories you see](#which-categories-you-see)):
| Category | Label | Fires when | Routes to |
| - | - | - | - |
| `user_booking` | User bookings | A member books or cancels | Admins, managers |
| `class_instructor` | Class bookings | A member books or cancels a class | The class's instructors |
| `user_membership` | User memberships | A member buys a membership | Admins, managers |
| `user_message` | User messages | A member sends an inbound message | Admins, managers, front desk, instructors |
| `user_review` | User reviews | A member leaves a review | Admins and managers; the reviewed session's instructor |
| `user_signup` | User signups | A new member signs up themselves | Admins, managers |
Renewal-payment-failure alerts route to admins and managers and are delivered under your **My billing** category.
Per-event booking alerts for a specific event are gated by that event's own "notify staff" setting, not by a toggle here. When an organizer turns that on, the alert always reaches admins and managers regardless of their personal **User bookings** preference, so it does not appear as a separate switch on this page.
### Which categories you see
The list is filtered to what you can receive:
* **Personal categories** (My inbox, My tasks, My bookings, My billing) always appear.
* **Role-based staff categories** appear when your role is in the alert's recipient roles. An admin or manager sees User bookings, User memberships, User signups, and so on; front desk sees User messages.
* **Instructor categories** (Class bookings, and instructor reviews) appear when you have an instructor profile, so you're notified about your own classes and sessions.
Owners are treated as admins for relevance, so they see every category.
## Channels
A notification can be delivered on any of these channels:
* **App** - Reaches you inside 1Club: the notification center in the dashboard, plus an alert on any phone or browser where you have allowed notifications. These are one channel, not two. Which of your devices light up depends on what you have connected, not on anything you pick here.
* **Email** - Sent through SendGrid, using your organization's branding.
* **SMS** - A text message from your gym's own [SMS sender](/settings/sms), used for staff alerts where a mobile number is on file.
In this settings screen you select **App** and **Email** per category. SMS is used by the platform for staff alerts where configured, and rides the same per-category preferences model.
**App** was previously two separate channels, **In-app** and **Push**. They were merged because they never really were separate: switching off In-app did not stop the phone alert. Anything you had set to In-app is now set to **App**, so you may start seeing device alerts you did not get before. To stop those while keeping the notification center, turn off notifications for 1Club in your phone or browser settings rather than here.
### How channels resolve
For each category, the effective channels are the ones you selected, intersected with the channels that category actually supports - you never receive a channel you didn't ask for. Beyond that:
* **Category off** - You receive nothing for that category, on any channel.
* **On-by-default categories with no saved preference** - You receive them on their default channels (App and Email).
* **Opt-in categories** (User bookings and My bookings) - You receive nothing until you enable them; they start off by design to avoid noise from high-volume booking activity.
Deleted, invited (invitation not yet accepted), and inactive accounts are never notified on any channel, regardless of preferences. This is a structural safeguard, not something you configure.
## Role defaults
An admin sets the starting point for everyone in a role under **Settings** > **Organization** > **Notifications**. Pick a role from the **Roles** list (each shows its user count and whether it is **Default** or **Customized**), and for each notification category set:
* **The switch** - whether the category is on for the role.
* **Channels** - which channels it uses. A category that is on needs at least one channel.
* **Allow users to change** - when off, the category is **Locked**: users of that role no longer see it on their own **My Notifications** list and always get the role's setting.
Save your changes before you use the other actions:
* **Apply to all** - replaces the setting for everyone in the role, including people who changed it themselves. It asks for confirmation and cannot be undone.
* **Reset to Default** - removes your configuration for the role and returns it to the built-in defaults. Users who changed a setting themselves keep their own choice.
Role defaults only reach users who have not changed that setting themselves. A note under a category says how many users have their own setting and will not get the default; use **Apply to all** to replace theirs.
## Staff notification routing
Staff alerts about member actions are resolved and delivered automatically. Understanding the routing helps you set expectations for your team:
* **By role** - Alerts like New booking, New membership, and New signup go to every active user whose role matches (admins and managers for most; front desk and instructors are also included for inbound messages).
* **By instructor** - Class-booking and instructor-review alerts resolve to the instructors of the specific class or session involved: the directly assigned instructor for one-to-one sessions, plus the class's instructors for class bookings. Only active instructors with an active linked account are reachable.
* **Deduplicated** - If one person matches more than one rule for the same event (for example an admin who also teaches the booked class), they receive a single notification, not one per rule.
* **Per-recipient channels** - Each recipient's own notification preferences decide which channels fire for them. One person may get a new booking in the app while another gets it by email.
Delivery per channel is independent: an email failure doesn't suppress the App notification, and vice versa.
## Worked examples
### Yoga studio owner who wants signal, not noise
You run a busy pilates and yoga studio and don't want a ping for every class booking.
* Leave **User bookings** off (it starts off anyway).
* Keep **User memberships** and **User signups** on with **App** and **Email** so you see new members and new revenue.
* Keep **My inbox** on so member replies reach you.
### Instructor at a climbing gym
You teach classes and want to know about your own sessions only.
* **Class bookings** appears because you have an instructor profile - keep it on to be told when someone books your class.
* **User reviews** appears too - keep it on to see reviews of your sessions.
* You won't see admin-only categories like User signups, because they never route to you.
### Front desk at a racket-sports club
You triage member enquiries at reception.
* **User messages** appears for your role - keep it on with **App** so inbound member emails and messages surface immediately at the desk. A member's email reply to a message you sent alerts in the app only, never by email, because your gym's mailbox already has the reply.
* Turn on **User bookings** if reception also tracks new court bookings; leave it off if that's noise for you.
### Studio manager who prefers email over app notifications
You'd rather everything arrive by email.
* For each staff category you care about, open its **Channels** selector and choose **Email** only, removing **App**.
* Those categories then skip the notification center and your devices, and land in your inbox instead.
## Tips & best practices
* **Start from the defaults.** Most categories are on with App and Email out of the box. Turn things off rather than trying to enable everything.
* **Opt in to booking alerts deliberately.** User bookings and My bookings are off by design; enable them only if you actually want per-booking pings.
* **Use channels to match how you work.** Front-desk staff often prefer App; owners who live in email can drop App for staff categories.
* **Remember routing is role-based.** If a colleague isn't getting an alert, check their role and instructor profile before assuming a preference is wrong - some categories simply never route to some roles.
## Troubleshooting
**Issue**: I'm not receiving a category I expect.
**Cause / fix**: Either the category is toggled off, or it's an opt-in category (User bookings, My bookings) you haven't enabled. Turn the switch on and confirm at least one channel is selected.
**Issue**: A category I want isn't even listed on the page.
**Cause / fix**: The list only shows categories that can route to you. Admin-only categories won't appear for a front-desk or instructor account, and instructor categories require an instructor profile. Adjust the person's role or instructor profile if they genuinely need it.
**Issue**: I enabled a category but still get nothing.
**Cause / fix**: Check the **Channels** selector for that row - if no channels are selected the category effectively delivers nothing. Also confirm the account isn't invited-but-not-accepted or inactive, which never receive notifications.
**Issue**: A staff member gets duplicate alerts for the same booking.
**Cause / fix**: They shouldn't - matching multiple routing rules for one event is deduplicated to a single notification. If you see genuine duplicates, confirm they aren't two different categories (for example a class instructor also being an admin who enabled User bookings), which are distinct notifications by design.
**Issue**: Turning off App didn't stop the email (or vice versa).
**Cause / fix**: Channels are independent. Removing **App** stops both the notification-center entry and the device alert; you must also remove **Email** from that category's channels to stop the email.
**Issue**: I started getting alerts on my phone for a category I had set to In-app.
**Cause / fix**: In-app and Push are now a single **App** channel, so a category you had on In-app now also alerts your devices. Either remove **App** from that category here, which also drops it from the notification center, or leave it on and turn off notifications for 1Club in your phone or browser settings to keep the notification center without the interruption.
## Related
* [Inbox](/inbox/messages) - Where member messages that trigger message notifications live.
* [Email inboxes](/settings/email-inboxes) - Capture inbound member email so it generates new-message alerts.
* [Admin users](/settings/admin-users) - Roles determine which staff-alert categories a person can receive.
* [Profile](/settings/profile) - Your other personal settings.
# Organization
Source: https://docs.1club.ai/settings/organization
Configure organization-wide identity, address, time zone, locales, clubs, branding, and custom domains in 1Club Settings.
Your organization settings hold the identity that applies to your whole 1Club account, not to any one gym: name, slug, headquarters address, time zone, contact info, and supported languages. Owners and admins manage them. This page is the anchor for the wider Organization category, which also covers your clubs, branding, custom domains, users, and your 1Club platform subscription.
Per-gym things like opening hours, address, and check-in mode live on each club under **Settings** > **Clubs**, not here. The two scopes exist for different jobs: the organization owns brand and identity, and each gym owns its own physical setup. See [Organization and clubs](/reference/organization-clubs).
## When to use it
You set these up once when you join 1Club, then come back when something about the business changes - a new name, a new location, a new language, or your own web address. Getting them right keeps your name, portal address, time zone, and languages consistent everywhere members and staff see them.
Typical uses:
* **Rebranding** - a padel and tennis gym changes its name, moves to a new portal address, and uploads a new logo.
* **Opening a second location** - a martial-arts gym adds a second location with its own address, time zone, sports, and hours, leaving the organization time zone untouched.
* **Serving members in two languages** - a yoga studio supports English and Bulgarian, with English as the default.
* **Using your own domain** - a climbing gym serves its member portal on its own hostname instead of the default subdomain.
See [Worked examples](#worked-examples) for the steps behind each of these.
## What you can edit on Details
Open **Settings** > **Organization** > **Details**. The Details page is a single form with three sections.
### Details
* **Name** - Your organization's display name. Used in emails, the member portal, and admin headers. Required.
* **Slug** - The URL-safe identifier. Lowercase letters, numbers, and hyphens only, and up to 40 characters. It becomes your member portal subdomain (`{slug}.portal.1club.ai`) and shows up in public links, so keep it short. Required, and it must be unique across all organizations.
* **Description** - Optional short blurb shown on public surfaces such as the marketplace listing.
### Contact info
* **Address** - Type it or pick from the map autocomplete. When you select a suggestion, latitude and longitude are saved alongside the address so the server can derive your time zone.
* **Phone** - International format. The country code defaults from your address country.
* **Time zone** - The default for everything date-related across the admin: today and this-week widgets, recurring jobs, and exports. The address selection pre-fills it, and you can override it manually.
* **Email** - The organization's contact address. Members and the marketplace may surface it, and it is also a candidate sender for organization-wide email such as invoices and notifications. Must be a valid email address. See [Email sending](/settings/email-sending) for how the From address on member email is chosen.
* **Website** - Your main public site, if any. Must be a valid URL.
### Localization
* **Supported locales** - The languages members can pick in your portal and that the admin offers in user profiles. The available options are English, Spanish, French, German, Bulgarian, and Czech.
* **Default locale** - The language used when a member's preference is not known. Defaults to English.
## How saving works
The form uses a floating save bar. Edit any field and a **Save** control appears at the bottom. Nothing is auto-saved, so you must save before leaving. After you save, the new values propagate to every place in the admin that reads organization settings; most pages refresh without a reload.
### About the time zone
The organization time zone is resolved from your coordinates or address only once, the first time it is set. Editing your address later does not silently move the org time zone, so if you relocate the headquarters, set the time zone yourself. Individual clubs behave differently: a club re-derives its own time zone whenever its address changes.
Currency and billing preferences are not on this page, and neither is your legal identity: the legal name, tax and registration numbers, registered address, bank details, and invoice numbering belong to the company you invoice through, under **Settings** > **Billing** > **General**. Most gyms have one; see [Billing entities](/settings/billing-entities). Form field configuration - which contact fields are required, optional, or hidden - lives under **Settings** > **Members** > **Contacts**. See [Members overview](/members/overview).
## Clubs
A club is a single physical location. Everything schedulable (areas, classes, staff shifts, check-in) belongs to a club, so you need at least one before members can book.
### Creating or editing a club
1. Go to **Settings** > **Clubs**. The list shows every location; filter by name, city, or state.
2. Click **Add Club** to open the club form, or click an existing club to edit it.
3. Fill in **Basic information** (**Club Name**, **Subtitle**, **Slug**, **Description**) and **Contact information** (**Email**, **Phone**, **Website**, address).
4. Set **Operating hours**, **Sports**, and **Amenities**.
5. Optionally assign **Club Managers** (staff who receive contact-form messages for this club) and upload **Club Images**.
6. Save.
### Club fields worth knowing
* **Email** - The location's contact address, and the **From** address on most member email for this club (booking confirmations, cancellations, daily summaries). Keep it on a domain you have verified. See [Email sending](/settings/email-sending).
* **Slug** - Must be unique across clubs; it appears in the club's public page URL.
* **Status** - A club is `active` or `inactive`. Inactive clubs stay in the admin but are pulled from public surfaces.
* **Sports and amenities** - Drive the marketplace listing and filtering. A club must be complete to stay published; an edit that leaves it incomplete is automatically unpublished.
* **Google Business Profile / OpenStreetMap** - Connect a club to import address, hours, sports, amenities, photos, and reviews. Empty fields are filled and existing values are kept.
* **Daily summary** - Per-club summary email to admins, with a configurable send time. New clubs get it on by default.
* **Kiosk options** - Per-club kiosk toggles (check-in, classes, plans, products) are configured here and stored against the club.
Deleting a club is blocked while it still has dependent data (bookings, areas, and so on); remove or reassign that first.
## Branding
Logo, favicon, colors, and fonts are configured under **Settings** > **Member Apps** > **Branding**, not on the Details page. Branding covers:
* **Logos and images** - Light logo, dark logo, logo alt text, and favicon.
* **Color scheme** - Primary color, header color, and their dark-mode variants.
* **Typography** - Body font and heading font.
Branding customization is not available on the **Free** plan. On Free, the page loads with a banner saying branding is not available in your plan, and saving is blocked.
## Domains
Custom domains are managed under **Settings** > **Organization** > **Domains**. Each domain is attached for one **purpose**:
* **Email (SendGrid)** - Send member email from your own domain. See [Email sending](/settings/email-sending) for the full setup, including which address members see.
* **Website (CMS)** - Serve your CMS website on your own domain.
* **Portal subdomain** - Serve the member portal on your own hostname instead of `{slug}.portal.1club.ai`.
### Adding and verifying a domain
1. Click **Add Domain**, enter the fully-qualified domain, and choose the purpose.
2. 1Club shows the **DNS records to add** (type, name, value). Add them at your DNS provider.
3. For website and portal domains, delete any leftover A, AAAA, or CNAME records for the same host first, or verification will fail.
4. Wait for DNS to propagate, then click **Verify**.
Domain status moves through:
* **Pending verification** - Added, DNS not yet confirmed.
* **Issuing SSL certificate** (`ssl_pending`) - DNS is confirmed, but the TLS certificate is still being issued. The domain is intentionally not live yet.
* **Verified** - DNS confirmed and a valid certificate is being served. The domain is live.
* **Failed** - Verification did not succeed.
## Subscription
Your 1Club platform plan lives under **Settings** > **Organization** > **Subscription**. This is what you pay 1Club (and where plan gates such as branding are decided), and it is separate from how you bill your own members, which is configured under **Settings** > **Billing**.
## Who can edit
Organization settings are gated on the organization settings permission, part of the **Admin** role's default bundle. Roles without it see the page read-only or get a permission error. Creating clubs and editing branding and domains require the organization management permission. See [Roles and permissions](/reference/roles-permissions).
## Worked examples
### Racket club rebrands and moves online-first
A padel and tennis club changes its name and wants a cleaner portal address. On **Details** they update **Name**, then edit **Slug** to `citypadel` so members reach `citypadel.portal.1club.ai`. Old links using the previous slug stop working, so they announce the new address. Under **Member Apps** > **Branding** they upload a new logo and set the primary color; because they are on the Run plan, the save succeeds.
### Martial arts gym opens a second location
A jiu-jitsu and muay-thai gym adds a second mat space. They go to **Settings** > **Clubs** > **Add Club**, name it, set its own address (which derives that club's time zone), select the `jiu-jitsu` and `muay-thai` sports, and set operating hours. The organization time zone is untouched; only the new club picks up the local zone.
### Multi-language yoga studio
A yoga and pilates studio serves English and Bulgarian members. Under **Localization** they add English and Bulgarian to **Supported locales** and set **Default locale** to English. Members now choose their language in the portal, and anyone whose preference is unknown sees English.
### Serving the portal on a custom domain
A climbing gym wants members to visit `members.gripcity.com` instead of the default subdomain. Under **Organization** > **Domains** they add the domain with purpose **Portal subdomain**, add the CNAME record shown, remove an old parking-page record, and click **Verify**. The domain sits in **Issuing SSL certificate** for a few minutes, then flips to **Verified** and goes live.
## Tips and best practices
* **Choose the slug carefully and early.** It is your portal web address. Changing it later breaks existing links and QR codes.
* **Set the address by selecting a map suggestion**, not by typing freehand, so coordinates and time zone resolve correctly.
* **Only enable locales you can support.** Adding a language exposes it to members even if your content is not translated yet.
* **Keep club status honest.** Mark a location inactive rather than deleting it if you might reopen; deletion is blocked while data exists anyway.
* **Verify domains during low traffic.** Website and portal domains are not live until the certificate is issued, so plan the cutover.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| Save fails with "slug is already in use" | Another organization already claimed that slug | Pick a different slug; it must be globally unique |
| Save fails saying the slug is too long | The slug exceeds 40 characters (it is your portal subdomain) | Shorten it to 40 characters or fewer |
| Time zone did not change after editing the address | The org time zone resolves only once, when first set | Set the **Time zone** field manually |
| Branding page blocks saving | You are on the Free plan | Upgrade to a paid plan under **Organization** > **Subscription** |
| Custom domain stuck on "Issuing SSL certificate" | DNS is confirmed but the certificate is still being issued | Wait a few minutes and click **Verify** again |
| Custom domain will not verify | Conflicting A, AAAA, or CNAME records for the same host | Delete the old records at your DNS provider, then verify |
| Cannot delete a club | It still has bookings, areas, or other dependent data | Remove or reassign that data first |
| The Organization page is read-only | Your role lacks the organization settings permission | Ask an Admin to grant it |
## Related
* [Email sending](/settings/email-sending) - Verify an email domain and set the addresses members see.
* [Organization and clubs](/reference/organization-clubs) - How orgs and gyms relate, and which settings live where.
* [Settings overview](/settings/overview) - The full map of Settings categories.
* [Roles and permissions](/reference/roles-permissions) - Who can edit organization settings.
* [Areas](/settings/areas) - Bookable spaces created within each club.
* [Profile](/settings/profile) - Per-user preferences (language, theme, password).
* [Members overview](/members/overview) - Contact and signup field configuration.
# Settings overview
Source: https://docs.1club.ai/settings/overview
Orient yourself across the 1Club Settings area - the thirteen categories, in the same order as the admin menu, that hold your organization, clubs, members, bookings, sales, billing, events, member apps, access control, AI, integrations, and data tools configuration.
Settings is the admin area where you configure everything that is not day-to-day work: your organization identity, clubs, member and lead behavior, booking rules, the till and billing, events, member-facing apps, access control, AI, integrations, and data tools. Open it from **Settings** at the bottom of the admin menu. Its categories follow the order of the admin menu, so the settings for each section sit where that section does.
## How Settings is organized
* **Category sidebar** - Settings pages are grouped into thirteen categories in a column on the left. Selecting a category opens its first page, and a category with several pages shows them as tabs across the top. A category with a single page, such as **Events** or **Integrations**, opens that page directly. On a phone, Settings opens on a list of the categories instead.
* **Permission-gated** - Settings pages require the organization-level settings permission, so what you see depends on your role. A few pages ask for more (**Permissions** is admin-only). See [Roles and permissions](/reference/roles-permissions).
* **Organization-scoped** - Almost every setting applies to your whole organization. Per-gym details (opening hours, address, booking labels, kiosk options) live on each club instead.
* **Global search** - Settings pages and their fields are indexed, so you can jump to a specific setting from the admin search instead of hunting through categories.
Your own profile and notification preferences are not in Settings. Open them from the account menu at the bottom of the admin menu: **Profile Settings** opens your [profile](/settings/profile) and **My Notifications** opens your [notification preferences](/settings/notifications).
## Settings categories
### Organization
Organization-wide identity, staff access, and your 1Club account.
* **[Details](/settings/organization)** - Organization name, slug, description, contact info, time zone, and supported languages.
* **[Subscription](/settings/platform-subscription)** - Your 1Club platform plan and billing portal. This is what you pay 1Club, not member billing.
* **[Users](/settings/admin-users)** - Staff accounts and their roles.
* **Permissions** - What each role can do, edited per role. Admin-only. See [Roles and permissions](/reference/roles-permissions).
* **[Notifications](/settings/notifications#role-defaults)** - Which notifications each role receives by default, and which of them users may change.
* **[Domains](/settings/organization#domains)** - Custom domains for your website, member portal, and outgoing email. See [Email sending](/settings/email-sending) for the email purpose.
* **[APIs & webhooks](/settings/api-tokens)** - API tokens and webhook endpoints for programmatic integrations.
### Clubs
Your locations and the shared vocabulary used to categorize spaces, staff, and classes.
* **[Clubs](/settings/organization#clubs)** - Add and edit gym locations. Each club also holds its own [booking labels](/reference/organization-clubs#booking-labels), the names members see on the booking cards.
* **[Areas](/settings/areas)** - The bookable courts, rooms, and studios at each gym.
* **Area Types** - Space categories such as Tennis Court or Reformer Studio, with the sports played in them.
* **Instructor Types** - Instructor categories such as Personal Trainer or Yoga Instructor. See [Instructors](/staff/instructors).
* **[Class Types](/settings/class-types)** - Class categories, including age group, party size, and the progression track a class counts toward.
### Members
How contacts, tags, progression, and leads behave.
* **Contacts** - Contact fields and which are required, optional, or hidden.
* **[Smart Tags](/settings/smart-tagging)** - Rule-based tags applied automatically (financial, lifecycle, membership, attendance, age, and more).
* **[Manual Tags](/settings/smart-tagging#manual-tags)** - Tags you apply by hand.
* **[Progression Levels](/settings/progression)** - Skill or belt progression tracks for members.
* **[Assessments](/members/assessments)** - Templates of criteria and a scale that instructors score members on after each training.
* **[Pipeline](/members/leads#pipeline-settings)** - Lead pipeline stages, the Cold rule, and contact sources.
### Bookings
The rules the schedule and front desk run on.
* **[Booking and cancellation policies](/settings/booking-policies)** - When payment is enforced, whether check-in is required, walk-ins, cancellation refunds, booking windows, and slot limits.
* **[Waivers](/settings/waivers)** - Documents members must sign and the document rules that require them.
* **[Schedule Display](/schedule/calendar)** - Staff and source coloring on the schedule, and which classes, instructors, and areas it shows by default.
### Communication
* **[Templates](/settings/templates)** - Reusable email, message, and document templates.
* **[Email Inboxes](/settings/email-inboxes)** - Inboxes that receive the mail members send to your gym's address.
* **[SMS](/settings/sms)** - Your gym's SMS senders: business details, sender name, country registrations, and a toll-free number for the US and Canada.
The From address members see on outbound email is set on the organization and each club. See [Email sending](/settings/email-sending).
### Sales
How the till and stock work.
* **[Point of Sale](/pos/point-of-sale)** - Point of sale behavior for products, plans, and checkouts.
* **[Inventory Management](/sales/inventory)** - Stock tracking for products, ingredients, and stock movements.
* **[Rentals](/sales/rentals)** - Lending gear out at the counter and tracking what has not come back.
* **[Cash Registry & Expenses](/sales/cash)** - The cash drawer, cash-flow tracking, and expenses.
* **Payment terminals** - Card terminals your staff can send a sale to for in-person payments, from every provider you use. See [Stripe](/settings/integrations/stripe#payment-terminals) and [myPOS](/settings/integrations/mypos).
### Billing
Financial configuration for member billing.
* **General** - Currency and whether prices show with tax, plus sections for [Billing entities](/settings/billing-entities) (the companies you invoice through, with their legal details, invoice numbering, and bank details), **Tax Rates**, **Revenue Accounts**, [Payment Methods](/settings/payment-methods), and [External Programs](/settings/external-programs).
* **[Pricing Rules](/settings/pricing-rules)** - Automatic discounts and surcharges by time, demand, and conditions.
* **[Seasons](/settings/seasons)** - The periods a season pass is sold for, and the volume discount for holding more hours a week.
* **Automated Billing** - The schedule that generates recurring invoices. See [Bill runs](/sales/bill-runs).
### Events
A single page with two switches:
* **Show Events** - Shows the [Events](/events/events) section in the admin menu. On by default.
* **Enable competition features** - Turns on tournaments, [matches](/events/matches), and 1club Rating. Off by default, and needs **Show Events** on, because tournaments run on events.
### Member Apps
Branding and the member-facing channels. Members sign in to every member-facing app with **Google**, **Apple**, **Facebook**, or an email and password. See [Signing in](/reference/sign-in).
* **[Branding](/settings/organization#branding)** - Logo, favicon, colors (light and dark), and typography. Not available on the Free plan.
* **[Member Portal](/settings/member-portal)** - Turn the portal on and choose which pages members see.
* **Mobile App** - What the 1club member app does, and a QR code and store links to share it with your members.
* **[Kiosk App](/settings/kiosk)** - The self-service kiosk, configured per gym.
* **[Check-in App](/settings/kiosk#check-in-app)** - The self check-in tablet app, configured per gym.
The 1club.me marketplace listing is managed under [Marketplace](/marketing/marketplace) in Marketing, not in Settings.
### Access Control
* **[Settings](/settings/access-control)** - Per gym, whether members can enroll their own credentials, and which identification methods (QR code, access card, PIN code) are enabled.
* **[Access grants](/settings/access-control-settings)** - The read-only list of access grants created from bookings and memberships.
### Artificial Intelligence
* **[AI assistants (MCP)](/mcp/connect)** - Copy the MCP endpoint, review connected assistants, and revoke their access.
* **[Agents](/reference/ai-assistants)** - The admin AI agents and example prompts.
* **[Conversation History](/reference/ai-assistants)** - Review past AI conversations.
### Integrations
* **[Integrations](/settings/integrations)** - Payment providers, door access systems, Google, marketing tools, and messaging channels.
### Data Tools
* **[Import](/settings/import)** - Bring in members, memberships, plans, bookings, and other records from CSV.
* **[Export](/settings/export)** - Export contacts, bookings, and memberships, or QuickBooks-ready accounting data.
* **[Archive](/members/suspend-and-archive#archive-a-member)** - View archived contacts and clean them up permanently.
* **Email activity** - Every email sent for your organization, with its delivery and engagement status.
## Where common tasks live
| I want to... | Go to |
| - | - |
| Rename the organization or change its portal address | **Organization** > **Details** |
| Change my 1Club plan or payment method for 1Club | **Organization** > **Subscription** |
| Add a new gym location | **Clubs** > **Clubs** |
| Add a court, room, or studio | **Clubs** > **Areas** |
| Change logo and brand colors | **Member Apps** > **Branding** |
| Set who pays when and how cancellations refund | **Bookings** > **Booking and cancellation policies** |
| Require a waiver before booking | **Bookings** > **Waivers** |
| Set the currency and billing preferences | **Billing** > **General** |
| Set the legal name, tax number, bank details, and invoice numbering that print on documents | **Billing** > **General**, on the company that issues them - see [Billing entities](/settings/billing-entities) |
| Turn on tournaments and matches | **Events** |
| Connect Stripe | **Integrations** |
| Add a custom website or email domain | **Organization** > **Domains** |
| Change the address member email comes from | **Organization** > **Details** and **Clubs** > **Clubs** (see [Email sending](/settings/email-sending)) |
## Tips and best practices
* **Configure top-down.** Set the organization details and time zone first, then add clubs, then branding and member apps. Downstream pages (schedule, invoices, exports) inherit the organization time zone and currency.
* **Let roles do the gating.** Rather than sharing one admin login, invite staff under **Organization** > **Users** with the narrowest role that fits. Front desk, instructor, and manager roles each expose a different slice of Settings.
* **Keep member billing separate from your 1Club bill.** **Billing** is how you charge members; **Organization** > **Subscription** is how 1Club charges you. They are unrelated.
* **Use search for deep settings.** Individual fields (for example, invoice numbering template or kiosk options) are indexed, so searching is faster than clicking through categories.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| A Settings category or page is missing from your sidebar | Your role lacks the required organization settings permission | Ask an Admin to grant it, or check [Roles and permissions](/reference/roles-permissions) |
| Branding page shows an upgrade message | Branding is not available on the Free plan | Upgrade under **Organization** > **Subscription** |
| Booking policy or tax setting is not taking effect | It is scoped to a specific club, area type, or class type | Open the setting and confirm its scope matches the bookings you expect it to affect |
| Cannot find **Operations** in Settings | Its pages moved: booking policies, waivers, and schedule display to **Bookings**; point of sale, inventory, rentals, and cash registry to **Sales** | Open the new category |
| Cannot find the marketplace listing in Settings | The listing moved to Marketing | Open [Marketplace](/marketing/marketplace) |
## Related
* [Roles and permissions](/reference/roles-permissions) - What each role can access in Settings.
* [Organization and clubs](/reference/organization-clubs) - How organizations and gyms relate, and which settings live at each scope.
* [Organization](/settings/organization) - The organization details, clubs, branding, and domains pages.
* [Profile](/settings/profile) - Your personal account preferences.
# Payment methods
Source: https://docs.1club.ai/settings/payment-methods
Configure which payment methods your organization accepts at checkout and in the member portal, and which one is preselected for member bookings.
Payment methods are the ways your organization can take money - cash, card via Stripe, in-app wallet credit, and any custom manual method you define. You configure them, control where each one is visible, and choose which method members see preselected when they book in the portal. The front desk picks from them at the point of sale, and members see the ones you make visible to them at checkout.
## When to use it
Set up a method for every way money actually reaches you, so each payment is recorded against the right one and your reports show how members paid. Visibility keeps desk-only methods out of the member portal.
Typical uses:
* **Cash at the front desk** - a gym adds a private "Cash" method that staff select at the point of sale, while members never see it in the portal.
* **Card payments in the portal** - a studio connects Stripe and preselects the Stripe Card method, so members booking a class check out with fewer clicks.
* **Bank transfer for invoiced customers** - a gym records transfers from corporate accounts and families against a private "Bank transfer" method when reconciling paid invoices.
* **Wallet credit for drop-ins** - a climbing gym keeps the **Wallet** method visible to members so they can spend topped-up credit at checkout.
See [Worked examples](#worked-examples) for the steps behind each of these.
## Channels
Every method belongs to a channel, which determines how a payment is processed:
* **Manual** - recorded outside any gateway (cash, bank transfer). This is the channel for every method you create.
* **Stripe (Card)** - card processing through your connected Stripe account. Provided as a system method.
* **Wallet** - in-app wallet credit. Provided as a system method.
A method's channel is fixed. To take card payments you don't create a Stripe method - you connect Stripe and the system Stripe Card method appears automatically.
## The payment methods list
Payment methods are configured in the **Payment Methods** section of **Settings** > **Billing** > **General**. The list combines two sources:
* **System methods** - provided by 1Club, marked with a **System** chip. Stripe Card appears here once you connect Stripe. You can't create, edit, or delete these from this page.
* **Organization methods** - manual methods you create yourself, for money you collect outside a payment gateway (cash, bank transfer, and similar).
Each row shows the method's **Name**, **Code**, **Visibility**, and a **Preferred** toggle (for methods available to member bookings). System methods sort to the top of the list.
## Adding a manual payment method
1. Go to **Settings** > **Billing** > **General** and scroll to **Payment Methods**.
2. Click **Add payment method**.
3. Fill in:
* **Name** - what the method is called in the admin and at checkout (for example, "Cash" or "Bank transfer").
* **Code** - a short identifier used in reports and integrations (for example, `cash`, `venmo`, `square`).
* **Visibility** - **Private** (admin only), **Public**, or **Member only** (shown in the member portal and app).
4. Click **Save**.
Manual methods record a payment without going through a gateway - useful for in-person collection or any flow you settle outside Stripe. Every manual method you create belongs to the **Manual** channel automatically; there's no channel picker.
## Editing or deleting a method
* **Edit** - open an organization method to change its **Name** or **Visibility**. The **Code** is locked after creation, because reports and integrations reference it.
* **Delete** - only methods that aren't referenced by any existing payment can be deleted. If a method has been used, change its **Visibility** to hide it instead - this keeps historical payments intact.
* **System methods** can't be edited or deleted here. For Stripe Card, the row's action links to **Configure Stripe integration** so you manage it under [Integrations](/settings/integrations).
## Preferred method for member bookings
Methods that are available to member bookings show a **Preferred** toggle in the list. Turn it on for one method to preselect it when a member books a class in the member portal, reducing checkout friction. Turning on a different method's toggle moves the preference; turn the current one off to leave members with no preselection so they choose explicitly.
## Worked examples
### Accept cash at the front desk
1. Add a manual method named "Cash" with **Visibility** set to **Private**.
2. Front-desk staff can select it on point-of-sale and walk-in payments, while members never see it in the portal.
### Let members pay by card in the portal
1. Connect Stripe under [Integrations](/settings/integrations).
2. The Stripe Card system method appears automatically in **Payment methods**.
3. Make sure it's visible to members, then turn on its **Preferred** toggle so it's preselected for bookings.
### Add bank transfer for invoiced billing
1. Add a manual method named "Bank transfer" with **Visibility** set to **Private**.
2. Use it when reconciling paid invoices for corporate accounts or families that settle by transfer.
### Sell class packs on a wallet at a climbing gym
If members top up an in-app wallet and spend it on drop-ins, keep the system **Wallet** method visible to members so wallet credit is an option at checkout, and reserve a **Private** "Cash" method for the desk.
## Tips & best practices
* **Keep codes stable.** They're referenced in reports and integrations, and can't be changed after a method is created.
* **Hide, don't delete.** Changing a deprecated method's visibility keeps historical payments intact; deletion is blocked once a method has been used.
* **Pick one preferred method.** Only one method is preselected for member bookings - choose the one most members use.
* **Set visibility deliberately.** Anything set to **Public** or **Member only** shows in the member portal and app; use **Private** for desk-only methods like cash.
## Troubleshooting
**A method won't delete**
It's referenced by at least one existing payment. Change its **Visibility** to hide it instead of deleting.
**Members can't pay by card in the portal**
Connect Stripe under [Integrations](/settings/integrations), then confirm the Stripe Card method's **Visibility** allows members to see it.
**The Preferred toggle isn't shown for a method**
Only methods available to member bookings show the toggle. Check that the method's visibility makes it available to members.
**A member sees a method they shouldn't at checkout**
Set that method's **Visibility** to **Private** so it's admin-only.
## Related
* [Integrations](/settings/integrations) - connect Stripe so card payments become available.
* [External programs](/settings/external-programs) - third-party entrance methods whose per-visit fees are collected with these payment methods.
* [Payments](/sales/payments) - review actual payment records by method.
# Platform subscription
Source: https://docs.1club.ai/settings/platform-subscription
Manage your organization's own 1Club (SaaS) subscription - the platform plan tiers Free, Grow, and Run, the 30-day Grow trial, plan add-ons like Website, usage quotas, and the Stripe billing portal.
Your platform subscription is the SaaS plan your organization pays 1Club for - the tier that unlocks features, sets your usage quotas, and determines what add-ons you can buy. Owners and admins manage it. This is entirely separate from the [Plans](/sales/plans) you sell to your own members: those are membership templates you charge members for, billed through your own Stripe account. This page is about what you pay 1Club.
Do not confuse the two. **Platform plan** (Free / Grow / Run) is your SaaS tier with 1Club. **Plan** under [Sales > Plans](/sales/plans) is a membership or pass template you sell to members. They use different billing rails and different money flows.
## When to use it
Your platform plan decides which 1Club features your gym can use and how many active members and marketing messages it can have. You come back to it when you start out, when your member count changes, when you need an add-on, or when you want to pause paid features.
Typical uses:
* **Trying paid features before paying** - a new padel gym starts the 30-day Grow trial with no card to try automations, verifies its email domain so it can send during the trial, and upgrades before the trial ends.
* **Matching the plan to your size** - a jiu-jitsu academy with 180 active members picks the **Run 200** tier, and moves up to **Run 400** as it grows.
* **Adding a public website** - a yoga studio on a paid plan adds the **Website** add-on, with a prorated charge on its next invoice.
* **Pausing paid features** - a climbing gym downgrades to Free over a quiet stretch and keeps its data for when it upgrades again.
See [Worked examples](#worked-examples) for how each of these plays out.
## Platform plan tiers
1Club offers three platform plans. The plan catalog (names, prices, quotas, and features) is defined in Stripe, so exact prices depend on your organization's currency (USD, EUR, or GBP) and current pricing. The tiers are:
| Plan | Who it is for | Active member quota | Marketing messages |
| - | - | - | - |
| **Free** | Clubs just getting started with core scheduling | Capped (indicatively around 100 active members) | Not included |
| **Grow** | Growing teams that want automation and reporting | Unlimited | Daily and monthly message allowance |
| **Run** | Larger organizations needing scale and premium support | Tiered by active member count | Included allowance |
* **Free** is a database-only state. There is no Stripe subscription behind it and no charge. If a paid plan or trial is cancelled or expires with no active subscription remaining, your organization falls back to Free automatically.
* **Grow** is a flat paid plan with unlimited active members and a messaging allowance. It is the plan the trial gives you.
* **Run** is a tiered paid plan. Its price and member quota scale with how many active members you have, across tiers named **Run 100**, **Run 200**, **Run 400**, and **Run Unlimited**. When you select Run you pick a tier with the **active members** slider.
"Active members" means contacts with a member or drop-in type in your organization. This is the count that drives Run tier pricing and the active-member quota.
### Monthly or yearly
Each paid plan can be billed **Monthly** or **Yearly**. Use the toggle above the plan cards to switch what price is shown; yearly is displayed as a per-month equivalent with a "billed yearly" note. The billing frequency you choose is applied when you check out.
## The Grow trial
The **Grow trial** is a one-time, 30-day entitlement to the Grow plan:
* **30 days.** The trial runs for 30 days from when it starts.
* **No card required.** The trial is tracked entirely in your platform subscription record (status `trialing` with a trial end date) with no Stripe subscription behind it. You are never charged to start it.
* **One per organization.** Each organization can use the trial only once. The trial end date is kept permanently as the "trial consumed" marker, so once you have had a Grow trial you cannot start another - even after downgrading to Free.
* **Expires to Free.** If you do not convert to a paid plan before the trial ends, your organization moves to the Free plan. You keep your data; you lose the paid features.
While the trial is active you see a banner: "Your Grow trial ends on " with the reminder "Choose a plan to continue using 1Club. After your trial ends, you'll be moved to the Free plan." To keep Grow features, select a paid plan from **Available plans** before the trial ends - this sends you to Stripe Checkout to add a payment method and start paying.
Sending marketing messages during the trial requires a verified email domain. A trialing organization without a verified domain is blocked from sending until it either verifies a domain or moves to a paid (active) plan. Paid customers are never subject to this restriction. Verify your sending domain under **Settings** > **Organization** > **Domains** before you plan a campaign; see [Email sending](/settings/email-sending).
## The subscription page
Your platform subscription is stored against your organization and controlled from **Settings** > **Organization** > **Subscription** at `/settings/organization/subscription`. **Account & Billing** in the account menu at the bottom of the sidebar opens the same page. From the subscription settings page you can:
* See your current platform plan, its price, billing frequency, and status.
* Compare the available tiers - **Free**, **Grow**, and **Run** - and switch between them.
* Start the **Grow trial** (30 days, no card required, one per organization).
* Add or remove **plan add-ons** such as **Website**.
* Track your **usage** against plan quotas (active members and marketing messages).
* Open the **Stripe billing portal** to manage payment methods, invoices, and cancellations.
Viewing the page requires the `settings.read.organization` permission. Changing your plan, managing add-ons, or opening the billing portal requires `settings.manage.organization`.
## Switching plans
1. Go to **Settings** > **Organization** > **Subscription**.
2. In **Available plans**, choose **Monthly** or **Yearly**.
3. For **Run**, drag the **active members** slider to the tier that covers your member count.
4. Click the button on the plan card:
* **Select plan** / **Upgrade** on a paid plan opens **Stripe Checkout**. Your existing subscription stays active until checkout completes, so there is no gap. On success you return to the subscription page with a "Plan updated successfully!" confirmation.
* **Downgrade** to **Free** asks you to confirm ("Downgrade to Free?"). Confirming cancels your paid subscription and moves you to Free. You lose access to paid features including messaging, campaigns, and extended member limits.
* **Upgrade** or **Downgrade** on the same Run plan (moving the slider to a different tier) changes your tier.
The button shows **Current plan** and is disabled for the plan and tier you are already on.
## Plan add-ons
A **plan add-on** is an optional capability you attach to your platform plan. The first add-on is **Website** - the public-facing storefront you publish for your club. Relative to your current plan, each add-on is in one of three states:
* **Included** - bundled with your plan at no extra charge. Shown with an "Included with " badge and a crossed-out price.
* **Available** - purchasable on your plan as a paid recurring line item. Shown with its price and an **Add** button.
* **Not available** - not offered on your plan. Shown with an "Available with " chip so you can see which plans to upgrade to.
### Adding an add-on
1. In the **Your subscription** panel, find the add-on under **Plan Add-ons**.
2. Click **Add**.
3. Confirm in the dialog. It shows the add-on price and explains that your next invoice will include a prorated charge for the rest of the current billing period. No immediate mid-cycle charge is taken.
### Removing an add-on
Active paid add-ons appear as line items in your subscription. Click **Remove** next to the line and confirm. Your next invoice includes a prorated credit for the unused portion of the period. You cannot remove an **Included** add-on - it comes with the plan.
1Club never silently starts charging you. If a plan change turns an add-on that was previously **Included** into merely **Available (paid)** on the new plan, the add-on is dropped rather than converted into a paid line item. Charging can stop silently; it never starts silently.
Adding a paid add-on requires an active paid subscription first - you cannot attach a paid add-on while on Free or during the card-free trial, because there is no Stripe subscription to add it to.
A plan add-on (org buys from 1Club) is not a [booking add-on](/sales/plans) (a member buys at checkout, like a towel or smoothie). Different audience, different billing.
## Usage and quotas
Your plan sets quotas that 1Club tracks as you use the platform. The two tracked dimensions are:
* **Active members** - the number of member or drop-in contacts. Free is capped; Grow is unlimited; Run is capped by your selected tier.
* **Marketing messages** - a daily and monthly send allowance. Free includes no marketing messages; Grow and Run include allowances.
A usage widget shows your current members and messages against your limits, with a progress bar per dimension. When you hit the daily message cap you see "Daily limit reached. Resets tomorrow."
### How Run tiers handle overflow
Run tiers carry a 10% buffer above the tier boundary before enforcement bites. For example, on **Run 100** you can grow slightly past 100 active members (up to roughly 110) before the limit is enforced, which nudges you toward the next tier rather than hard-stopping you mid-month. The widget shows the tier boundary (for example 100); enforcement allows the buffer on top.
Live card payments to your members are gated on a paid plan. An organization on Free or still in the Grow trial cannot run live Stripe charges - it must be on a proper paid subscription first. Plan the upgrade before you go live with member billing. See [Stripe integration](/settings/integrations/stripe).
## Billing portal
Click **Manage billing** in the **Your subscription** panel to open Stripe's secure customer portal in a new tab. There you can:
* Update your payment methods.
* View and download invoices.
* Manage or cancel your subscription.
The portal works for any organization - Free, trialing, or paid. If no Stripe customer exists yet, one is created for you when you open it. When you finish, Stripe returns you to the subscription settings page.
## Subscription statuses
Your subscription status reflects where you are in the billing lifecycle:
| Status | Meaning |
| - | - |
| **Active** | On a paid plan and current. |
| **Trialing** | In the Grow trial (no charge yet). |
| **Past due** | A payment failed and Stripe is retrying. Update your payment method to avoid interruption. |
| **Unpaid** | Payment failed and the subscription is unpaid. Update your payment method to restore service. |
| **Canceled** | The subscription was cancelled. |
| **Incomplete** / **Expired** | Setup was not completed, or a trial ended without a payment method. Add a payment method to activate. |
| **Free** | On the Free plan (no Stripe subscription). |
1Club treats the database as authoritative for **Free**, **Trialing**, and **Canceled** states; Stripe is authoritative for **Active** and **Past due** (only those states have a live Stripe subscription).
## Worked examples
**A padel club trials Grow, then commits.** A new padel club signs up on Free and starts the 30-day Grow trial to try automations and campaigns - no card needed. Before running their first member blast they verify their email domain so trial sending is unblocked. On day 25 they click **Upgrade** on the Grow card, complete Stripe Checkout, and move to a paid Grow subscription. Their trial end date stays on record, so they cannot start a second trial later.
**A growing jiu-jitsu academy on Run picks a tier.** A martial arts academy with 180 active members selects **Run** and drags the active-members slider to the **Run 200** tier. As they add members they briefly cross 200 during a promotion; the 10% buffer (up to \~220) keeps them running while they decide to move up to **Run 400**.
**A yoga studio adds a Website.** A pilates and yoga studio on a paid plan wants a public storefront. Under **Plan Add-ons** the **Website** add-on shows as **Available** with a monthly price. They click **Add**, confirm the prorated charge, and the Website add-on appears as a line item in their subscription. Months later they downgrade a tier where Website is **Included** - the separate paid line item is dropped and no longer charged.
**A climbing gym downgrades for the off-season.** A climbing gym on Grow decides to pause paid features over a quiet stretch. They click **Downgrade** on the Free card and confirm. Their paid subscription is cancelled, they move to Free, and any paid add-ons become unavailable. Their data stays intact for when they upgrade again.
## Tips and best practices
* **Start the trial deliberately.** You only get one Grow trial per organization, and the end date is permanent. Start it when you are ready to actually evaluate the paid features.
* **Verify your domain before trialing campaigns.** Trial sending needs a verified email domain, so set that up first.
* **Pick Run tiers with headroom.** Choose a tier that covers your expected member count for the period; the 10% buffer is a cushion, not a plan.
* **Use yearly billing for stable clubs.** If your plan is unlikely to change, yearly billing shows a lower per-month equivalent.
* **Manage payment details in the portal.** Cards, invoices, and cancellations all live in the Stripe billing portal, not on the settings page.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| Cannot start the Grow trial | The organization has already used its one trial (the trial end date is on record). | Select a paid plan directly instead; there is no second trial. |
| Messages are blocked during the trial | Trialing organizations must have a verified email domain to send. | Verify your [email sending domain](/settings/email-sending), or move to a paid (active) plan. |
| "Payment is required" banner after the trial | The trial ended with no payment method on file, so the subscription is incomplete or expired. | Open **Manage billing** and add a payment method, or select a paid plan to check out. |
| **Add** on an add-on is missing or fails | No active paid subscription exists (you are on Free or trialing), or the add-on is not available on your plan. | Upgrade to a plan that offers the add-on first, then add it. |
| Live card payments to members are rejected | Live Stripe mode is gated on a paid plan; Free and trialing organizations cannot run live charges. | Move to a paid subscription, then configure [Stripe](/settings/integrations/stripe). |
| Plan price looks different from a colleague's | Prices are shown in your organization's currency (USD, EUR, or GBP). | Confirm your organization currency under [Organization settings](/settings/organization). |
| Downgraded but still see a paid add-on charge | Prorated credits and charges settle on the next invoice, not immediately. | Check the upcoming invoice in the billing portal; the credit appears there. |
## Related
* [Plans](/sales/plans) - the membership and pass templates you sell to your own members (not your 1Club subscription).
* [Website](/marketing/website) - the storefront published via the Website plan add-on.
* [Stripe integration](/settings/integrations/stripe) - connect Stripe to take live payments from members.
* [Payment methods](/settings/payment-methods) - the methods you accept from members at checkout.
* [Organization settings](/settings/organization) - your organization profile and currency.
* [Inbox](/inbox/overview) - message sending that consumes your message quota.
# Pricing rules
Source: https://docs.1club.ai/settings/pricing-rules
Configure dynamic pricing for bookings - publish an hourly rate per time band, layer peak surcharges and off-peak discounts, give plan holders their own member rate, and scope any rule to a gym, area, instructor, or class type.
Pricing rules decide what a booking costs. Each rule defines an adjustment (a percentage, a fixed amount, or an outright price per hour), a scope (which gyms, areas, instructors, or class types it applies to), and a set of conditions (time of day, day of week, booking timing, demand, duration, date range, or plan). When a booking's context matches a rule, that rule is applied to the resource's base price. You set rules up once, and members and the front desk see the result as the price under each slot on the booking calendar, in booking search, and at checkout.
Two ways to use them, and most gyms use both. Layer **percentage** and **fixed** adjustments on top of an area's price per hour for surcharges and discounts, or publish your printed rate card directly with **Absolute price** rules that state what each time band costs.
## When to use it
Use pricing rules when one flat price per hour doesn't match how you actually charge - when evenings cost more, quiet hours cost less, or plan holders pay their own rate. Rules apply automatically wherever a booking is priced, so nobody works out the price by hand.
Typical uses:
* **Peak evenings on padel courts** - a +25% surcharge on weekday courts from 18:00 to 22:00, applied only to the part of a booking inside that window.
* **Filling quiet hours** - a midday weekday discount on a fitness studio's gym floor.
* **A printed rate card with a member column** - an indoor tennis venue states what each time band costs, plus the lower rate season pass holders pay for the same hour.
* **A cheaper court when a coach is booked with it** - a fixed discount on the court, while the coach is still charged their full rate.
See [Worked examples](#worked-examples) for the exact rules behind each of these, and more.
## The list page
You manage pricing rules under **Settings** > **Billing** > **Pricing Rules** (`/settings/billing/pricing-rules`). You can also reach it from the **Pricing rules** button in the header of **Settings** > **Clubs** > **Areas**.
The page opens with an info banner explaining dynamic pricing, followed by a table of your rules with these columns:
* **Name** - the rule name.
* **Adjustment** - the percentage or fixed value, signed. Increases show in one color and discounts (negative values) in another so you can read the intent at a glance.
* **Scope** - a summary of the scoped resources, for example *All* when unscoped, or *Areas (3)* when three areas are targeted.
* **Conditions** - a count of attached conditions, or *No conditions*.
* **Status** - **Active** or **Inactive**. Only active rules are considered when pricing a booking.
Use **Add** to create a rule, or the row menu to **edit** or **delete** one. Deleting a rule is permanent and asks for confirmation.
## Creating or editing a rule
Click **Add** (or edit an existing row) to open the rule dialog. It has three sections: **Basic Information**, **Scope**, and **Conditions**.
### Basic information
* **Rule Name** (required) - a label such as *Padel peak hours* or *Weekday off-peak discount*.
* **Description** - optional free text to explain the rule to other staff.
* **Adjustment Type** - **Percentage**, **Fixed Amount**, or **Absolute price**.
* **Percentage Adjustment (%)** - shown when the type is Percentage. Accepts -100 to +100. Enter a positive number to increase the price and a negative number to decrease it.
* **Fixed Amount** - shown when the type is Fixed Amount. Enter the amount to add per hour; use a negative value for a discount.
* **Price per hour** - shown when the type is Absolute price. What this hour costs, replacing the area price rather than adjusting it. Enter the rate for one hour; a 90-minute booking is charged one and a half times it.
* **Priority** - precedence when several rules match the same slot, and the lower number wins. Leave it at 0 unless you need a rule to beat another one; see [Priority](#priority).
* **Active** - toggle the rule on or off without deleting it.
### Scope
The scope decides which bookings a rule can apply to. Add one or more scope entries; each entry has a **Scope Type** and a list of resources:
* **Clubs** - the rule applies only at the selected gyms. This is the scope a multi-venue organization needs to publish a different price list per site.
* **Areas** - the rule applies only to bookings against the selected areas (courts, rooms, fields).
* **Instructors** - the rule applies only to bookings against the selected instructors.
* **Class types** - the rule applies only to class bookings of the selected class types.
For any scope entry, leave the selector empty to match **all** resources of that type. A rule with no scope entries at all matches every booking (subject to its conditions).
Scope entries are combined with AND. If you add both an Areas entry and an Instructors entry, a booking must carry both an area and an instructor to match. In practice, use one scope type per rule unless you specifically need that combination. To price a court only when a coach is booked with it, use the **Booked with an instructor** condition instead of adding an Instructors entry. A scope also gates by resource presence: an Areas-scoped rule never applies to a class or instructor-only booking, so area rules do not leak into other booking types.
### Conditions
Conditions further restrict when a rule fires. Add as many as you need - **all** conditions must pass for the rule to apply (AND logic). A rule with no conditions applies whenever its scope matches.
* **Weekdays with Time Range** - choose weekdays (optional; empty means every day) plus a **Start Time** and **End Time**. The rule applies to bookings whose start time falls in that window, evaluated in your organization's timezone. The end time is exclusive (a 12:00-14:00 window covers slots from 12:00 up to, but not including, 14:00). This is the condition that splits a booking at the window boundary.
* **Applies to** - how the window is read. **The part of the booking inside the window** (the default) splits the booking at the window's edges, so only that part is priced by the rule - this is how a peak band works. **Bookings that start in the window** and **Bookings that fit inside the window** price the whole booking or none of it, and do not split it.
* **Days of Week** - restrict to specific days without a time range (for example, weekends only).
* **Availability** - fire based on how full the resource is over the next X days, compared with an operator (`>`, `<`, `>=`, `<=`) and a percentage. Useful for demand-based pricing.
* **Booking Timing** - fire based on how many days ahead the booking is made, compared with an operator and a day count. Useful for early-bird discounts or last-minute surcharges.
* **Demand Level** - fire on **Low Demand** or **High Demand**, which the system derives automatically from availability.
* **Duration Threshold** - fire only when the booking is at least a **Minimum duration (hours)**, and optionally at most a **Maximum duration (hours)**. Set both to the same value to target one length. The duration is always the whole booking's, even when another rule splits it into segments.
* **Date Range** - fire only between a **Start Date** and **End Date** (interpreted as whole days in your organization's timezone), for seasonal pricing.
* **Plan** - fire only for members on the selected plans (leave empty for all plans). Bookings with no active plan do not match a plan-restricted rule. A rule with a Plan condition is a **member rate**; see [Member rates](#member-rates) for how two plans are resolved.
* **Booked with an instructor** - fire only when the court is booked together with an instructor (leave the selector empty for any instructor). Pair it with an **Areas** scope: the rule then adjusts the court's price whether staff book the court and the coach as one booking or a member books them together. It never changes the instructor's own price, and a court booked alone keeps its normal price.
## How a rule matches a booking
When the system prices a booking, it loads every **active** rule for your organization and keeps the ones that match, in this order:
1. **Scope check** - the booking must carry the resource the scope targets. An Areas scope requires an area; a Class types scope requires a class booking; a Clubs scope requires the booking to be at one of the selected gyms. If the scope lists specific IDs, the booking's gym, area, instructor, or class type must be one of them. Empty IDs mean any resource of that type.
2. **Condition check** - every attached condition must pass (weekday and time window, days, duration, date range, plan, and the availability, booking-timing, and demand checks).
Every **percentage** and **fixed** rule that passes both checks is applied, so those still stack. **Absolute price** is different: it states a total rather than a movement, so only one absolute rule can win a slot, and [Priority](#priority) decides which.
A Clubs-scoped rule needs the booking to name a gym. A context that carries no gym fails the scope rather than inheriting it, so one venue's price list can never reach another venue's courts.
## Priority
**Priority** is precedence, and the lower number wins. There is one direction to remember, and it is that one: give the rule you consider most specific the smallest number.
What "wins" means depends on whether the rules can coexist:
* **Percentage and fixed rules all apply**, so priority only sets the order they are applied in, lowest first. That matters when you mix the two, because a percentage taken before a fixed deduction gives a different total than after it.
* **Absolute price rules replace the price**, so exactly one wins: the lowest priority, then the more specific rule (one scoped to a plan beats one that is not), then the older rule.
* **Member rates nominate a plan.** The lowest priority decides which plan grants the rate.
Every rule you create through the admin starts at priority 0, which is the right answer while your rules do not overlap. Reach for it when two rules of the same kind fight over one slot, for example a holiday rate that has to beat a year-round band:
* Year-round day rate, absolute 39, **Priority 10**.
* Christmas week rate, absolute 25, date range 22 Dec to 2 Jan, **Priority 0**.
Inside the date range the holiday rule wins because its number is lower. Outside it the rule does not match at all, so the band applies. Without the priority difference the two rules tie, and the tie is broken by age, so the older band would keep winning and the holiday rate would silently never apply.
## Member rates
A rule with a **Plan** condition is the rate that plan's holders pay. It behaves like any other rule with one addition: **only one plan may grant the rate on a booking**.
A member can easily hold two rate-granting plans at once - a season pass for the court and a ten-lesson coaching card. Rather than compounding both discounts, the pricing engine nominates one plan:
1. The lowest **Priority** among the matching plan-scoped rules nominates its plan.
2. If two plans are nominated together, the winner is the one that leaves the member paying **less**. A gym that gave two overlapping member rates the same priority has not said which it means, and the platform does not resolve that against the person who paid for both.
3. Every rule scoped to the winning plan then applies normally, so "season plan gets 34 per hour" and "season plan gets another 10% off in winter" both take effect.
Rules that are not plan-scoped are never candidates here. Your published time bands keep applying over the winner, which is how a printed price list reads: the band says what the hour costs, the member rate says what a member pays for it.
### Which memberships grant a member rate
* **An active membership** grants its rate on any slot it is quoted against.
* **A season pass sold before its season opens** grants its rate too, but only for slots inside its own season. A gym sells the winter in September, so the pass waits at pending until the season's first day; the holder is still quoted their rate for the December hours they buy on top of the contract, and the published rate for anything before the season starts. The slot's start time decides, read on the venue's clock, and the season's last day is covered in full.
* **A pass waiting on its payment** grants nothing. The rate is part of what was bought, and the money is not in yet.
* **A cancelled, expired or paused membership** grants nothing.
The calendar and the booking dialog resolve this the same way, so the price shown under a slot is the price checkout charges.
## How the booking price is computed
The price is built from a base rate and then adjusted by the matching rules.
**Base price.** The base comes from the booked resource:
* **Area** bookings use the area's **price per hour** multiplied by the duration.
* **Instructor** bookings use the instructor's **hourly rate**.
* If a booking has both an area and an instructor, their hourly rates are **summed**.
* **Class** bookings use the class's own price as a fixed base.
* If no rate is set, the base is zero.
**Time-window splitting.** If any matching rule uses a Weekdays-with-Time-Range condition read as a band (the default), the booking is split into segments at the window boundaries, so each segment is either fully inside or fully outside the window. The base price is prorated across the segments by duration.
**Applying adjustments.** For each segment, the winning absolute rule is resolved first and replaces the base price, then the composing rules fold over that result in priority order:
* **Absolute price** sets the segment price outright: the rate multiplied by the segment's duration in hours. Only the winner is applied, and a losing absolute rule leaves no line on the quote, because a price that was never applied did not price the slot.
* **Percentage** rules multiply the current segment price: `adjustment = currentPrice x percentage / 100`, added to the running price. Multiple percentage rules compound (each applies to the running total, not the original base).
* **Fixed Amount** rules add the amount **per hour**: a fixed amount is multiplied by the segment's duration in hours (so a fixed -10 becomes -5 for a 30-minute segment).
The order is what makes a rate card behave: a band rule stating 39 and a member rule taking 10% off produce 35.10, not one or the other depending on which rule you happened to create first.
A segment price is floored at zero (a discount can bring a segment to free but not negative), and the segment totals are summed into the final price.
**Tax.** After the final price is computed, tax is applied using your organization's tax configuration, producing the tax rate, tax amount, and the total price with tax.
Membership coverage is resolved before pricing rules. If a member's plan covers the booking (a free session or included hours), the booking is priced at zero and pricing rules do not apply.
## Pricing sheets
A pricing sheet is the calculated price for every bookable slot of a resource across a day, produced by running the same pricing engine slot by slot. Sheets are what let the admin calendar and booking search show a price under each time slot without a separate calculation per click.
* Slots are generated across the resource's effective operating hours (falling back to 06:00-22:00 when hours are not set), stepped by the booking policy's duration step or a default slot length (30 minutes; configurable from 5 to 240 minutes).
* Each slot is priced with the active rules for that start time, so peak and off-peak differences appear directly on the calendar.
* Sheets can be generated for a single resource on one day, or in batch for multiple areas and instructors across a date range.
* The response also carries the effective booking policy (minimum and maximum slot length, duration step, notice, and how far ahead members can book) so the calendar can size and constrain slot selection.
You do not build pricing sheets by hand - they are generated automatically wherever prices need to be shown. Editing a pricing rule refreshes the sheets within about a minute (rules are cached briefly for performance).
## Worked examples
### Padel peak-hour surcharge
Charge more for prime evening court time on weekdays.
* **Adjustment Type**: Percentage, **+25%**.
* **Scope**: Areas - your padel courts (leave empty to cover every court).
* **Conditions**: Weekdays with Time Range - Monday to Friday, 18:00 to 22:00.
A 60-minute booking at 17:30 crosses the 18:00 boundary: the first 30 minutes stay at the base rate and only the 18:00-18:30 portion gets the +25%, because the booking is split at the window edge.
### Off-peak gym discount
Encourage midday visits to your fitness studio.
* **Adjustment Type**: Percentage, **-20%**.
* **Scope**: Areas - the gym floor or studio.
* **Conditions**: Weekdays with Time Range - Monday to Friday, 09:00 to 16:00.
Alternatively use a **Fixed Amount** of -5 to knock a flat five off each hour booked in the window.
### Weekend court premium
Add a flat premium to tennis and padel courts on Saturday and Sunday.
* **Adjustment Type**: Percentage, **+15%**.
* **Scope**: Areas - your tennis and padel courts.
* **Conditions**: Days of Week - Saturday, Sunday.
Because rules stack, a Saturday 19:00 padel booking can pick up both the weekend premium and the peak-hour surcharge if both rules match.
### Early-bird class discount
Reward members who book a spin or reformer class well ahead of time.
* **Adjustment Type**: Percentage, **-10%**.
* **Scope**: Class types - Spinning (or Reformer Pilates).
* **Conditions**: Booking Timing - more than (>) 7 days ahead.
### Long-session climbing discount
Give a break to climbers who book a longer wall session.
* **Adjustment Type**: Percentage, **-15%**.
* **Scope**: Areas - your bouldering or roped areas.
* **Conditions**: Duration Threshold - minimum 2 hours.
### Member plan rate
Offer a standing discount to members on a premium plan for personal training.
* **Adjustment Type**: Percentage, **-20%**.
* **Scope**: Instructors - your personal trainers.
* **Conditions**: Plan - your Premium plan.
### Court discount when booked with a coach
Take 2 off the court whenever it is booked together with a coach, at any hour.
* **Adjustment Type**: Fixed amount, **-2**.
* **Scope**: Areas - your courts (leave empty to cover every court).
* **Conditions**: Booked with an instructor - leave empty for any instructor.
The discount stacks with your other court rules: a court that costs 28 in a weekday off-peak band costs 26 when a coach is booked with it. The coach is still charged their full rate, and on the booking's charges the discount appears on the court's line.
### A published rate card with a holder column
An indoor tennis venue prints one table with two columns: what an hour costs, and what a season pass holder pays for the same hour. Reproduce it with two absolute rules per band.
The published rate, for everyone:
* **Adjustment Type**: Absolute price, **Price per hour** 39.
* **Scope**: Clubs - the venue.
* **Conditions**: Weekdays with Time Range - Monday to Friday, 10:00 to 17:00.
* **Priority**: 10.
The holder rate, for the same hour:
* **Adjustment Type**: Absolute price, **Price per hour** 34.
* **Scope**: Clubs - the venue.
* **Conditions**: Weekdays with Time Range - Monday to Friday, 10:00 to 17:00, plus Plan - the season's plan.
* **Priority**: 0.
Repeat the pair for each band (off-peak morning, day, peak, late, weekend) and the slot prices itself for both audiences. This is also what a [season pass](/schedule/season-passes) is priced from: its total is the sum of the weeks it books at these rates. See [Seasons](/settings/seasons).
## Tips & best practices
* **Offers members should see belong in promotions.** Pricing rules are silent: members see the resulting price, never the rule. For a named offer such as "2 for 1 on daytime courts" shown on the booking calendar, use an auto-apply [promotion](/sales/promotions#time-of-day-offers-on-the-booking-calendar).
* **Name rules for their intent.** *Padel peak +25%* reads better in the list than *Rule 3*.
* **Keep one scope type per rule** unless you deliberately want to require a booking to carry two resources at once.
* **Use the sign deliberately.** Positive percentages and fixed amounts increase the price; negatives discount it. Fixed amounts are per hour, so they scale with duration.
* **Remember percentage and fixed rules stack.** If two of them match the same booking, both apply and percentages compound. Check overlapping time windows and scopes so you do not double-charge or over-discount by accident.
* **Use Absolute price for a printed table, adjustments for a policy.** "The day rate is 39" is an absolute rule. "Evenings cost 25% more" is a percentage rule. Translating a printed rate into an adjustment off a base price only introduces arithmetic errors.
* **Give the more specific rule the lower priority.** A member rate at 0 and a published band at 10 reads the way you mean it, and beats relying on the age of the rules.
* **Scope to a gym when venues price differently.** Without a Clubs scope, a rule authored for one venue applies to every court in the organization.
* **Set a base price first.** Rules adjust the area price per hour, instructor hourly rate, or class price. With no base rate, there is nothing to adjust and the slot prices at zero.
* **Deactivate instead of deleting** a seasonal rule you will reuse. Toggle **Active** off and back on rather than recreating it.
* **Mind the timezone.** Time windows and date ranges are evaluated in your organization's timezone, not the admin's browser timezone.
## Troubleshooting
**Issue**: A rule never applies to a booking.
**Solution**: Confirm the rule is **Active**, that the booking carries the scoped resource (an Areas-scoped rule does nothing for a class or instructor-only booking), and that every condition passes. Remember conditions are AND - one failing condition blocks the whole rule.
**Issue**: The discount is bigger or smaller than expected on a longer booking.
**Solution**: Fixed Amount adjustments are applied **per hour**, so a fixed -10 becomes -20 over two hours. Use a percentage if you want the adjustment to stay proportional regardless of duration.
**Issue**: Only part of my time-window booking got the surcharge.
**Solution**: That is expected. A Weekdays-with-Time-Range condition splits the booking at the window edges, so only the portion inside the window is adjusted. The end time is exclusive.
**Issue**: Two rules both fired and the price compounded.
**Solution**: Matching percentage and fixed rules stack; there is no single winner among them. Narrow the scopes or time windows so only the intended rule matches, deactivate one of them, or state the price outright with an Absolute price rule, of which only one can win.
**Issue**: My new Absolute price rule has no effect on the slot.
**Solution**: Another absolute rule is winning it. Only one absolute rule prices a slot, decided by the lowest **Priority**, then specificity, then age. Two rules both left at 0 tie, and the older one wins, so give the rule that should win a lower priority number.
**Issue**: A member holding two plans got the wrong rate.
**Solution**: Only one plan grants the member rate. The lowest **Priority** among the matching plan-scoped rules nominates the plan, and a tie is settled in the member's favour. Set an explicit priority on the rate you intend to win, or scope the other plan's rule so it does not match this booking.
**Issue**: A holder was quoted the published rate instead of their member rate.
**Solution**: Check what the membership grants right now; see [Which memberships grant a member rate](#which-memberships-grant-a-member-rate). The usual causes are a pass still waiting on payment, a cancelled or expired one, or a season pass whose season has not started priced against an hour that falls outside that season.
**Issue**: A rule authored for one venue is pricing another venue's courts.
**Solution**: Add a **Clubs** scope naming the venue it belongs to. Without one, a rule applies across the whole organization.
**Issue**: A booking priced at zero and ignored the rule.
**Solution**: Membership coverage is resolved before pricing rules. If the member's plan covers the booking, it is free and no rule applies. Check the member's active membership.
**Issue**: I edited a rule but the calendar still shows old prices.
**Solution**: Pricing rules are cached briefly (about a minute) so the calendar can price many slots quickly. Wait a moment and refresh; the pricing sheets will pick up the change.
## Related
* [Areas](/settings/areas) - Set the price per hour that rules adjust, and reach the Pricing rules page from the Areas header.
* [Plans](/sales/plans) - Membership plans referenced by the Plan condition and by membership coverage.
* [Booking policies](/settings/booking-policies) - Control when members pay, slot lengths, and cancellation rules that work alongside pricing.
* [Seasons](/settings/seasons) - The period a season pass is sold for, and its volume discount.
* [Season passes](/schedule/season-passes) - Priced entirely from these rules, week by week.
# Profile
Source: https://docs.1club.ai/settings/profile
Manage your own admin account - name, photo, email, phone, password, language, and theme - from Profile Settings in the account menu.
Your profile is your personal admin account: your name, photo, sign-in email, phone number, and password. It is the one settings screen every admin user can open regardless of role, and it is not part of the Settings area, because everything here is about you, not the organization. You cannot change your own role here - roles are managed by an admin under [Users](/settings/admin-users).
## When to use it
Your profile is where you keep your own contact details and sign-in current, so notifications reach you and you can always get back into your account.
Typical uses:
* **Getting SMS booking alerts** - a receptionist adds their phone number in international format and verifies it with a code, so SMS notifications can reach them.
* **Fixing an unverified email** - you send yourself a fresh verification email, because unverified contact details can block or delay notifications and some sign-in flows.
* **Changing your password** - you set a new password, which also signs you out on every other device.
See [Worked examples](#worked-examples) for a step-by-step version.
## What you can edit
Open the account menu at the bottom of the sidebar and choose **Profile Settings**. Your profile opens over the page you are on.
The profile form has one section, **Profile information**, with a floating save bar that appears as soon as you change anything.
* **Profile image** - Upload from your device. Uploaded files are stored under `1club/users`. Your avatar appears next to your name in user lists and the account menu.
* **First name**, **Last name** - Your display name in user lists and the account menu.
* **Email** - Your sign-in email. A chip shows whether it is verified. If it is not, a warning icon appears with a send action - click it to trigger a fresh verification email.
* **Phone** - Enter in international format. A chip shows whether the number is verified. If it is not, click the send action to open the verification dialog and confirm the number with a code. The verified chip tracks the number currently in the field, so an edited-but-unsaved number reads as unverified until you verify and save it.
### How saving works
Edit any field and a floating **Save** bar appears at the bottom of the page. Click it to commit. Your name, email, phone, and profile image sync across every browser and device you sign in from.
## Changing your password
Click **Change password** in the top right to open the dialog. It asks for your current password, a new password, and a confirmation.
* The new password must meet the listed password requirements (at least 8 characters).
* Changing your password revokes your other active sessions for security, so you stay signed in on this device but are signed out elsewhere.
* If you signed in with Google, Apple, or Facebook and never set a local password, you cannot use this dialog because it requires your current password. Ask an admin to send you a password reset from [Users](/settings/admin-users) instead. See [Signing in](/reference/sign-in) for how sign-in methods link to one account.
## Language and theme
Language and theme are not on the profile form. You set them from the account menu at the bottom of the sidebar.
* **Language** - Choose English, French, Bulgarian, or Spanish. The change applies immediately and is saved to your account, so it follows you on your next sign-in.
* **Theme** - Choose **Light**, **Dark**, or **System** (follows your operating system). Theme is saved per browser, so it does not leave the local machine.
## Notifications
Which notifications reach you, and on which channels, is set under **My Notifications** in the same account menu, not on the profile form. See [Notifications](/settings/notifications).
## Worked examples
### Front desk staff at a padel club enabling SMS alerts
A receptionist wants to receive SMS alerts for new bookings. On their profile they enter a phone number in international format, click the send action to verify it with the code they receive, and save. Their number now shows the verified chip and can receive SMS notifications.
### Instructor switching the admin to French
A jiu-jitsu coach prefers French. They open the account menu in the sidebar, pick **Français** under language, and the admin switches immediately. The choice is saved to their account, so it stays French the next time they sign in on any device.
## Tips and best practices
* **Verify your email and phone.** Unverified contact details can block or delay notifications and some sign-in flows.
* **Use a real profile photo.** It makes your entries easier to recognise in shared user lists and activity.
* **Keep your recovery method current.** If you rely on a social sign-in, make sure the linked email is one you control, since password resets go there.
* **Save before you leave.** The floating save bar only commits when you click it - navigating away discards unsaved edits.
## Troubleshooting
**Issue**: The email verified chip stays as a warning after I clicked resend.
**Solution**: The verification email was sent, but the chip only clears once you open the email and complete verification. Check your inbox and spam folder, then reload.
**Issue**: My phone shows as unverified even though I verified it before.
**Solution**: The verified chip tracks the number currently in the field. If you edited the number, verify the new one. If it matches your saved number and still reads unverified, save the profile and reload.
**Issue**: The Change password dialog rejects me or I never set a password.
**Solution**: The dialog needs your current password. If you only ever signed in with Google, Apple, or Facebook, you have no local password to enter. Ask an admin to send a password reset from [Users](/settings/admin-users), then set a password from the reset link.
**Issue**: I want to change my own role.
**Solution**: You cannot change your own role from your profile. Ask an admin to update it in [Users](/settings/admin-users). What you can see and do is set by your role - see [Roles and permissions](/reference/roles-permissions).
## Related
* [Users](/settings/admin-users) - How admins invite users and manage roles and password resets.
* [Roles and permissions](/reference/roles-permissions) - What your role gives you access to.
* [Signing in](/reference/sign-in) - Supported sign-in methods and how they link to one account.
* [Organization](/settings/organization) - Org-wide identity and time zone.
# Progression tracks
Source: https://docs.1club.ai/settings/progression
Track member skill progression with tracks and levels - martial-arts belts, swim levels, tennis tiers. Enable platform templates, fork and customize them, author your own levels, set the sessions each level takes, and assign or promote members with a full progression history.
A progression track is a named ladder of skill levels that a member advances through over time: the belt system in a jiu-jitsu academy, the graded levels in a kids swim program, or the skill tiers in a tennis club. Each track belongs to one sport and holds an ordered list of levels (for example White, Blue, Purple, Brown, Black). Members are assigned a current level on any enabled track, optionally with stripes and notes, and every change is kept as a dated history entry. You set up the tracks once, and staff assign and promote members from their member profile.
## When to use it
Use progression tracks when your members work through a graded syllabus and you want each person's current level, and how they got there, on their profile instead of in a coach's notebook. Start from a platform template with one toggle, customize a copy, or build your own ladder.
Typical uses:
* **Belts and stripes** - a jiu-jitsu academy tracks White to Black belt with up to four stripes per belt, and notes each grading in the member's history.
* **Kids swim levels** - a swim school records which level each child has reached, from water confidence to endurance.
* **Knowing who is ready to grade** - a Muay Thai gym sets 20 sessions per armband, so students see how close they are and coaches get a list of who is eligible before grading day.
* **Streaming players into groups** - a tennis gym uses skill tiers from Beginner to Competition to put members in the right coaching group.
See [Worked examples](#worked-examples) for how to set up each of these, plus a Muay Thai armband track.
## Tracks and levels
A **track** has:
* **Track name** - what members and staff see, for example "Adult BJJ Belts" or "Learn to Swim".
* **Sport** - a short tag used to group tracks (for example `jiu-jitsu`, `swimming`, `tennis`). Pick from the sport list.
* **Description** - optional context shown on the track card.
* **Stripes per level** - the maximum number of stripes a member can earn within a single level, from 0 to 10. Set it to 0 to turn stripes off entirely; set it to 4 for the classic jiu-jitsu belt system.
Which classes count toward a track is set on the class type, not the track: a [class type's](/settings/class-types#counting-toward-a-progression-track) **Counts toward** field links it to one track. Each track card shows the class types linked to it, or "All sessions count toward this track" when none are.
A **level** has:
* **Skill** - the primary label for the rung, for example "White belt", "Level 2", or "Intermediate".
* **Level** - an optional secondary label shown as a colored chip next to the skill (for example a degree, grade number, or short code).
* **Color** - an optional hex color used for the level's chip and the belt/line visual on member cards.
* **Order** - levels are ordered; drag to reorder them into progression sequence.
* **Sessions** - optional. How many sessions a member needs at the level below before they are eligible for this one. The first level has no **Sessions** field, because there is no level below it to count at.
## Global templates vs org-owned tracks
You manage tracks and levels under **Settings** > **Members** > **Progression Levels** in the admin. The Progression settings page shows every track visible to your organization as a card:
* **Global templates** are platform-wide and shared across organizations. They are read-only: you cannot edit their levels directly. Each global card has an enable/disable **toggle** and a **Customize** action.
* **Org-owned (custom) tracks** belong only to your organization. They carry a **Custom** chip, are fully editable, and can be deleted. You get these either by creating a new track or by customizing (forking) a global template.
### Enable a global template
Flip the **toggle** on a global template's card to enable it for your organization. Enabled tracks become available for member assignment and show a highlighted card border.
To turn one off, flip the toggle again. You are asked to confirm, because disabling a track stops new level assignments on it. Existing member assignments are kept but hidden. Class types linked to the track keep their link, so turning it back on restores the same counting; their form shows the track as disabled until you do. A track that still has active member assignments cannot be disabled - you see "Cannot disable track: there are active member assignments" and must clear those assignments first.
### Customize (fork) a template
Click **Customize** on a global template to open its levels in an editable copy. The dialog shows the hint "Editing a copy of this template. Nothing is saved until you click Save." Make your changes, then **Save**. At that point 1Club:
1. Creates an org-owned copy of the track and all its levels.
2. Disables the original global template for your organization (so you do not end up with two parallel ladders).
3. Re-points any existing member assignments from the template's levels to the matching levels on your new copy.
4. Moves any class types that counted toward the template over to your new copy, so the same sessions keep counting.
The result is a **Custom** track you fully control, seeded from the template so you keep everything you had.
## Creating a track from scratch
1. Go to **Settings** > **Members** > **Progression Levels**.
2. Click **Create track**.
3. Fill in **Track name** and **Sport** (both required), an optional **Description**, and **Stripes per level** (0 to disable stripes).
4. Save. The levels editor opens automatically so you can author the ladder.
## Authoring levels
Open a custom track (or a global template's Customize view) to edit its levels.
* Click **Add level** to append a row.
* For each row, fill in the **Skill**, an optional **Level** label, and pick a **Color**.
* Enter **Sessions** on any level after the first to set how many sessions it takes (see [Sessions to the next level](#sessions-to-the-next-level)). Leave it empty for no requirement.
* Drag the handle to reorder rows into the correct progression sequence - order is the ladder.
* Use the delete icon to remove a row. A level cannot be deleted while a member is currently assigned to it; remove or reassign those members first.
* Set **Stripes per level** for the whole track at the bottom of the dialog.
* Click **Save** to persist all additions, edits, reorders, and deletions together.
## Assigning and promoting a member
Progression is managed from the member profile, not the settings page. A member (contact) does not need a linked user account to be assigned a level.
1. Open the member's profile and go to the progression section (**Current progression levels**).
2. Click **Manage progression** (or click an existing track card to focus it).
3. In the dialog, click **Assign level**, then:
* Pick a **Track** (skipped automatically when only one track is enabled).
* Pick a **Level** - the picker shows each level's skill, its color, and its level chip.
* If the track allows stripes, choose the number of **Stripes** (0 up to the track's stripes-per-level cap).
* Add optional **Notes** (for example "Promoted at winter grading").
4. **Save**.
Promoting a member is the same flow: pick the new level (and stripes) and save. Only tracks enabled for your organization can be assigned.
### Progression history
Every save appends a dated entry to the member's progression timeline - level changes, stripe-only changes, and note edits all create a history row. The row is the event: a member's current level on a track is simply the most recent entry for that track.
The history list under the assign form shows each entry's date, track, level (with belt/line and stripes), and notes, newest first. Delete an individual entry with its trash icon; if you delete the most recent entry, the next-most-recent one becomes the member's current level automatically.
## Sessions to the next level
Set **Sessions** on a level and 1Club counts how many sessions each member has attended toward it.
* **Per level, not a running total.** "Blue belt: 20" means 20 sessions at the level below Blue. The count starts again at every new level, so a later "Black belt: 60" means 60 sessions at the level just below it, not 60 since the member joined.
* **What counts as a session.** A check-in, or a completed booking - the same count as the member's week streak on their account page, so the two never disagree. Cancelled bookings and no-shows never count. If your gym does not require check-in, a booking counts once the day is over even when the member did not show up, unless you mark it a no-show. Require check-in for an accurate grading count.
* **When counting starts.** At midnight on your gym's clock, the day after the member earned their current level. The session where a belt is awarded belongs to the level it was earned at. Backdating a level, or correcting its date, recounts automatically.
* **Which sessions count depends on the track's class types.** A track with no class types linked counts every session, so if you teach two disciplines a session in one counts toward both. Once you link class types to a track (with **Counts toward** on the [class type](/settings/class-types#counting-toward-a-progression-track)), only sessions of those types count toward it. Private lessons and court bookings have no class type, so they don't count toward a track that has class types linked.
### Where the count shows
* **Member profile** - the **Progress** tile shows the count with a bar in the next level's color, for example "13 / 20 sessions to Blue belt", or **Eligible for promotion to Blue belt** once the member reaches it.
* **Member account (portal and website)** - the **Your progression** card on **My account** > **Overview**, next to the week streak, and the progression section of the member's **Profile** show the same count and bar.
### Eligible is a signal, not a promotion
Reaching the number changes nothing on its own: no level changes and no message is sent. You still promote the member yourself (see [Assigning and promoting a member](#assigning-and-promoting-a-member)). For a list of who is eligible, close, stalled or just promoted, turn on the **Progression** category in [Smart tagging](/settings/smart-tagging#progression-disabled-by-default).
## Worked examples
### Adult jiu-jitsu belt system (martial arts)
Enable or fork the BJJ belt template, or create a track named "Adult BJJ Belts" with sport `jiu-jitsu`. Author five levels in order, each with a matching color:
| Order | Skill | Color |
| - | - | - |
| 1 | White belt | White |
| 2 | Blue belt | Blue |
| 3 | Purple belt | Purple |
| 4 | Brown belt | Brown |
| 5 | Black belt | Black |
Set **Stripes per level** to `4` to match the IBJJF four-stripe system. When you promote a student, pick the belt and set stripes 0-4; a note like "3rd stripe awarded at May grading" is stored in history.
To show students how close they are, enter **Sessions** on each belt from Blue up and require check-in, so only attended classes count. Turn on the **Progression** smart tags to see who is eligible before your next grading. If you also run other classes, set **Counts toward** to this track on your BJJ class types so only BJJ sessions count.
### Learn to swim, kids levels (swimming)
Create a track "Learn to Swim" with sport `swimming` and **Stripes per level** `0` (no stripes). Author sequential levels such as Level 1 (Water confidence), Level 2 (Floating and gliding), Level 3 (Freestyle and backstroke), Level 4 (All four strokes), Level 5 (Endurance). Assign each child their current level; parents can see the belt/line progress on the member card.
### Tennis skill tiers (racket sports)
Create a track "Tennis skill tiers" with sport `tennis` and no stripes. Author tiers like Beginner, Improver, Intermediate, Advanced, Competition, using increasingly warm colors. Use it to stream members into the right coaching group, and pair it with the 1club Rating on **Matches** for objective match-based ranking.
### Muay Thai armbands (martial arts)
For a `muay-thai` program, author levels mirroring the prajioud armband colors (White, Yellow, Green, Blue, Brown, Red) with stripes disabled, and record gradings in the notes field.
## Tips & best practices
* **Prefer enable over fork.** If a global template matches your syllabus, just toggle it on. Fork only when you need to rename, recolor, reorder, or add and remove levels.
* **Order is the ladder.** Levels are assigned in the order you arrange them, so drag them into true progression sequence before you start promoting members.
* **Use stripes for intra-level progress.** Set stripes per level only where a level is earned incrementally (belts). Leave it at 0 for graded or tiered programs where you jump level to level.
* **Write promotion notes.** Notes are stored per history entry and make the timeline a real grading record - capture the date, event, or assessor.
* **Color consistently.** Reuse the real-world colors of your ladder so the member card belt/line reads at a glance.
* **Set sessions where attendance drives grading.** Members see exactly how far they are from the next level, which keeps them coming. Require check-in so the count reflects real attendance.
* **One track per program, not per class.** A track is a syllabus (Adult BJJ, Kids Swim), not a schedule; keep the ladder stable and manage attendance elsewhere.
* **Link the classes that train for a track.** If your gym runs more than one discipline, set **Counts toward** on each class type so a strength class doesn't move anyone toward a belt. Linking or unlinking a class type recounts every member on the track straight away.
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| A global template's levels cannot be edited | Global templates are read-only and shared across organizations | Click **Customize** to fork it into an editable org-owned copy, then edit and save |
| "Cannot disable track: there are active member assignments" | The track still has members assigned to a level on it | Reassign or clear those members' assignments, then toggle the track off |
| A track will not delete | The custom track has assigned members (the assignment history references its levels) | Remove the members' assignments first, then delete the track |
| A level will not delete | A member is currently assigned to that level | Reassign those members to another level, then delete the level |
| A member's session count dropped or jumped | A class type was linked to or unlinked from the track, which recounts every member's sessions | Expected. Link every class type that trains for the track under **Settings** > **Clubs** > **Class Types**, or unlink them all to count every session |
| A track does not appear in the member assign dialog | The track is not enabled for your organization | Enable the global template, or confirm the custom track exists, under **Settings** > **Members** > **Progression Levels** |
| The Stripes selector is missing when assigning a level | The track's **Stripes per level** is set to 0 | Edit the track and set stripes per level to 1 or more |
| The member sees no session count | The member has no current level, holds the top level, or the next level has no **Sessions** set | Set **Sessions** on the next level |
| The session count looks lower than expected | Only sessions after the day the current level was earned count, and a booking only counts once it is completed | Check the level's earned date in the member's progression history |
| A booked member who did not show up was counted | Your gym does not require check-in, so a finished booking counts as attended | Mark the booking as a no-show, or require check-in |
| Deleting a history entry changed the member's current level | Current level is always the most recent history entry per track | This is expected; the next-most-recent entry becomes current. Add a new assignment to restore the intended level |
## Related
* [Member profiles](/members/member-profiles) - where you assign, promote, and review a member's progression and history.
* [Members analytics](/analytics/members) - reporting across your member base, including progression.
* [Matches](/events/matches) - the 1club Rating system for objective, match-based skill ranking alongside progression tracks.
* [Smart tagging](/settings/smart-tagging) - tag members who are eligible for promotion, close to it, stalled at their level or recently promoted.
# Seasons
Source: https://docs.1club.ai/settings/seasons
Define the fixed period a season pass is sold for - a winter, a term, a league - set the volume discount for holding more hours a week, and choose when the season goes on sale.
A season is the fixed period a [season pass](/schedule/season-passes) is sold for: a winter, a term, a league. It carries a name your members recognize, a first and last day, and the volume discount that rewards holding more hours each week. Everything else about the price comes from your [pricing rules](/settings/pricing-rules). You set up the season, and staff sell season passes against it from the [schedule](/schedule/calendar).
## When to use it
Create a season when members commit to the same weekly court time for a fixed period and pay for it as one contract. Its price is its hourly rates from pricing rules, multiplied by its weeks, with the volume discount applied once, so your published price table stays in one place and members who hold more hours a week pay less per hour.
Typical uses:
* **An indoor tennis winter** - a tennis venue sells October to April as one season, and a member holding two one-hour courts a week lands on the 5% step of the volume discount.
* **One price list per venue** - an organization with two gyms on different price lists runs a separate season at each, because a season belongs to a single gym.
* **Stopping sales without touching contracts** - turn **On sale** off to stop offering a season, and the passes already sold are untouched.
See [Worked example: an indoor tennis winter](#worked-example-an-indoor-tennis-winter) for the full setup.
## The list page
Open **Settings** > **Billing** > **Seasons** (route `/settings/billing/seasons`). It sits directly after **Pricing Rules**, because they are the same task.
The table has these columns:
* **Name** - what you called the season. This is the name members see everywhere.
* **Dates** - first and last day.
* **Length** - the season's length in weeks.
* **Setup** - a chip reading **On sale**, **Pricing not set up**, **Pricing needs fixing**, or **Not on sale**. Hover it to read the exact issue.
* **Passes** - how many season passes have been sold against this season.
* **Status** - whether the season is upcoming, running, or finished.
Use **New season** to create one, or the row menu to edit or delete. When you have no seasons yet, the page explains what a season is and offers the same button.
## Creating a season
Click **New season**. The dialog has four sections.
### Season
* **Name** (required) - whatever your members call it, for example *Winter 2027*. This name appears on the schedule, on the member's pass, and on the sale.
* **Club** - the gym the season runs at. Once a pass has been sold against the season this is locked, because the courts it holds belong to that venue.
### Dates
* **First day** and **Last day** - the season's range, inclusive. The subtitle shows the resulting length in weeks as you pick.
* **Existing subscribers claim by** - an optional date you record for your own renewal communication with last season's holders. The platform does not enforce it: nothing is reserved and no reminder is sent.
A range that runs backwards is refused. A range that is not a whole number of weeks is allowed but warns you, because some weekdays then occur more often than others: two members buying one weekly hour would be charged different totals depending on the day they picked.
### Pricing
* **Revenue account** - where the season's income posts. Optional, exactly like any other plan; leave it empty to use your organization default.
* **Volume discount** - the ladder. Each step is **From (hours/week)** plus a **Discount**, read as "hold this many hours a week, get this discount".
A new season starts with a sensible default ladder:
| From (hours/week) | Discount |
| - | - |
| 1 | 3% |
| 2 | 5% |
| 3 | 7% |
| 4 | 10% |
The highest step a member reaches is the one that applies, and the last step is the cap, so someone holding five or six hours a week simply gets the top discount. Below the first threshold there is no discount. Steps are keyed on **hours**, not on the number of weekly slots, so a single 90-minute slot lands on the one-hour step rather than falling into a gap.
Two steps cannot start at the same number of hours, hours must be between 0 and 168, and a discount must be between 0% and 100%. Without any step at all, every season pass sells at full price.
A season plan carries no price field. The pass costs the sum of the hours it books, priced by your [pricing rules](/settings/pricing-rules), with one volume discount applied to that total. Set an hourly rate per time band in Pricing rules and the season prices itself from them.
### Availability
* **On sale** - turn this off to stop selling the season. Passes already sold are untouched; the sale option simply stops being offered on the schedule.
## What saving a season creates
Saving writes two things: the season itself, and the plan the pass is sold through. You never create that plan by hand, and it never appears in a plan catalogue - not in the member portal, not on the marketplace, not in POS, and not in the plan picker on a booking - because a season pass can only be sold from the schedule, where the courts are.
If the season saves but its pricing does not, the dialog tells you so and keeps your input: fix the field it points at and save again. You will not end up with a duplicate season.
## Readiness states
The **Setup** chip answers one question: can you sell this season right now?
| Chip | What it means |
| - | - |
| **On sale** | Priced and available. The schedule offers the sale inside its dates. |
| **Pricing not set up** | The season has no plan to sell yet, so nothing can be priced. |
| **Pricing needs fixing** | The plan exists but is misconfigured. Hover the chip for the reason. |
| **Not on sale** | Configured correctly, but the **On sale** toggle is off. |
The reasons behind **Pricing needs fixing** are specific, and each names the field to change:
* A season is paid once, not on a repeating cycle.
* The season needs to belong to one gym.
* The pricing belongs to a different gym than the season.
* A season counts sessions in hours.
* No volume discount is set.
## Editing and deleting
Editing a season updates its name, dates, discount ladder, revenue account, and availability. Two things are protected:
* **The gym is locked once a pass has been sold**, because the sold courts belong to that venue.
* **Deleting is only possible while nothing has been sold** against the season. Once a pass exists, the season is part of a paid contract; turn **On sale** off instead. Deleting is permanent and asks for confirmation.
## Worked example: an indoor tennis winter
A tennis venue sells its indoor winter as a season, October to April.
1. **Pricing rules first.** Author one rule per time band with the **Absolute price** type, matching the printed price table: off-peak morning 41, day 39, peak 47, late 41, weekend 41. Add a second rule per band scoped to the season's plan with the member rate: 37, 34, 42, 37, 37. See [Pricing rules](/settings/pricing-rules).
2. **Create the season.** Name it *Winter 2026/27*, pick the gym, set the first and last day so the range is a whole number of weeks, and record your claim-by date for last season's holders.
3. **Set the ladder.** The default 1/2/3/4-hour steps at 3/5/7/10% match how most venues publish theirs. Adjust the percentages to your own table.
4. **Turn On sale on**, then sell from the schedule. A member holding a Tuesday 19:00 court and a Thursday 10:00 court holds two hours a week, so they land on the 5% step, and each week is priced by the band its hour falls in.
## Current limits
* **Renewal is not automated.** A season pass never auto-renews, and the **Existing subscribers claim by** date is a note to yourself, not an enforced hold. Next season is a fresh sale.
* **One gym per season.** A multi-venue organization creates one season per venue.
* **No self-serve purchase.** Members cannot buy a season pass themselves yet; the sale happens in the admin, on the schedule.
* **The ladder is per season**, not per plan or per member. Two different discount structures need two seasons.
## Troubleshooting
**Issue**: The schedule does not offer **Add Season Pass** on a date inside my season.
**Solution**: The option appears only when the season is actually sellable on that date. Check the **Setup** chip: a season that is switched off, or never finished pricing, still sits in its own date range but cannot be sold. Also confirm you hold the permission to create memberships.
**Issue**: Every season pass sells at full price.
**Solution**: The volume discount ladder has no steps. Add at least one.
**Issue**: The dialog warns that my range is not a whole number of weeks.
**Solution**: That is a warning, not an error, and the season saves. Adjust the last day so the range covers whole weeks if you want every weekday to occur the same number of times; otherwise expect a Monday hour and a Saturday hour to total differently.
**Issue**: I cannot change the gym on a season.
**Solution**: A season that has sold a pass is locked to its gym, because the courts held for that pass belong to it. Create a new season for the other venue.
**Issue**: I cannot delete a season.
**Solution**: A season with a pass sold against it cannot be deleted, so financial history and the booked courts stay intact. Turn **On sale** off to retire it.
## Related
* [Season passes](/schedule/season-passes) - Selling, cancelling, and makeup hours.
* [Pricing rules](/settings/pricing-rules) - Where the hourly rates and the member rate live.
* [Schedule](/schedule/calendar) - Where a season pass is sold.
* [Memberships](/sales/memberships) - Where a sold pass appears afterwards.
# Smart tagging
Source: https://docs.1club.ai/settings/smart-tagging
Rule-based tags that 1Club applies to members automatically, segmenting them by payments, lifecycle, membership expiry, attendance, loyalty, age, household role, language, and the sports they book.
Smart tagging applies tags to your contacts automatically based on rules you configure once. The engine looks at how a member pays, how long they've been with you, when their membership expires, how often they check in, how active they've been overall, their age, their household role, their preferred language, and which sports they book. From then on 1Club keeps every contact's tags in sync, so staff can filter and target members without curating lists by hand. Smart tags sit next to manual tags everywhere they're used - member lists, [promotion segments](/sales/promotions), and messaging - but you can't add or remove a smart tag on an individual profile; you change the rule instead.
## When to use it
Use smart tags when you want segments that maintain themselves - who is overdue, whose membership is about to expire, who plays padel - and then act on them with promotions, automations, and reports.
Typical uses:
* **Marketing by sport** - a padel and tennis gym tags members who book a sport regularly and targets a padel-night promotion at the padel tag only.
* **Chasing renewals** - a gym pairs the **Expiring Soon** tag with a renewal discount.
* **Recovering payments** - a Pilates studio builds an automation that messages members tagged **Overdue**.
* **Age and family messaging** - a martial-arts school tags kids and juniors automatically, and tags parents from household accounts so it can message them about their children.
See [Worked examples](#worked-examples) for the categories and settings behind each of these.
## How it differs from manual tags
You can attach two kinds of tags to a contact:
| | Manual tags | Smart tags |
| - | - | - |
| **Where they come from** | You create them and apply them to specific contacts | 1Club applies them automatically based on rules |
| **Who controls membership** | You add or remove them per contact | The engine, based on the contact's current data |
| **Configured in** | **Settings** > **Members** > **Manual Tags**, or any tag chip on a contact | **Settings** > **Members** > **Smart Tags** |
| **Editable per contact** | Yes | No - change the rule, not the tag on the contact |
| **Examples** | "VIP", "Family of John", "Yoga teacher" | "Regular", "Overdue", "Expiring soon", "Adult" |
Both types behave identically when you filter or segment. A promotion that targets "Overdue" works the same whether that tag is manual or smart.
## Who gets smart tags
The engine treats contacts differently depending on what they are:
* **Members** (contacts with bookings or memberships) are eligible for every enabled category.
* **Leads** (contacts with no activity yet) only receive the demographic and lifecycle-of-contact categories: **Age**, **Contact stages**, **Relationship**, and **Language**. The activity-based categories (Attendance, Financial, Lifecycle, Loyalty, Membership, Interest, Progression, Assessment) don't apply until the lead becomes a member.
* **Staff** (instructors, managers, admins) never receive smart tags. Any auto-tag they picked up before becoming staff is stripped on the next evaluation; their manual tags are kept.
* **Archived contacts** are frozen - their tags stay as a snapshot and are not recomputed.
## Categories
Each category has an **Enable this tag category** switch. Within a category, a contact gets at most one tag - the highest matching tier - except **Interest** (one tag per qualifying sport) and **Relationship** (a contact can be both Parent and Child). Disable a category and every tag it produced is removed on the next sync.
The categories below are listed in the order they appear on the page. Four are enabled out of the box: **Financial**, **Lifecycle**, **Membership**, and **Contact stages**.
### Financial (enabled by default)
Flags members with unpaid or overdue payments. Each tier defines the minimum number of days a payment can be overdue before the tag applies.
Defaults:
* **Unpaid** - owes money, but nothing is past due yet (at least one pending, failed, or overdue charge dated today or later).
* **Past Due** - a payment is 1+ day overdue.
* **Overdue** - a payment is 7+ days overdue.
The field is labelled **Min Days Overdue**. The **Unpaid** tier is fixed and can't be deleted; you can add, rename, recolour, or remove the past-due tiers.
### Lifecycle (enabled by default)
Tracks how long a member has been with you, counted from the start date of their first membership. Requires an **active** membership, so a lapsed member drops out of the category.
Defaults: **Trial** (day 1+), **Probation** (day 15+), **Active** (day 46+). The field is labelled **Min Days Since First Membership**.
### Membership (enabled by default)
Monitors where a member sits relative to their membership expiry date. A **positive** value means days *before* expiry; a **negative** value means days *after*.
Defaults: **Expiring** (7 days before), **Expiring Soon** (3 days before), **Expired** (7 days after).
Two behaviours worth knowing:
* **Before-expiry** tiers only match memberships that are active and **not auto-renewing**. An auto-renewing membership is never flagged as expiring, because it isn't going to lapse.
* **After-expiry** tiers only apply when the member has **no** active membership at all.
### Loyalty (disabled by default)
Ranks members by total lifetime activity (bookings + check-ins). Use it for tenure-style recognition, in contrast to Attendance, which measures recent engagement.
Defaults: **Legend** (50+), **Champion** (25+), **Pro** (10+), **Rising Star** (5+), **Beginner** (fewer than 5). The field is labelled **Minimum Activity**.
### Attendance (disabled by default)
Groups members by how often they check in over a recent window. Each tier defines a **Minimum Visits** count and a **Period (Days)**.
Defaults: **All-Star** (16+ visits / 30 days), **Regular** (8+), **Casual** (4+), **Low Activity** (1+), **Inactive** (0 in the period). Thresholds and periods are fully editable.
### Age (disabled by default)
Tags members by age group from their date of birth. Each tier has a **Min Age** and **Max Age**.
Defaults: **Infant** (0-4), **Kid** (5-12), **Junior** (13-17), **Adult** (18-64), **Senior** (65+). Edit the bounds to match how you market and price. These defaults are the same age bands a [class type's](/settings/class-types#age-group-and-difficulty) age group uses, so a Kids class and the Kid tag start out meaning the same ages. Editing a tag's bounds changes only the tag.
### Contact stages (enabled by default)
Tags contacts by how recently they were added - useful for spotting fresh leads. The field is **Max Days Since Created**.
Default: **New** (within 14 days).
### Relationship (disabled by default)
Tags members by their household role. Household owners and adults are tagged as parents; dependents are tagged as children.
This category has two fixed tags - **Parent** and **Child**. You can only edit their display name and colour; there are no thresholds and you can't add or remove tiers. A contact can carry **both** if, for example, they're an adult in one household and a dependent in another. It applies to leads as well as members.
### Language (disabled by default)
Tags members by their preferred language, taken from their linked account profile. There's one fixed tag per supported locale: **English**, **Español**, **Français**, **Deutsch**, **Български**, **Čeština**. As with Relationship, you can only edit the display name and colour - not the locale mapping - and you can't add or remove rows.
A contact with no linked user account (a walk-in or lead who never registered) has no known language and receives no language tag.
### Interest (disabled by default)
Sport-by-sport tags applied to members who've booked the same sport at least N times. Tags are generated dynamically, one per sport that crosses the threshold, so a member can hold several interest tags at once. The field is **Minimum Bookings per Sport** (default 3). Use these to target audiences with offers relevant to what they already do - for example, a padel-only promotion for members tagged with the padel interest.
### Progression (disabled by default)
Tags members by their levels on your [progression tracks](/settings/progression). The four tags are fixed: you can edit each one's name, colour and threshold, but you can't add or remove rows. A member who holds several tracks gets a tag if any of their tracks qualifies, and can carry more than one.
* **Eligible for Promotion** - has the sessions the next level requires (see [Sessions to the next level](/settings/progression#sessions-to-the-next-level)).
* **Close to Promotion** - not eligible yet, but has reached a share of the sessions needed (default 80%).
* **Stalled at Rank** - holds a level but hasn't attended a session in the last N days (default 30). The check runs per track, counting only the sessions that count toward that track, so a member training only in classes linked to another track is stalled on this one.
* **Recently Promoted** - was promoted in the last N days (default 14). A member's first level, a stripe or a correction never counts as a promotion.
### Assessment (disabled by default)
Tags members by their [assessments](/members/assessments). The three tags are fixed, like Progression.
* **Not Assessed Recently** - attended at least one session in the last N days (default 30) but has no assessment in that time. Members who stopped coming aren't flagged.
* **Top Performer** - the average of their last 3 assessments is at least a share of the scale (default 80%).
* **Needs Attention** - the average of their last 3 assessments is at most a share of the scale (default 50%).
The average takes every score on those assessments as a share of its template's scale - the same measure as the average on the member's profile.
### Engagement scoring (disabled by default)
Sitting at the bottom of the same page, engagement scoring is a related feature rather than a tag category. When enabled, it computes a member engagement score from four weighted inputs - **Financial**, **Services**, **Physical**, and **Engagement** - whose weights must sum to 100%. The score surfaces on the [member engagement](/analytics/marketing) view; it doesn't create tags.
## Configuring rules
1. Go to **Settings** > **Members** > **Smart Tags**. (The **Manual Tags** tab next to it manages plain manual tags; see [Manual tags](#manual-tags).)
2. For each category:
* Toggle **Enable this tag category** on or off.
* Edit a tier - adjust the threshold (visits, days, age range, and so on) and rename or recolour the tag.
* Add a tier with **Add tag** (not available for Relationship, Language, Interest, Progression, or Assessment, whose rows are fixed or derived).
* Delete a tier you don't need (the fixed Unpaid, Parent/Child, language, progression, and assessment rows can't be deleted).
3. Save with the floating save alert at the bottom of the page.
Tag names must be unique within a category, and the dialog validates thresholds (for example, minimum visits can't be negative and overdue days must be at least 1).
After you save, 1Club syncs the tag registry (renames, new tiers, deletions) and re-evaluates affected contacts. To see a big change take effect immediately, use **Sync tags** (below).
## How and when tags are kept in sync
Smart tags are recomputed:
* **Reactively**, on the events that could shift a tag - when a contact is created, a membership is created or cancelled, a payment succeeds or fails, a booking is created, cancelled, marked no-show, or checked in, a member is promoted or a level is corrected or removed, an assessment is added, edited or removed, or a member has a birthday. The affected contact is re-evaluated right away.
* **Nightly**, as a backstop, for every contact in the organization (runs at 03:00 UTC). This is what catches purely time-based transitions - a membership crossing its expiry window, a lead ageing past "New", a household-role change.
* **On demand**, via the **Sync tags** button on the Smart tagging page. It re-evaluates everyone and reports how many members were updated.
Re-evaluation only touches tags the engine manages. Manual tags are never removed by a sync.
## Where smart tags show up
Smart tags behave exactly like manual tags wherever tags are consumed:
* **Member lists** - filter the list by tag; a tag chip tells you whether it's manual or smart.
* **Contact profile** - both kinds appear in the same Tags section.
* **[Promotion segments](/sales/promotions)** - restrict a voucher or discount to contacts carrying specific tags.
* **Automations** - use tags in entry conditions to control which contacts a workflow runs for.
* **Reports and analytics** - break KPIs down by tag to compare cohorts (for example, revenue from "Regular" vs. "Overdue").
Because smart-tag membership is recomputed continuously, these segments stay current without anyone curating lists.
## Editing tags on a contact
You **cannot** add or remove a smart tag on a single contact from their profile. A smart tag is the output of a rule, so the engine would just put it back (or take it off) on the next evaluation. To change who has a tag:
* **Change the rule** under **Settings** > **Members** > **Smart Tags** (this affects every contact).
* Or, to flag one person, use a **manual tag** instead - manual tags can be added and removed per contact at any time.
## Manual tags
Manual tags are the ones you apply by hand. Manage the list under **Settings** > **Members** > **Manual Tags**: click **Add Tag**, enter a **Tag Name**, and optionally pick a **Color**. Click a tag to edit it. Deleting a tag removes it from every member who has it.
You add and remove manual tags on a single contact from the tag chips on their profile.
## Renaming or disabling categories
* **Renaming a tier** updates the tag name across every contact that has it.
* **Disabling a category** removes every tag in that category from every contact on the next sync.
* **Re-enabling a category** re-applies its tags on the next sync.
* **Deleting a tier** removes it from contacts that currently have it (next sync) and leaves the rest of the category intact.
Promotion and automation filters that referenced a disabled or deleted tag simply stop matching anyone - they don't break.
## Worked examples
### Racket-sports club: fill courts and chase renewals
A padel and tennis club wants to market by sport and keep courts busy.
* Enable **Interest** (Minimum Bookings per Sport = 3) so regulars pick up padel and tennis interest tags. Target a padel-night promotion at the padel tag only.
* Enable **Attendance** to separate court regulars ("Regular", "All-Star") from members who are slipping ("Low Activity", "Inactive").
* Keep **Membership** on and pair the "Expiring Soon" tag with a renewal discount.
### Martial-arts school with a kids' programme
A jiu-jitsu and muay-thai academy runs junior classes and family memberships.
* Enable **Age** so juniors (13-17) and kids (5-12) are tagged automatically for age-appropriate class messaging.
* Enable **Relationship** to tag **Parent** and **Child** from household accounts, so you can message parents about their dependents' progress.
* Enable **Progression** to see who is **Eligible for Promotion** before a grading, and to reach members who are **Close to Promotion** or **Stalled at Rank** with a nudge to keep training.
* Enable **Assessment** so coaches see who is **Not Assessed Recently**, and so parents of a **Top Performer** can be congratulated automatically.
### Pilates/yoga studio: recover payments and reward loyalty
* Leave **Financial** on and build an automation that messages the "Overdue" segment before it becomes "Overdue" at 7 days.
* Enable **Loyalty** to recognise long-standing members ("Legend", "Champion") with perks.
### Multi-language community gym
Enable **Language** so members are tagged by their preferred locale (for example Español or Français), then send each promotion in the right language by segmenting on the language tag.
## Tips & best practices
* **Start with the defaults.** Financial, Lifecycle, Membership, and Contact stages are on out of the box because almost every club needs them. Add Attendance, Loyalty, Interest, and the demographic categories as you're ready to act on them.
* **Use Attendance for short-term campaigns and Loyalty for long-term recognition.** They answer different questions: "who's gone quiet recently?" vs. "who's been with us the longest?"
* **Don't duplicate categories with manual tags.** If you find yourself hand-tagging members "Overdue", adjust the Financial thresholds instead.
* **Remember auto-renewing memberships never show "Expiring".** If you rely on the Membership tags for renewals, they target non-auto-renewing plans.
* **Re-check thresholds periodically.** What counted as "Regular" attendance a year ago may not match how your club runs today.
## Troubleshooting
**Issue**: A member isn't getting an activity tag (Attendance, Financial, Loyalty, and so on).
**Cause / fix**: The contact is still a lead with no bookings or memberships, so only demographic categories apply - or the category is disabled. Confirm they have activity and the category is on.
**Issue**: A member has no Language tag.
**Cause / fix**: The tag comes from a linked account's preferred language. A contact with no linked user account (a walk-in or unregistered lead) can't be tagged. Also confirm the Language category is enabled.
**Issue**: A member isn't tagged "Expiring" even though their membership ends soon.
**Cause / fix**: Auto-renewing memberships are never flagged as expiring. The before-expiry tiers only match active, non-auto-renewing memberships whose end date falls inside the window.
**Issue**: A staff member has no smart tags.
**Cause / fix**: That's by design - staff are excluded from smart tagging entirely.
**Issue**: I changed a threshold but tags didn't update.
**Cause / fix**: Reactive re-evaluation fires on data changes, not rule changes. After a rule change, click **Sync tags** to apply it now, or wait for the nightly run at 03:00 UTC.
## Related
* [Members](/members/overview) - where tags are surfaced and used.
* [Promotions](/sales/promotions) - restrict offers to specific tags via segment filters.
* [Marketing analytics](/analytics/marketing) - break down performance by member segment and view engagement scores.
# SMS
Source: https://docs.1club.ai/settings/sms
Text members from the Inbox and automations, from your gym's own sender name or toll-free number. Set up business details, a sender name, country registrations and a US or Canadian number, and handle opt-outs.
SMS lets your gym text members from the [Inbox](/inbox/compose) and from [automations](/marketing/automations), the same way you already send email and WhatsApp. Every text leaves from your gym's own sender: your gym's name in Europe, and your gym's own toll-free number in the US and Canada. An owner or admin sets it up once under **Settings** > **Communication** > **SMS**, and from then on anyone who sends messages can pick **SMS** when they compose.
## When to use it
SMS reaches members who don't read email and haven't installed the app, and it arrives on any phone without an account or a connection. It is the channel for short, time-sensitive news.
* **A class moves at the last minute** - the coach is ill and the 18:00 yoga class moves to Studio 2. The front desk texts everyone booked, and they read it before they leave home.
* **Reminders that people actually see** - a padel gym adds an SMS step to its booking reminder automation, so members who ignore email still get "Your court is at 19:00 tomorrow".
* **Members abroad or in a second location** - a climbing gym in Sofia texts its Bulgarian members by name once the name is registered there, and its Canadian members from its own toll-free number.
## How it works
SMS has no single identity that works everywhere. Each country's carriers decide what a business may text from, so 1Club picks the sender by the country of the **member's phone number**, not by where your gym is.
| Member's phone number | What they see as the sender | What you need first |
| - | - | - |
| Germany, France, United Kingdom, Portugal, Italy, Netherlands, Greece, Poland | Your gym's sender name | An approved sender name |
| Bulgaria, Spain, Czechia, Austria, Ireland | Your gym's sender name | An approved sender name, registered for that country (two to four weeks) |
| United States, Canada | Your gym's own toll-free number | A verified toll-free number (about three business days) |
| Any other country | Not texted | Not supported yet |
There is no fallback sender. If your gym has no ready sender for a member's country, that member is skipped and the send tells you why, so nobody ever gets a text from a name or number that isn't yours.
A few rules shape everything below:
* **Every sender name is checked by 1Club before it is used.** All gyms text through one shared SMS account, so a name that isn't your gym's own brand (another business, a bank) would put everyone's texts at risk. We usually review a name within one business day.
* **A sender name can't be replied to.** Members see the name instead of a number and have nothing to answer. Texts from your toll-free number can be answered, but only with the opt-out keywords (see [Opt-outs](#opt-outs)). Replies don't arrive in the Inbox.
* **Texts from your toll-free number start with your gym's name**, for example "Sofia Climbing: Your class moved to Studio 2." US and Canadian carriers expect the business to be named in the message. Texts from a sender name don't need it, because the name already says who is texting.
* **SMS is a paid-plan feature.** On the Free plan and during the trial, the SMS page can't be set up and the composer doesn't offer SMS.
## Set up SMS
Open **Settings** > **Communication** > **SMS**. The page has four cards, one per step. You need the **settings.manage.organization** permission to change anything; **settings.read.organization** is enough to see the status.
Click **Add business details**. Carriers check these against the company register, so enter the legal entity behind your gym, not its brand.
* **Legal business name** and **Business type** (**Private company**, **Publicly traded company**, **Non-profit** or **Sole proprietor**).
* **Register**, **Registration number** and **Country of registration** - for example your VAT number, an EIN in the US, or a Business Number in Canada. A sole proprietor skips these.
* **Business address**.
* **Website**, **Privacy policy URL** and **Terms and conditions URL**. Carriers check that your website is live and that both policies are published on it.
* **Contact person** - someone the carriers can reach about your senders, with a **Mobile number** in international format (for example `+359888123456`).
The form starts from your organization's website and the legal details of your default [billing entity](/settings/billing-entities), so most fields are already filled. Check every one: a mismatch with the register is the most common reason a sender is rejected.
Enter the name members should see and click **Submit for review**. A sender name must be:
* 3 to 11 characters long.
* Latin letters, digits, spaces and `- + _ &` only, with at least one letter.
The card shows **In review** until we decide, then **Active**. If we turn a name down, the reason appears on the card and you can submit another one. Once approved, the name sends straight away in Germany, France, the United Kingdom, Portugal, Italy, the Netherlands, Greece and Poland.
You can ask for a new name at any time. While it waits for review, your approved name keeps sending.
In **Countries that require registration**, tick the countries where you have members (Bulgaria, Spain, Czechia, Austria, Ireland) and click **Request registration**. We file the registration with the carriers for you and update the card when they answer, usually in two to four weeks. Each country shows **In review**, **Active** or the reason it was rejected; you can request a rejected country again.
Requesting a registration needs saved business details and an approved sender name.
Only needed if you have members with US or Canadian phone numbers. In the **United States and Canada** card, click **Get a number** and tell the carriers how members agree to your texts:
* **Where members agree** - **Online sign-up form**, **Paper form at the club**, **Verbally at the front desk** or **QR code**.
* **Screenshot** - a link to a screenshot or photo of the form, page or sign where members agree to texts.
* **Texts a month** - roughly how many texts you expect to send.
Click **Submit for verification**. 1Club gets your gym its own toll-free number and submits it, with your business details, for verification, which usually takes about three business days. The card shows the number while it is verified, then **Active**. If the carriers reject it, the reason appears on the card: fix what they asked for and click **Correct and resubmit**. You keep the same number.
To stop texting US and Canadian members, click **Release number**. The number is given up and can't be recovered; you can get a new one later.
**Get a number** is only available when your gym is based in the US or Canada,
or has at least one contact with a US or Canadian (`+1`) phone number. A
toll-free number costs money every month whether or not it sends, so a gym
with nobody to text there doesn't get one.
### Why not just use your gym's name?
Because carriers only accept a short name in a limited alphabet. Most gym names are longer than 11 characters, and names in Cyrillic, Greek or with accents aren't accepted at all, so "Sofia Climbing Club" or "Йога Студио" can't be used as they are. Pick a short version members will recognize, such as `SofiaClimb` or `YogaStudio`.
In the registration countries, carriers also register the exact name. Once you have requested a registration, the name can no longer change (unless every registration you asked for was rejected).
## Send a text
1. Open the **Inbox** and click **Compose**.
2. Pick **SMS**. It only appears once your gym can text: a paid plan and at least one approved sender.
3. Choose recipients manually or with a segment. The contact picker only lists contacts with a phone number who haven't opted out.
4. Write the message and click **Send Message**.
Under the text, a counter shows how long the message is and how many SMS it takes, for example "47 characters, sent as 1 SMS". Carriers bill per SMS, not per message:
* A message in the standard SMS alphabet fits 160 characters in one SMS, and 153 per SMS once it is longer.
* One character outside that alphabet (Cyrillic, most emoji) switches the whole message to 70 characters, and 67 per SMS once longer. A Bulgarian message of 100 characters is two SMS.
* Variables such as a first name are counted as you wrote them, so the real text can be a little longer.
* For texts from your toll-free number, the counter includes your gym's name at the start.
If your sender for your own country is still being reviewed (for example the registration for Bulgaria is in progress), the composer says so above the recipients: members there are skipped until it is approved.
### Who isn't texted, and why
After a send, 1Club tells you who was skipped. If nobody could be texted, nothing is sent and the message names the reasons:
* **Replied STOP** - the member opted out (see [Opt-outs](#opt-outs)).
* **No usable phone number** - the number is missing or can't be read.
* **Their country needs a sender your gym has not set up yet** - set it up, or wait for the review to finish.
* **Their country isn't supported** - the number is from a country not in the table above.
Phone numbers saved without a country code, such as `0888 123 456`, are read in your organization's country.
### Automations and other texts
* A **Send message** step in an [automation](/marketing/automations) can use the **SMS** channel. If the text can't go out for one of the reasons above, the step still completes and its other channels (email, app) are sent. If SMS is the step's only channel and the text fails to send, the step is retried.
* Invitations sent by SMS, member notifications sent by SMS to members without an email address, and [staff alerts](/settings/notifications) by SMS also leave from your gym's sender, and follow the same rules.
* Sign-in codes and phone verification codes are the only texts that come from 1Club itself.
## Opt-outs
A member who doesn't want texts any more is never texted again, from the Inbox, automations or notifications.
* **Texts from your toll-free number** - the member replies **STOP** (also UNSUBSCRIBE, CANCEL, END, QUIT or STOPALL). They get a confirmation and are opted out. Replying **START** (or YES, UNSTOP) lets them be texted again.
* **Texts from your sender name** - the member can't reply, so they tell you, at the desk, by email or by phone. Open their profile, click the **More actions** menu (**...**) and choose **Stop SMS**. Choose **Allow SMS again** if they change their mind.
An opted-out contact shows a **No SMS** chip next to their phone number. An opt-out belongs to the phone number, not the person: every contact in your gym with the same number is opted out too, for example a parent whose number is on their child's profile.
A member who opted out by WhatsApp isn't texted either: a STOP on one
messaging channel is treated as a request to stop messaging them. **Allow SMS
again** doesn't override a STOP the member sent to your toll-free number -
they need to reply START themselves.
## Permissions
| Action | Permission |
| - | - |
| See the SMS page and its status | `settings.read.organization` |
| Change business details, the sender name, registrations or the toll-free number | `settings.manage.organization` |
| Send SMS from the Inbox | `messages.create.own` or broader |
| **Stop SMS** and **Allow SMS again** on a profile | `members.update.club` |
## Troubleshooting
| Symptom | Likely cause | Fix |
| - | - | - |
| **SMS** isn't offered in the composer | Your gym is on the Free plan or in its trial, or has no approved sender yet | Upgrade the plan, then set up a sender under **Settings** > **Communication** > **SMS** |
| A member in Bulgaria, Spain, Czechia, Austria or Ireland isn't texted | The sender name isn't registered for their country yet | Request the registration, or wait until it shows **Active** |
| A US or Canadian member isn't texted | Your toll-free number isn't verified yet | Wait for **Active**, or correct and resubmit a rejected verification |
| **Get a number** is disabled | No business details yet, or nobody in your gym has a US or Canadian number | Add business details; add the member's number with its `+1` code |
| The sender name field is locked | A country registration was requested, so carriers hold the exact name | Keep the name, or ask 1Club support if it has to change |
| "None of the texts could be sent" | The carrier refused every text, usually because of invalid numbers | Check the numbers and try again |
| A member says they get no texts | They opted out (look for **No SMS** on their profile) | Choose **Allow SMS again** if they asked to receive texts; on the toll-free number they reply START |
## Related
* [Compose](/inbox/compose) - Writing and sending a message on any channel.
* [Automations](/marketing/automations) - Send message steps that can text members.
* [Email sending](/settings/email-sending) - The email counterpart: sending from your own domain.
* [WhatsApp](/settings/integrations/whatsapp) - Two-way messaging on your own WhatsApp Business number.
* [Notifications](/settings/notifications) - Staff alerts by SMS.
# Templates
Source: https://docs.1club.ai/settings/templates
Reusable message content in 1Club - custom templates for manual sends, plus the system template catalog you can customize for booking, membership, and lifecycle emails. Variables resolve per recipient at send time.
A template is reusable message content that 1Club stores so you do not retype it every time. There are two kinds: **custom templates** you author for manual sends, and **system templates** - the built-in catalog behind booking confirmations, receipts, welcome emails, and other automatic messages. An owner or admin writes and customizes templates, staff with template access pick custom ones when composing, and members receive system templates automatically.
## When to use it
Templates keep repeated messages consistent and in your gym's own voice. A custom template saves you rewriting the same note from the inbox, and customizing a system template rewords an automatic email without rebuilding it - you copy the built-in version, edit the copy, and can reset to the default at any time. Both use `{{variable}}` tokens that resolve per recipient when a message is sent, so each member sees their own name, booking, or plan.
Typical uses:
* **A welcome note for new members** - a padel gym keeps a `message` template that greets each new member by first name and invites them to book an intro session.
* **A booking confirmation in your own words** - a padel gym rewords the Booking Confirmation email to add court etiquette and the member's door PIN, which disappears for members without one.
* **A warmer renewal reminder** - a yoga studio customizes the Membership Expiration Reminder with the plan name, expiry date, and renewal price.
See [Welcome message example](#welcome-message-example-racket-sports), [padel booking confirmation](#worked-example-padel-booking-confirmation), and [yoga membership renewal](#worked-example-yoga-membership-renewal) for the full text of each.
Template content is channel-neutral: 1Club stores structured blocks (paragraphs, buttons, images), and each channel renders its own projection - email through the branded transactional layout, SMS and WhatsApp as plain text.
Customizing templates is a paid capability. Free organizations and organizations still on the Grow trial see a **plan-locked** banner and an **Upgrade** button on the Templates page. The **Add template** button is disabled until you upgrade.
## The Templates list
Both kinds of template live at **Settings** > **Communication** > **Templates**. The grid has four columns:
* **Name** - the template name.
* **Source** - a **Custom** chip for templates you authored, a **System** chip for built-in templates, and an additional **Customized** chip when you have an active override for a system template.
* **Type** - `message`, `transactional`, or `receipt`.
* **Actions** - depends on the source (see below).
A customized system template is shown as its **system** row carrying both the **System** and **Customized** chips; the underlying override is folded into that row rather than appearing as a separate line, so you never see duplicates.
### Row actions
* **Custom template** - **View**, **Edit**, **Copy**, **Delete**.
* **System template** - **Customize** if you have not overridden it yet, or **Edit** if you already have a customized version.
## Creating a custom template
Use a custom template for the ad-hoc notes you send from the [Inbox](/inbox/overview) - a class reminder, a promotional blurb, a re-engagement note.
1. On the Templates page, click **Add template**. (The button is disabled if your plan does not include template customization.)
2. Fill in **Name** (required).
3. Choose the **Type**:
* **Message** - shows up in the inbox compose picker for manual sends.
* **Transactional** - a system-style email body.
* **Receipt** - invoice receipt content.
4. For **message** and **transactional** types, add a **Subject** and optional **Preview text** (the snippet shown in the inbox preview line of an email client).
5. Write the body in the block editor. Add paragraphs, buttons, images, and dividers.
6. Insert variables from the **Available Variables** panel on the right. Click a variable chip to copy `{{token}}` to your clipboard, then paste it into the body or subject.
7. Click **Save**.
To duplicate an existing custom template, use the **Copy** action on its row. A copy is created with `(Copy)` appended to the name and opens in the editor.
### Welcome message example (racket sports)
A `message` template you send to new padel members after they join:
```text theme={null}
Subject: Welcome to {{organizationName}}!
Hi {{contactFirstName}},
Welcome to {{organizationName}}. Your first court is on us - reply to
this message and we'll get you booked for an intro padel session.
Questions? Reach us at {{organizationEmail}} or {{organizationPhone}}.
- {{senderName}}
```
## System template catalog
System templates are the built-in messages 1Club sends automatically. The Templates page lists only the **customizable** ones - the emails you are allowed to rebrand and reword. Each declares its own variable set, which the editor surfaces when you customize it.
| Template | What it is sent for |
| - | - |
| Booking Confirmation | Sent when a booking is confirmed |
| Booking Update | Sent when a booking is changed |
| Booking Cancellation | Sent when a booking is cancelled |
| Booking Player Welcome | Sent to additional players added to a booking |
| Booking Reminder | Sent before an upcoming booking |
| Instructor Schedule Invite | Sent to an instructor when a class or PT session they run is scheduled, updated, or cancelled |
| Member Invitation | Sent when inviting someone to join as a member |
| Payment Link | Sent with a pay-now link for an invoice or charge |
| Waitlist Joined | Sent when a member joins a class waitlist |
| Waitlist Spot Available | Sent when a waitlist spot opens up |
| Waitlist Expired | Sent when a held waitlist spot expires |
| Review Request | Sent after attendance to request a review |
| Membership Expiration Reminder | Sent before a membership expires |
| Welcome Email | Sent once when a member first becomes active |
### System emails you cannot customize
A separate set of system emails is fixed and does not appear in the catalog, because they carry security-sensitive or platform-level content: password reset, email verification and resend verification, inbox verification, organization invitation, account confirmation, instructor invitation, invitation reminder, signup notifications, and AI report verification. These always send with their built-in wording.
## Customizing a system template
Customizing replaces the built-in email with your own version for that one template key. Under the hood, 1Club copies the system template's content blocks into an organization-owned template, then applies your edits; at send time it uses your active override instead of the built-in email.
1. On the Templates page, find the system template row and click **Customize**.
2. The editor opens pre-filled with the system template's default subject, preview text, and content blocks.
3. Edit the subject and body. Use the in-editor **Insert variable** picker - it lists exactly the variables this template supports (for example, `{{entityName}}`, `{{clubName}}`, `{{bookingDate}}` on Booking Confirmation), grouped by category.
4. Click **Save**. The row now shows both **System** and **Customized** chips.
To go back to the built-in email, open your customized template and click **Reset to Default**. This deletes the override after a confirmation prompt and cannot be undone; the built-in system email takes over again immediately.
Keep every required variable in a customized template. If you delete a token the template depends on (for example `{{bookingDate}}` on a booking confirmation), that value simply will not appear in the sent email - the token is not re-inserted for you.
### Worked example: padel booking confirmation
Rebrand the Booking Confirmation email for a padel club so it reinforces the court-etiquette rules and surfaces the door PIN:
```text theme={null}
Subject: You're on court - {{entityName}} at {{clubName}}
Hi {{contactFirstName}},
You're confirmed for {{entityName}} at {{clubName}} on {{bookingDate}},
{{bookingTime}} ({{bookingDuration}}).
Your door PIN: {{accessPin}}
Please arrive 10 minutes early and bring clean indoor shoes.
```
`{{accessPin}}` resolves to the member's access PIN, or to nothing when they do not have one - so the PIN line quietly disappears for members without door access.
### Worked example: yoga membership renewal
Customize the Membership Expiration Reminder for a yoga studio to add a warm renewal nudge:
```text theme={null}
Subject: Keep your practice going - {{planName}} renews soon
Hi {{contactFirstName}},
Your {{planName}} membership at {{clubName}} expires on {{expirationDate}}.
Renew for {{renewalPrice}} to keep your unlimited mat time.
```
## Variables
Write variables as `{{variableName}}`. They are substituted per recipient when the message is sent, using the recipient's contact record and your organization's settings. A token that is not part of the template's variable set is left in the message verbatim - it is not replaced and it does not block saving.
### Manual message templates
For `message` templates, the following variables resolve at send time. This is the set surfaced in the **Available Variables** panel and the same set expanded when a manual message goes out:
| Variable | Description |
| - | - |
| `{{contactName}}` | Recipient's full name |
| `{{contactFirstName}}` | Recipient's first name (for greetings) |
| `{{contactEmail}}` | Recipient's email |
| `{{organizationName}}` | Your organization's name |
| `{{organizationEmail}}` | Your organization's contact email |
| `{{organizationPhone}}` | Your organization's phone number |
| `{{senderName}}` | The staff member sending the message |
| `{{subject}}` | The message subject (handy inside body copy) |
| `{{messageDate}}` | The formatted send date |
### Receipt templates
`receipt` templates additionally resolve `{{receiptNumber}}`, `{{receiptDate}}`, `{{amount}}`, and `{{paymentMethod}}` alongside the contact and organization tokens above.
### System template variables
Each system template declares its own variable set - Booking Confirmation exposes booking tokens (`{{entityName}}`, `{{clubName}}`, `{{bookingDate}}`, `{{bookingTime}}`, `{{bookingDuration}}`, `{{bookingTotal}}`, `{{accessPin}}`), Membership Expiration Reminder exposes `{{planName}}`, `{{expirationDate}}`, `{{renewalPrice}}`, `{{renewUrl}}`, and so on. When you customize a system template, the **Insert variable** picker shows exactly that template's tokens, so you can only reference values 1Club can actually fill for that send. These booking and membership tokens are only meaningful on their own system template (or on a trigger-based [automation](/marketing/automations) that binds the matching record) - they do not resolve on a plain manual `message` template.
Unknown tokens are not rejected when you save through the admin editor - they are simply left un-substituted in the sent message. Only 1Club's AI assistant enforces the variable allow-list when it drafts a template for you. Always confirm your tokens against the **Available Variables** or **Insert variable** list.
## Per-channel content
The same template can be delivered over email, SMS, or WhatsApp. Because content is stored as channel-neutral blocks:
* **Email** renders the blocks through the branded transactional layout, using the **Subject** and **Preview text**.
* **SMS and WhatsApp** render a plain-text projection of the same blocks - buttons collapse to their link text, and formatting is stripped. Keep bodies short if you send over these channels.
## Tips & best practices
* **Greet with `{{contactFirstName}}`, not `{{contactName}}`.** First name reads warmer in a salutation; full name is better for formal receipts.
* **Customize the highest-volume emails first.** Booking Confirmation, Booking Reminder, and Welcome Email reach nearly every member - branding those has the most impact.
* **Preview text earns the open.** Fill **Preview text** on email templates; it is the snippet shown next to the subject in most inboxes.
* **Do not delete required tokens.** Removing a value like `{{bookingTime}}` or `{{expirationDate}}` leaves a gap in the sent email rather than an error.
* **Reset is your undo.** If a customization goes wrong, **Reset to Default** restores the built-in email cleanly - there is no need to hand-rebuild it.
* **Use automations for triggered sends.** A template is reusable content; to send it automatically on an event (booking upcoming, membership expiring, review after check-in), wire it into an [automation](/marketing/automations).
## Troubleshooting
| Symptom | Cause | Fix |
| - | - | - |
| **Add template** button is greyed out | Template customization is not in your plan, or you are on the Grow trial | Upgrade from the plan-locked banner or **Settings** > **Organization** > **Subscription** |
| A `{{token}}` shows up literally in a sent message | The token is not in that template's variable set, or it is a system token used on a plain `message` template | Replace it with a variable from the **Available Variables** / **Insert variable** list |
| Customized a system template but members still get the old wording | The override is inactive, or you edited a different template than the one that fires | Confirm the row shows the **Customized** chip; re-open and re-save |
| A value like the access PIN is blank in the email | That variable is optional and the recipient has no value for it (for example, no PIN assigned) | Expected behavior - the line renders empty; consider wording that reads fine without it |
| Booking or membership tokens do not resolve in a manual send | Those tokens only bind on their system template or a trigger-based automation | Move the send into an [automation](/marketing/automations), or use manual-message variables only |
| Cannot find a system email to customize (for example password reset) | That email is non-customizable by design | Only the catalog templates listed above can be edited; security emails are fixed |
## Related
* [Compose](/inbox/compose) - Where `message` templates are applied to outbound sends.
* [Inbox](/inbox/messages) - The conversation list these messages land in.
* [Automations](/marketing/automations) - Trigger-based sends that reuse system templates with booking and membership variables.
* [Settings overview](/settings/overview) - Where Communication settings, including Templates, live.
# Documents and waivers
Source: https://docs.1club.ai/settings/waivers
Define signable documents (liability waivers, terms, policies) under Settings > Bookings > Waivers, and use document rules to require the right members to accept them, with audience targeting and mandatory gating.
Documents and waivers are the signable agreements your members accept - liability waivers, terms of service, codes of conduct, and policies. Each document is paired with one or more **document rules** that decide who must accept it and how strictly acceptance is enforced, so the right people are asked to sign the right agreements at the right moment. Owners and admins set up documents and rules, and members accept them during signup, in the portal, or in the app.
## When to use it
Rules target each agreement at the people it applies to, and a mandatory document gates a member until they accept it. You stop chasing signatures by hand, and nobody is asked to sign something that doesn't apply to them.
Typical uses:
* **A waiver for one plan only** - a climbing gym requires a lead-climbing liability waiver, with a typed name, only from members on its **Lead Climbing** plan.
* **A minor's waiver signed by the parent** - a jiu-jitsu academy targets its youth waiver at dependents, so the parent accepts it for each child during signup.
* **A release for every drop-in** - a padel gym asks every drop-in visitor who pays per court to sign a court-use release.
See [Worked examples](#worked-examples) for the exact rule settings.
## How it works
The same document can be targeted by several rules. When rules overlap, the strictest wins: any mandatory rule makes it mandatory, and any typed-name rule makes typed name required.
A signature counts as accepted regardless of the stored document version, so cosmetic edits to a published document don't re-gate members who already signed.
## Authoring a document
Manage documents and their rules from **Settings** > **Bookings** > **Waivers** (route `/settings/operations/waivers`). The page has two sections: **Documents** (the agreements themselves) and **Document Rules** (the targeting and enforcement layer on top of them).
A document is a CMS content item with its type set to `document`, so it is written in the same block editor as your other [content](/marketing/content) (posts, FAQs, reusable blocks), with the same title, slug, body, and language-variant support.
1. On **Settings** > **Bookings** > **Waivers**, click **Add document**. This opens the content editor with the type pre-set to `document`.
2. Give it a **Title** (for example, `Liability waiver`) and write the body in the block editor.
3. Set the **status** to **Published**. Only published documents are enforced - a draft or archived document is skipped by every rule, so members are never asked to sign it.
4. Save. The document now appears as a card in the **Documents** section and becomes selectable when you create a rule.
Write one document per agreement (waiver, terms, code of conduct) so each can be targeted, versioned, and reported on independently. Use language variants on the document itself so members see the agreement in their own language when they sign.
## Creating a document rule
A rule connects a published document to an audience and sets how acceptance is enforced.
1. In the **Document Rules** section, click **Add Rule**.
2. Fill in the dialog:
* **Name** - An internal label for the rule (for example, `Adults - liability waiver`). Members never see this.
* **Document** - Pick the published document this rule enforces.
* **Contact Filters** - Narrow the audience (see below). Leave a filter empty to place no restriction on that dimension.
* **Optional** - Off by default, so documents are mandatory unless you flip this on.
* **Require review and typed name** - Force the member to scroll through the document and type their full name before accepting.
* **Active** - Whether the rule is currently in effect.
3. Click **Save**. To edit or delete a rule later, click its card in the **Document Rules** section.
## Contact filters (audience targeting)
Filters decide who a rule applies to. The four categories combine with **AND**: a contact must match every category that has values set. Within a single category, matching **any** value is enough (**OR**). A category you leave empty places no restriction, so a rule with no filters at all applies to everyone.
### Contact Types
Match on the kind of contact. The available types are **Member**, **Drop-in**, **Lead**, **Staff**, **Lapsed**, and **Contact**. Use this to ask, for example, only drop-in visitors to sign a one-time liability release, or only members to sign the full membership terms.
### Plan
Match on the plans a contact holds through an active membership. Select one or more [plans](/sales/plans); the rule then applies to anyone whose active membership is on one of those plans. During signup - before a membership exists - the rule matches against the plan the member is purchasing in the wizard.
### Tags
Match on contact tags (for example, a `competition-squad` or `under-18` tag). Use tags when your audience does not map cleanly to a contact type or plan.
Tag filters are skipped during the public signup flow, because a brand-new contact has no tags yet. A rule that relies only on a tag filter will not surface at signup - it will apply later, once the contact is tagged. For documents that must be signed at signup, target by contact type, plan, or household role instead.
### Household Role
Match on a contact's role within a [household account](/members/accounts): **Owner**, **Adult**, or **Dependent**. This is how you route a minor's waiver to the `dependent` role so it is presented to the guardian to sign on the child's behalf.
## Mandatory gating vs optional
Every rule is **mandatory** unless you switch on **Optional**.
* **Mandatory** (default) - The member cannot finish using the platform until they accept the document. Mandatory documents feed the incomplete-setup state: an authenticated member with an unsigned mandatory document is gated until it is signed.
* **Optional** - The document is offered but never blocks access. Optional documents are useful for informational consents (marketing preferences, photo release) that you want to record but not enforce.
## Typed-name acceptance
**Require review and typed name** makes acceptance deliberate: the member must scroll through the full document and type their full name before the accept button unlocks. This produces a stronger evidentiary record for waivers and legal agreements.
This option is only meaningful on mandatory documents. If a rule is optional, the toggle is disabled and the setting is forced off - review and typed name can only be required on mandatory documents. If you make a mandatory rule optional later, its typed-name requirement is cleared automatically.
## Worked examples
### Climbing gym - lead-climbing waiver tied to one plan
A climbing gym requires a belay-and-lead-climbing liability waiver only from members on its **Lead Climbing** plan; recreational top-rope members do not need it.
1. Author a published document `Lead climbing liability waiver`.
2. Create a rule named `Lead climbers - liability waiver`:
* **Document**: `Lead climbing liability waiver`
* **Plan**: `Lead Climbing`
* **Optional**: off (mandatory)
* **Require review and typed name**: on
3. Now only members whose active membership is on the Lead Climbing plan are gated until they scroll through and type their name to accept.
### Martial arts - a minor's waiver signed by the guardian
A jiu-jitsu academy enrolls children through a household account, and the parent must accept the injury-risk waiver for each child.
1. Author a published document `Youth participation and injury-risk waiver`.
2. Create a rule named `Dependents - youth waiver`:
* **Document**: `Youth participation and injury-risk waiver`
* **Household Role**: `Dependent`
* **Optional**: off (mandatory)
3. During signup, the guardian completing the wizard is shown the dependent's waiver and accepts it on the child's behalf. The resulting signature records the child as the subject and the guardian as the signer. See [Document signatures](/members/document-signatures) for how proxy signing is recorded.
### Racket sports - one-time release for drop-in visitors
A padel club lets non-members pay per court but wants a signed release from every drop-in.
1. Author a published document `Drop-in court-use release`.
2. Create a rule named `Drop-ins - court release`:
* **Document**: `Drop-in court-use release`
* **Contact Types**: `Drop-in`
* **Optional**: off (mandatory)
### Whole-club code of conduct
To require every contact to accept a code of conduct, create a rule with **no contact filters** and leave it mandatory. An empty filter set means the rule applies to everyone.
## Tips and best practices
* **Publish before you target.** A rule pointing at a draft document silently enforces nothing. Confirm the document status is **Published**.
* **Name rules for their audience, not the document.** `Dependents - youth waiver` tells you at a glance who it hits; `Waiver rule 2` does not.
* **Prefer contact type, plan, or household role for signup-time documents.** Tag filters do not fire during public signup.
* **Keep documents single-purpose.** One agreement per document makes targeting, typed-name requirements, and the signatures ledger clean.
* **Use language variants** on the document so members sign in their own language; the signing flow resolves the variant that matches the member's locale.
* **Deactivate instead of delete** when you want to pause a rule but keep its history - toggle **Active** off rather than removing it.
## Troubleshooting
| Symptom | Likely cause | Fix |
| - | - | - |
| A member is never asked to sign a document | The document is not **Published**, or the rule is inactive | Publish the document; toggle the rule **Active** on |
| A rule does not fire at signup even though the audience seems right | The rule relies on a **tag** filter, which is skipped during public signup | Re-target by contact type, plan, or household role |
| The **Require review and typed name** toggle is greyed out | The rule is **Optional** - typed name is only allowed on mandatory rules | Turn **Optional** off to make the rule mandatory |
| Too many (or too few) members are gated | Filters combine with AND across categories | Widen by clearing a filter category, or narrow by adding values; remember an empty category means "no restriction" |
| A previously signed member is prompted again | Expected only when a new signature is genuinely required; cosmetic edits do not re-gate | Confirm the member has a signature on file in [Document signatures](/members/document-signatures) |
| A dependent's waiver is not offered during signup | The rule's **Household Role** does not include `Dependent`, or the document is unpublished | Add `Dependent` to the rule and publish the document |
## Related
* [Document signatures](/members/document-signatures) - The ledger of who signed what, when, and how, including guardian signing for dependents.
* [Content](/marketing/content) - The block editor and content types used to author document bodies and language variants.
* [Accounts](/members/accounts) - Household accounts and the owner/adult/dependent roles that household-role targeting uses.
* [Plans](/sales/plans) - The plans that plan-based targeting matches against.
* [Settings overview](/settings/overview) - Where Waivers lives among the other settings.
# Instructors
Source: https://docs.1club.ai/staff/instructors
Manage your coaching staff, their bookable rates, sports, types, and operating hours.
Instructors are the coaches, trainers, and class leads who teach classes or take one-on-one bookings. Owners and managers set up their profiles, rates, and availability, members book bookable instructors directly, and instructors who sign in see their own timetable and earnings.
## When to use it
An instructor profile decides whether members can book that person directly, when they are available, and how they are paid. The same profile fits a coach who sells private lessons, a guest who only leads classes, and part-time staff whose hours change week to week.
Typical uses:
* **A padel coach who takes private lessons** - members book one-on-one lessons from the portal at the coach's hourly rate, only within their working hours.
* **A class-only martial-arts lead** - a visiting black belt leads the Saturday BJJ class without appearing in the booking selector or as a bookable column on the schedule.
* **A Pilates studio on rotating shifts** - part-time instructors follow a weekly rotation that sets their bookable availability, with one-off swaps edited on the roster.
* **Pay only for paid drop-ins** - coaches earn on memberships, fully-paid bookings, and paid-and-checked-in external-program visits, never on no-shows or unpaid seats.
See [Worked examples](#worked-examples) for how to set up each of these.
## The instructor list
Instructors live under **Staff** > **Instructors**. The [calendar](/schedule/calendar) and the booking dialog both surface them.
The list shows one row per instructor with a drag handle and these columns: **Instructor** (name and avatar), **Display name**, **Type**, **Status**, **System status**, **Sports** (up to three with a `+N` overflow), **Club**, and **Rate** (the hourly rate).
Drag a row to reorder; the new order is what shows on the schedule and in selectors. The drag handle is disabled while a reorder request is in flight. If the server rejects the reorder, the list snaps back to the server order with a toast.
Click a row to open the instructor profile, or **Add Instructor** to create one. The profile has tabs including **Pay rates**, **Time off**, and **Roster**, so you can manage one instructor's pay, absences, and shifts in one place.
## Creating or editing an instructor
The user dialog handles both inviting a new instructor (which creates the contact, user, and instructor profile in one shot) and editing an existing one. The instructor section asks for:
* **Instructor type** - managed under **Settings** > **Clubs** > **Instructor Types**. Picking a type can default the max concurrent bookings.
* **Hourly rate** - in your organization's currency. Used when a booking is created against this instructor; duration multiplied by this rate seeds the line total.
* **Sports** - tag-style list. Used by the [public marketplace](https://1club.me) for filtering and on the list row.
* **Experience** and **Certifications** - free-text fields shown on the profile.
* **Bookable** toggle - when on, the instructor appears in the booking dialog's instructor selector and on the schedule as a bookable column. When off, the instructor can still be assigned to classes but cannot be booked directly.
* **Max concurrent bookings** - only visible when bookable is on. How many overlapping one-on-one bookings the instructor accepts.
* **Active instructor** toggle - inactive instructors are hidden from selectors but kept for historical bookings.
* **Operating hours** - per-day working hours used by the booking dialog's availability checks.
* **Give instructor access** - when on, the user is invited with the instructor role and can sign in and see their own classes and earnings on [Home](/home/overview).
Per-instructor display colors used on the schedule are configured separately under **Settings** > **Bookings** > **Schedule Display**, not on the instructor dialog itself.
## How instructors show up on the schedule
The [Schedule](/schedule/calendar) page reads bookable instructors and renders one column per instructor in day view. A class assigned to an instructor produces a "teaching" mirror event on the instructor's column, so the instructor reads as busy during their classes (in addition to the class itself appearing under its class-type column).
Instructor time off (added from the calendar with **Add** > **Time off**, or under **Staff** > **Time off**) renders as grey blocks on the instructor's column. When schedule **staff coloring** is enabled, blocks use the assigner's profile color instead of the resource color. The toggles are under **Settings** > **Bookings** > **Schedule Display**.
## Shifts and time off
For staff whose hours change week to week, drive an instructor's availability from a **shift roster** instead of fixed operating hours: build reusable shifts and weekly rotations under **Staff** > **Roster** and assign instructors to them. Record absences under **Staff** > **Time off**. See [Roster and time off](/staff/roster).
## Rescheduling on the calendar
On the **Schedule** calendar you can drag a booking or class to a new time. The move keeps the same resource (area, instructor, or class type) and the same duration - only the start and end time shift. Every drop asks you to confirm the old and new time, and for recurring events you choose whether the change applies to just that occurrence or to all future ones.
## Pay rates and earnings
What an instructor earns is set by pay rate policies under **Staff** > **Pay rates**, not by the **Hourly rate** (which is what members pay). Policies can pay per class, per attendee, per session, or a share of revenue, and can hold pay back until the booking is paid. Instructors who sign in see the result on their own **Earnings** page. See [Pay rates](/staff/pay-rates).
## Mixed-role accounts
A user who is both an instructor and an admin or manager has access to two schedule views. The org-wide schedule lives under the **Schedule** menu (see [Schedule](/schedule/calendar)). A personal "My teaching schedule" view, scoped to just their own classes and bookings, is reached through the instructor menu in the sidebar.
## Worked examples
### Padel coach who takes private lessons
Create the coach, set their **Instructor type** to *Coach*, add padel and tennis to **Sports**, set the **Hourly rate**, turn on **Bookable**, and set **Max concurrent bookings** to 1 for one-on-one lessons. Their operating hours (or shift roster) then gate when members can book them from the portal.
### Class-only martial-arts lead
A visiting black belt leads the Saturday BJJ class but never takes private bookings. Add them, assign them to the class, and leave **Bookable** off. They appear on classes without cluttering the booking selector or the schedule with a bookable column.
### Pilates studio on rotating shifts
A studio with part-time instructors puts them on rotating shifts: it builds a "Morning 06:00-14:00" shift with a lunch break and assigns each instructor to a weekly rotation. The generated roster becomes their bookable availability; a one-off swap is edited directly on the roster and sticks as a manual override.
### Pay only for paid drop-ins
A gym that runs paid drop-in classes sets a pay rate policy with **Compensate for unpaid bookings** left off, so coaches earn on memberships, fully-paid bookings, and paid-and-checked-in external-program visits - never on no-shows or unpaid seats.
## Tips & best practices
* **Turn Bookable off for guest and class-only leads.** It keeps the booking selector and schedule focused on people members can actually book.
* **Use a shift roster when hours vary week to week.** Fixed operating hours are simpler for steady schedules; rosters suit part-time and rotating staff.
* **Gate pay on payment to avoid overpaying.** The paid-booking toggle stops payouts on unpaid or no-show visits, and each recurring occurrence is gated on its own charge.
* **Set sports accurately.** They drive marketplace filtering, so a member searching for a padel coach only sees the right people.
* **Deactivate, don't delete, departing staff.** Inactive instructors drop out of selectors but keep their history on past bookings.
## Troubleshooting
**Issue**: An instructor isn't in the booking dialog's selector.
**Solution**: Only **bookable**, **active** instructors appear. Check the **Bookable** and **Active instructor** toggles on their profile.
**Issue**: An instructor wasn't paid for a booking they delivered.
**Solution**: Their pay rate policy likely has **Compensate for unpaid bookings** off, and the visit is unpaid or a no-show. Pay is recorded once the charge settles; for recurring bookings, each occurrence pays out only when it's charged.
**Issue**: The instructor can't sign in to their dashboard.
**Solution**: Access is only granted when **Give instructor access** was on at invite. Edit the instructor and enable it to send the invitation.
**Issue**: A bookable instructor shows no availability.
**Solution**: If the instructor is rostered for that date, availability comes from their **roster**, not their operating hours - and a rostered day with no shift is a day off. Assign them to a rotation or add roster shifts under **Staff** > **Roster** (see [Roster and time off](/staff/roster)).
## Related
* [Schedule](/schedule/calendar) - Where instructor columns and time off live.
* [Classes](/schedule/classes) - Instructors are assigned to classes here.
* [Bookings](/schedule/bookings) - One-on-one bookings against instructors.
* [Roster and time off](/staff/roster) - Shifts, rotations, and absences that set availability.
* [Pay rates](/staff/pay-rates) - What instructors earn.
* [Roles and permissions](/reference/roles-permissions) - What the instructor role can and can't do.
# Staff
Source: https://docs.1club.ai/staff/overview
The Staff section: your team's admin logins, instructors, pay rate policies, time off, and the shift roster.
The **Staff** section in the left rail is where you manage the people who work at your gym: who can sign into the admin, who teaches and can be booked, how coaches are paid, and when they're available. Owners, admins, and managers use it; it needs the **staff.read.organization** permission.
## What's in the section
| Tab | What it's for |
| - | - |
| [Team](#team) | Admin logins for non-teaching staff: admins, managers, and front desk. |
| [Instructors](/staff/instructors) | Coaches and class leads, their bookable rates, sports, and operating hours. |
| [Pay rates](/staff/pay-rates) | Pay rate policies that decide what coaches earn for sessions and classes. |
| [Time off](/staff/roster#time-off) | Instructor absences, with the bookings and classes they clash with. |
| [Roster](/staff/roster) | Shifts, weekly rotations, and the scheduled shifts that set instructor availability. |
Opening **Staff** lands on **Team**.
## Team
**Team** lists everyone with an admin login who isn't an instructor - people with the **Admin**, **Manager**, or **Front desk** role. Instructors are listed under [Instructors](/staff/instructors) instead. For the full list of admin users, including instructors, use **Settings** > **Organization** > **Users** (see [Users](/settings/admin-users)).
The columns are **Contact** (name, email, and avatar), **Phone**, **Role** (with an **Owner** chip for the account owner), **Status**, **Sign-in methods**, and **Joined**.
### Add a staff member
Click **Add staff member** to open the user dialog. Pick an existing contact or add a new one, choose a **Role**, and save to create the user and send the invitation. The roles you can assign depend on your own: admins and owners can assign any of the three, while managers can assign up to **Manager**. The dialog is the same one used under **Settings** > **Organization** > **Users**; see [Users](/settings/admin-users) for every field and what each role can do.
### Manage a staff member
Click a row, or use its actions menu:
* **Edit** - change the role and the other details in the user dialog.
* **Delete** - remove the user's access, or delete a pending invitation.
* **Send Invitation** or **Resend** - for someone who hasn't accepted yet.
* **Reset password** - for an active user.
You can't edit or remove yourself here, and you can only act on people whose role you're allowed to manage. Managers, for example, can't change an admin.
## Related
* [Users](/settings/admin-users) - Roles, invitations, and account ownership in detail.
* [Roles and permissions](/reference/roles-permissions) - What each role can do.
* [Schedule](/schedule/overview) - Where instructors, time off, and shifts show up.
# Pay rates
Source: https://docs.1club.ai/staff/pay-rates
Set what instructors earn for classes and one-on-one sessions with pay rate policies: flat fees, per-person fees, or a share of revenue, scoped by class type, entrance method, and instructor.
A pay rate policy is the rule for what a coach earns for a unit of work - a class, a head in a class, a one-on-one session, or a percentage of what it brought in. Owners and managers set policies under **Staff** > **Pay rates**, and instructors see the result on their own **Earnings** page.
A pay rate is not the instructor's **Hourly rate**. The hourly rate on the instructor profile is what a member pays to book that coach; a pay rate is what your gym pays the coach.
## When to use it
Pay rate policies replace the spreadsheet you'd otherwise keep to work out coach pay at the end of the month. Each class taught and each session delivered is costed as it happens, so earnings are always current.
Typical uses:
* **A flat fee per class plus a bonus per attendee** - group-class leads earn a base per class and a little more for every person who turns up.
* **A revenue share for private coaches** - padel coaches keep a percentage of what each one-on-one lesson earns.
* **A different rate for one coach** - your head coach is on a higher rate than the gym default, without changing anyone else's.
* **Pay only for paid visits** - coaches earn on memberships, fully-paid bookings, and paid external-program visits, never on no-shows or unpaid seats.
## How it works
Each policy has a type, earnings, and three filters that decide which work it covers. An empty filter means "everything":
* **Instructors** - no instructor named makes it the organization's default policy, for every coach without a policy of their own.
* **Class types** - class policies only. Empty covers all classes.
* **Entrance methods** - how the attendee got in: direct payment, a membership (any, or a specific plan), or an external program (any, or a specific one). Empty covers all entrance methods.
When several policies match a booking, the most specific one wins, and a policy that names the instructor always beats the default. Two policies can't cover the same instructor for the same work at the same level of detail: saving one that overlaps shows an error such as "Alex is already covered by 'Standard classes'". A broad policy can still sit under a narrower one, for example an all-classes default with a separate rate for one class type.
Changing a policy recalculates earnings from today onward, including classes already under way today. Past days stay as they were.
## The Pay rates page
**Staff** > **Pay rates** lists your policies with **Name**, **Compensation**, **Type** (**Individual training**, **All classes**, or the class types it covers), **Entrance methods**, and **Applies to** (the instructors, or **All instructors**). The most specific policies are listed first. Use the row actions to edit or delete a policy.
You can also manage policies per instructor: an instructor's details page has a **Pay rates** tab, and **Add** > **Pay rate** there lets you attach an existing policy or create a new one for that instructor.
## Create a pay rate policy
Click **Add pay rate**, then:
1. Choose the type: **Class** for group classes, or **Individual training** for one-on-one and instructor bookings. You can't change the type once the policy is saved.
2. Enter a **Policy name**.
3. Fill in the **Earnings**. At least one amount must be greater than zero, and you can combine them.
* For **Class**: **Per class** (a flat fee for teaching the class), **Per person** (for each attendee), and **% of total class revenue**.
* For **Individual training**: **Per session**, **Per extra person** (added for each person beyond the first when a session with no court is booked for a group), and **% of total session revenue**.
4. Decide on **Compensate for unpaid bookings** (see below). It's off for new policies.
5. Under **Applies to**, narrow the policy by class types (class policies only), **Instructors**, and entrance methods, or leave them empty to cover everything.
6. Click **Add**.
## Paid-booking gating
**Compensate for unpaid bookings** controls whether payment status matters:
* **Off** (the default) - the instructor earns only on paid visits: memberships, fully-paid bookings, and external-program visits that were both checked in and paid. Unpaid bookings and no-shows earn nothing.
* **On** - the instructor is paid for every booking, including unpaid ones and no-shows.
With the toggle off:
* Pay for a booking or check-in is held back until the underlying charge is settled, then recorded.
* For recurring bookings, each occurrence is gated on its own payment - future unpaid occurrences don't pay out until they're charged and paid.
* If a class is deleted, the related pay records are cleared.
## Instructor earnings
Instructors who sign in to their own dashboard get a personal **Earnings** page. The top of the page shows four summary cards for the selected timeframe: **Total earnings**, **Total classes**, **Attendance**, and **Personal training sessions**.
Below that, a daily earnings bar chart lets the instructor drag-select a date range, or switch between **Last 7 days**, **30 days**, **90 days**, or a custom range. The summary cards and breakdown update with the selection.
The breakdown table lists each class or booking with name, type (class or standalone booking), count of attendees or sessions, and the compensation earned.
## Troubleshooting
**Issue**: Saving a policy fails with "... is already covered by ...".
**Solution**: Another policy already covers that instructor for the same class types and entrance methods. Narrow one of the two (for example, give it specific class types), or remove the instructor from one of them.
**Issue**: An instructor wasn't paid for a booking they delivered.
**Solution**: Their policy has **Compensate for unpaid bookings** off, and the visit is unpaid or a no-show. Pay is recorded once the charge settles; for recurring bookings, each occurrence pays out only when it's charged.
**Issue**: An instructor earns the default rate instead of their own.
**Solution**: Check their policy's class types and entrance methods. If the booking falls outside them, the default policy is the most specific match left.
## Related
* [Instructors](/staff/instructors) - The instructor profile, including the hourly rate members pay.
* [Class types](/settings/class-types) - The class types a class policy can target.
* [External programs](/settings/external-programs) - Third-party entrance methods a policy can be scoped to.
# Roster and time off
Source: https://docs.1club.ai/staff/roster
Set instructor availability with reusable shifts and weekly rotations on the Staff > Roster page, and record absences under Staff > Time off.
The **Roster** and **Time off** tabs of the **Staff** section decide when an instructor is available. The roster sets regular working hours from reusable shifts and weekly rotations; time off records absences. Owners and managers maintain both, and members can only book an instructor inside the hours they leave open.
## When to use it
Fixed operating hours on the instructor profile suit coaches with a steady week. When hours change week to week, a roster saves you editing each profile, and time off blocks bookings for a holiday or sick day without touching the regular pattern.
Typical uses:
* **A Pilates studio on rotating shifts** - part-time instructors alternate mornings and evenings on a two-week rotation, and their bookable availability follows it.
* **A lunch break nobody can book over** - a "Morning 06:00-14:00" shift carries a 12:00-13:00 break, so members can't book the coach over lunch.
* **A coach's holiday** - you add a week of time off, and the dialog lists the lessons and classes it clashes with so you can reassign or cancel them in one go.
## How it works
There's no global on/off switch. An instructor uses their roster on any date they're rostered - a date covered by a rotation assignment, or one with a shift placed directly on it - and falls back to their [operating hours](/staff/instructors#creating-or-editing-an-instructor) on every other date. A rostered day with no shift reads as a day off.
Time off sits on top of either: during a time-off entry the instructor can't be booked, and the entry shows as a grey block on their column on the [calendar](/schedule/calendar).
## The Roster page
Open **Staff** > **Roster**. The page has two parts: the **Scheduled shifts** grid at the top (one row per instructor, one column per day, with week navigation) and the **Shifts & rotations** panel below it, where you build the reusable pieces. Viewing needs **staff.read.organization**; creating and editing shifts, rotations, and assignments needs **staff.manage.organization**.
## The building blocks
* **Shifts** - reusable named time windows like "Morning 06:00-14:00", each with a color and optional standard **breaks** (e.g. lunch). Managed in the **Shifts** card.
* **Rotations** - a weekly pattern that picks a shift for each day, repeating over a cycle of 1-12 weeks. Managed in the **Rotations** card.
* **Assignments** - attach an instructor to a rotation for a date range; this is what generates their roster.
* **Roster** - the generated list of individual shifts, shown in the Scheduled shifts grid and editable shift by shift. An assignment with an **Effective until** date generates the whole roster up to that date; an **Ongoing** one uses a rolling 8-week window. See [Repeating schedules](/reference/repeating-schedules).
## Set up a rotating schedule
In the **Shifts** card, choose **Add shift**. Give it a name, a start and
end time, and a color, then add any standard **breaks** (name, start, end) -
for example a 12:00-13:00 lunch. Breaks must fall inside the shift window.
Repeat for each distinct pattern you run (e.g. "Morning", "Evening").
In the **Rotations** card, choose **Add rotation**. Set a **Cycle length**
(1-12 weeks) and a **Cycle start date** (the date week 1 begins). Then, for
each day of each week, pick one of your shifts or leave it **Off**. A
two-week cycle that alternates mornings and evenings, for instance, captures
a typical part-time pattern.
In the **Scheduled shifts** grid, on the instructor's row, click their
rotation cell (**Set regular shifts**). Pick the rotation, the **Starting
week** (which week of the cycle they begin on), and the **Effective from** /
**Effective until** dates - leave the end open for an **Ongoing**
assignment. Their roster generates immediately: to **Effective until** when
you set one, so a coach contracted through June has their whole roster on the
grid, or a rolling 8 weeks when the assignment is ongoing.
The grid now shows each instructor's generated shifts with their breaks.
Click any day cell to add a one-off shift or edit an existing one. Editing a
generated shift marks it as a **manual override**, so rotation regeneration
leaves it untouched; use **Cancel shift** to drop a single occurrence.
An assignment with an **Effective until** date generates its roster all the way
to that date. An **Ongoing** assignment is generated for a rolling 8-week
window and extended automatically. Either way, manual overrides and one-off
shifts survive regeneration; unedited rotation shifts are replaced when you
change the rotation.
## Breaks
Breaks are the unavailable windows inside a working shift - most commonly lunch. Define them on a shift (in the **Shifts** card) as a name plus a start and end time within the shift window.
Once a shift is on the roster, its breaks:
* **Block bookings** during their window, so nobody can book the instructor over lunch.
* **Show on the roster grid** under each shift, as a `Break 12:00-13:00` line.
* **Show on the Schedule calendar** as a muted, non-interactive card in the instructor's column, labeled with the break name.
* **Are editable per shift** - open any shift from the roster grid to add, edit, or remove its breaks. One-off shifts can carry their own breaks too.
## Working hours on the instructor profile
On an instructor's details page, a read-only **Working hours** block shows the hours derived from their roster, with a **Manage shifts** link back to **Staff** > **Roster**.
## Time off
**Staff** > **Time off** lists every instructor time-off entry with **Instructor**, **Start time**, **End time**, **Status** (upcoming, active, or past), and **Note**, newest first. You can also add time off from the calendar (**Add** > **Time off**) and from an instructor's details page.
To add an entry, click **Add time off** and fill in:
* **Instructor** - who is away.
* **Start time** and **End time** - the window. For a new entry it defaults to tomorrow's opening hours.
* **Note** - optional, for example "Vacation".
As soon as the instructor and window are set, the dialog checks for bookings and classes that overlap it and lists them under **Resolve scheduling conflicts**. For each one, pick an action, or use **Apply to all**:
* **Keep as is** - leave it with this instructor.
* **Reassign instructor** - pick a replacement in **Reassign to**.
* **Leave unassigned** - remove the instructor and keep the booking or class.
* **Cancel booking** or **Cancel class** - cancel it. Cancelling a booking waives the member's cancellation charge unless you turn on **Keep the cancellation charge**; cancelled classes follow their own cancellation flow.
Click a row to edit an entry, or delete it from the row actions. Managing time off needs the **staff.manage.organization** permission.
## Related
* [Instructors](/staff/instructors) - Operating hours, the bookable flag, and the instructor profile.
* [Calendar](/schedule/calendar) - Where shifts, breaks, and time off show on instructor columns.
* [Repeating schedules](/reference/repeating-schedules) - How rotation assignments generate the roster.