Memberships
A membership is a pass that charges every month and gives its holder a discount. Create one with
POST /passes and billing: "monthly":
- the price is the monthly fee, in the shop's currency;
discount_percent(1 to 100) comes off every line the membership covers, for everyone on the booking;- it has no visits and no dates: it works while it is paid.
The first month is paid when the membership is bought (on the booking page, in POS, or on a
booking sent to POST /bookings with passes). The card is kept
and Stripe charges it on the same day each month. Members keep the price and discount they signed
up at, even if the pass changes later.
A monthly fee is a Stripe subscription on the shop's own Stripe account. A shop that does not take
card payments through Stripe, or whose Stripe account cannot take charges yet, gets
422 memberships_need_stripe when it creates a monthly pass or switches a pass to monthly.
A membership a customer holdsโ
A membership shows up in GET /customer-passes like any other pass, with
billing: "monthly" and these fields filled in:
{
"id": 57,
"pass_id": 9,
"customer_id": 208,
"holder_name": "Jane Doe",
"code": "MEMBER-4HQZ-8KDW",
"name": "Members Club",
"status": "active",
"billing": "monthly",
"discount_percent": 15.0,
"paid_through": "2026-08-09",
"renews_on": "2026-08-10",
"cancel_at": null,
"ended_at": null,
"status_label": "Active - renews Aug 10, 2026 - $20.00 a month",
"price": 20.0,
"currency": "USD"
}
| Field | Description |
|---|---|
paid_through | The last day of the month already paid for, in the shop's time zone |
renews_on | The day the next monthly charge is taken. null once it is cancelled or has ended |
cancel_at | Set when it was cancelled to run to the end of the paid month: the last day it works |
ended_at | When it stopped for good |
status_label | The same line the shop sees on the membership's page |
status | Meaning |
|---|---|
pending | Bought, first month not paid yet |
active | Paid up. The discount works |
payment_due | A monthly charge failed. The discount keeps working to paid_through, then stops until it is paid. Stripe retries for about a week |
ended | Cancelled and past its paid month, or Stripe gave up on the payment. The discount no longer works |
Cancel a membershipโ
POST /v1/customer-passes/{id}/cancel
Scope store:write.
| Field | Type | Required | Description |
|---|---|---|---|
now | boolean | no | Default false: it keeps working to the end of the month already paid for, then stops. true ends it today |
Either way it is not charged again. Returns 200 with the membership. A membership that is already
cancelled or has ended returns 422 membership_not_cancelled; a season pass returns 404.
List the monthly chargesโ
GET /v1/customer-passes/{id}/payments
Scope store:read. Every monthly charge on a membership, newest first.
{
"data": [
{
"id": 311,
"customer_pass_id": 57,
"status": "paid",
"amount": 20.0,
"processing_fee": 0.0,
"refunded_amount": 0.0,
"stripe_fee": 0.88,
"stripe_fee_tax": null,
"platform_fee": 0.20,
"payout_id": "po_1Nexample",
"net_amount": 20.0,
"currency": "usd",
"period_start": "2026-07-10",
"period_end": "2026-08-09",
"receipt_url": "https://invoice.stripe.com/i/acct_example/test_example",
"paid_at": "2026-07-10T16:00:04+00:00",
"created_at": "2026-07-10T16:00:04+00:00"
}
]
}
status is paid, failed or refunded. net_amount is what the shop kept: the amount less
anything refunded, and 0 for a failed charge. processing_fee is the card fee inside amount when
the shop passes processing fees on to its customers, and 0 otherwise.
stripe_fee is the fee Stripe took on the charge, stripe_fee_tax the tax Stripe charged on that
fee (where it charges one), platform_fee the EquipDash fee, and payout_id the Stripe payout the
charge went out in. All four are null until Stripe has settled the charge. The
membership.renewed and membership.payment_failed webhooks send the same fields.
Put the member discount on a bookingโ
POST /v1/bookings/{id}/membership
Scope bookings:write. The same check the booking page and POS use.
| Field | Type | Required | Description |
|---|---|---|---|
code | string | yes | The membership code |
The discount comes off every line the membership covers. A membership and a promo code do not
combine: if the booking already has a promo code that takes more off, the promo code stays and
the call returns 422; if the membership takes more off, it replaces the promo code.
{
"data": {
"discount": 12.0,
"discount_percent": 15.0,
"replaced_promo_code": false,
"booking_total": 68.0,
"membership": { "id": 57, "code": "MEMBER-4HQZ-8KDW", "status": "active" }
}
}
Returns 201. When the code does not work the call returns 422 membership_not_applied and
error.message says why, for example "This membership has a payment due." Whether a membership
works is judged on the day the booking is made, so a paid-up member can book months ahead.
Take the member discount offโ
DELETE /v1/bookings/{id}/membership
Scope bookings:write. Returns 204, or 404 when the booking has no member discount.
Webhooksโ
| Event | When | data |
|---|---|---|
membership.renewed | A monthly charge is paid (a new month, or a failed charge paid later) | The charge, as in the list above, with its membership |
membership.payment_failed | A monthly charge fails | The charge, with its membership |
membership.cancelled | A membership is cancelled (cancel_at set, still working to the end of the paid month), and again when it stops (status: "ended") | The membership |
See Webhook events.