System & settings
API & webhooks
Read your club’s data over a REST API, receive signed webhook notifications (payments, members, check-ins, and content changes such as news, events, fixtures and pages, with timestamped v2 signatures), and connect an AI assistant over MCP — with a token that can be limited to reading, to making changes, or to publishing, and narrowed to particular areas of the club. On every plan, a public read API with no key returns your published news, events, fixtures, sponsors, teams, pages and club details for other websites and apps.
API & webhooks lets your club's data flow into your own tools. Create a read-only API key to pull members, membership types, events, orders and check-ins into a spreadsheet, reporting dashboard or custom app — and add webhook endpoints so your systems hear about new members, paid orders and check-ins the moment they happen. You can also connect an AI assistant (like Claude) so it can answer questions about your club and prepare drafts for you to review. You'll find it all in the admin sidebar under System → API & webhooks.
Who can use this
You need to be a full admin for the organisation — moderators can't see this page, and it can't be granted through a council position or permission override. API keys give tenant-wide read access and webhook endpoints receive member data, so both stay with admins only.
The REST API and webhooks are a Pro plan feature (Enterprise included); on other plans that part of the page shows an upgrade prompt. AI assistant access (MCP) is different — it works on every plan, including Free, because an assistant acting as one admin is not the same thing as a bulk data tap.
What's on the page
- API keys — create named keys, see when each was created and last used, and revoke any key instantly.
- AI assistant access (MCP) — create tokens that let an AI assistant connect to your organisation over the Model Context Protocol. Choose whether it may read only, make changes, or publish; limit it to particular parts of the club; set an expiry; revoke it in one click.
- Webhook endpoints — add HTTPS URLs we should notify, choose which events each receives, pause/resume, send a test event, or delete an endpoint. Each endpoint has its own signing secret, shown on the page.
- Recent deliveries — the last 20 webhook deliveries with their event, status, attempt count and time queued.
Step by step
Create an API key and make your first request
- Under API keys, give the key a name that says where it will be used (e.g. Reporting spreadsheet) and click Create key.
- Copy the full key from the green banner straight away — for security we only store a fingerprint, so the key is shown this once and never again.
- Call any endpoint with the key in the
Authorizationheader:
curl -H "Authorization: Bearer chk_..." https://clubhelix.com/api/v1/members
- Available endpoints (all GET, JSON, paginated with
?limit=up to 100 and?offset=):/api/v1/members— your roster (status, joined date, paid-until, name and email)/api/v1/membership-types— your active membership types/api/v1/events— published events (add?upcoming=truefor future ones only)/api/v1/orders— membership, shop and ticket orders summarised, newest first/api/v1/checkins— attendance records
Use the public read API (no key)
Everything already public on your website can also be read as JSON, with no key and on every plan. Use it to show your latest news or upcoming events on another site, a noticeboard screen or an app.
curl https://clubhelix.com/api/public/v1/your-club/events
Replace your-club with your organisation's address (the part after /group/). If you have a custom domain, the same path works there too, for your own organisation only.
news— published news, newest firstevents— public events, upcoming by default; add?from=2026-10-01&to=2026-12-31for a date rangefixtures— public fixtures; add?timeframe=resultsfor recent resultssponsors,teams— as shown on your sitepages— a list of your published public pages (titles and addresses only)club— your name, logo, social links, theme colours andlocale, the language your site is written in (such asen-AUores)
Add ?limit= for up to 50 items (the default is 10; fixtures stop at 20 and teams at 24). News and events also take ?category= and ?tag= with a category or tag slug. Every answer looks like { "data": ..., "truncated": true }, where truncated says there were more items than you asked for. Times are in UTC.
Nothing private is ever included: members-only events and pages, drafts, and contact details stay out, exactly as they do for a visitor who isn't signed in.
When something goes wrong, both APIs answer with { "error": { "code": ..., "message": ... } }. Check the code: it never changes. The message is written for a person, and it follows the request's Accept-Language header once your language is available.
Add a webhook endpoint
- Under Webhook endpoints, enter the HTTPS URL your system listens on. It has to be a public address on the internet that already resolves: we won't save, or deliver to, an internal or private address, a URL with a username or password in it, or a port other than 443.
- Tick the events you want delivered — or leave every box unticked to receive all of them.
- Choose a signature format. Leave it on v2 unless your receiver was written for the older v1 format (see below).
- Click Add endpoint. Note the signing secret shown on the endpoint's card — your receiver uses it to verify deliveries.
- Click Send test event to queue a
test.pingdelivery, then watch it arrive in Recent deliveries (deliveries go out every few minutes; failures retry up to 5 times).
Verify a webhook signature
Every delivery is a JSON POST with an X-ClubHelix-Event header (the event type) and an X-ClubHelix-Signature header. Each endpoint card shows which signature format it uses.
v2 (the default for new endpoints). The signature header looks like t=1759449600,v2=5f2c…: a Unix timestamp and a hex HMAC-SHA256 of the timestamp, a full stop, the delivery id from the X-ClubHelix-Delivery header, another full stop and the raw body, keyed with your endpoint's secret. The delivery id is part of what is signed, so nobody can resend a delivery they captured under a new id to get past your duplicate check. Verify like this:
t, v2 = parse(header["X-ClubHelix-Signature"])
delivery_id = header["X-ClubHelix-Delivery"]
if abs(now_unix() - t) > 300:
reject the request # older than five minutes: a replay
expected = hmac_sha256_hex(secret, t + "." + delivery_id + "." + raw_request_body)
if not constant_time_equals(expected, v2):
reject the request
if already_seen(delivery_id):
ignore it # a retry of one you already handled
v1 (endpoints added before v2). The header is a hex HMAC-SHA256 of the raw body alone:
expected = hmac_sha256_hex(secret, raw_request_body)
if not constant_time_equals(expected, header["X-ClubHelix-Signature"]):
reject the request
A v1 signature has no timestamp, so anyone who captures one delivery can send it to you again later. Existing endpoints stay on v1 so their receivers keep working. To move one to v2, add a new endpoint, update your receiver, then delete the old one.
Always compute the HMAC over the raw bytes of the body, before any JSON parsing.
Revoke or rotate a key
- Click Revoke next to the key — it stops working immediately.
- To rotate, create a new key first, switch your integration over, then revoke the old one.
Connect an AI assistant (MCP)
- Under AI assistant access (MCP), name the token after where it will live (e.g. Claude on the secretary's laptop) and pick an expiry — 90 days is a good default.
- Choose what it may do. Both boxes off is a read-only assistant that answers questions. Allow changes lets it create and edit pages, news and events. Allow publishing additionally lets it make that work public. Most clubs start with changes on and publishing off, so a person still presses publish.
- Optionally open Limit what this token can touch and tick a few areas. Tick nothing and the token can do everything you can. Tick "Publish news" alone and it can write your news page and nothing else — not the shop, not your members.
- Copy the token from the green banner straight away — like API keys, it is shown once and never again.
- In the assistant, add the connection using the command shown under Connect Claude Code on the page (the token travels in an
Authorizationheader — never paste it into a chat message). See Connecting an AI assistant for the setup in other tools, and for the one rule that says in advance whether a given tool can connect at all. - Try it out: ask the assistant to "summarise our membership", then "draft a news post about Saturday's game". Ask it "what can you do here?" and it will tell you exactly which powers this token carries.
What the token can never do, by design:
- It only sees your organisation. The token is pinned to your club — asking about any other organisation is refused, and the same row-level security that protects the admin area applies to every request underneath.
- It can never do more than you can. A token acts as the admin who created it, so it is limited to that person's own permissions, checked afresh on every request. Ticking boxes can only take powers away, never add them.
- It never sends email or moves money. Broadcasts still wait unsent in the composer for you to choose an audience, and nothing about payments, refunds or member records can be changed over a token.
- Nothing happens without a confirmation. Every change is previewed first: the assistant shows exactly what it would do, and only acts when it is told to go ahead a second time.
- It stops working when you do. If the creating admin loses their admin role, the token stops working immediately — the role is re-checked on every request. Revoking it on this page is instant too. Changing plan does not affect it.
- It can never change your subscription. Plans, billing and payment settings are platform-staff tools; no organisation token reaches them, whatever you tick.
- Every action is logged. Everything the assistant reads or changes is recorded in the audit log under the creating admin's name.
Field reference
Webhook event types:
| Event | Fires when |
|---|---|
member.created | A membership application is approved |
member.updated | A member's payment status or paid-until date changes |
membership_order.paid | An online membership purchase or renewal is paid |
donation.paid | A donation payment succeeds |
shop_order.paid | A shop order is paid |
facility_booking.paid | A facility booking is paid by card |
raffle_order.paid | A raffle ticket order is paid |
program_enrolment.paid | A program enrolment is paid |
program_pass.paid | A program pass is paid and ready to use |
checkin.created | A member checks in (staff list, QR or kiosk) |
Content events tell another website (your WordPress site, for example) that something it shows has changed, so it can refresh:
| Event | Fires when |
|---|---|
news.published | A news post goes live |
news.updated | A news post is edited |
news.unpublished | A news post is taken down, or saved as a draft from the editor |
event.published | An event goes live |
event.updated | An event is edited, or taken back to draft |
event.deleted | An event is deleted |
fixture.updated | A fixture is added, changed or removed |
result.recorded | A match result is recorded, confirmed or approved |
ladder.updated | A ladder adjustment is saved |
team.updated | A team is added, changed, archived or restored |
sponsor.updated | A sponsor is added, changed or removed |
page.published | A website page is published |
membership_type.updated | A membership type is added, changed, archived or restored |
product.updated | A shop product is saved or removed |
program.updated | A program is saved, published, unpublished or archived |
tenant.theme_updated | The site theme or its colours change |
tenant.settings_updated | Organisation settings, logo or favicon change |
Delivery payloads all share one shape: { "event": "...", "tenant_id": "...", "data": { ... }, "created_at": "..." }. For the payment, member and check-in events, data is a small snapshot of the record (ids, status, amounts — you can fetch fuller details from the API using the ids). For content events, data is only { "id": "...", "updated_at": "..." }: your system fetches the item itself, so nothing about people ever travels in a content event.
Tips & FAQ
The key I created has disappeared — where do I find it? You can't: the full key is shown once at creation and only a fingerprint is stored. Create a new key and revoke the old one.
Why am I getting 403 responses? The API needs the Pro or Enterprise plan. If your club has moved to a lower plan, existing keys stop working until you upgrade again.
Why am I getting 429 responses? Each key is limited to 120 requests per minute. Slow your requests down or spread them out — the Retry-After header says how long to wait.
Can the API change or delete data? No. Version 1 keys are strictly read-only, and every key is scoped to your organisation only.
I marked a booking paid at the venue — why was there no webhook? The .paid events fire when a card payment goes through your payment provider. Money you record yourself in the admin, like Mark paid on a facility booking, is saved to the booking but sends nothing to your endpoints. Marking a member paid is the exception: that fires member.updated.
A delivery shows as Failed — will it retry? Each delivery retries up to 5 times. After the fifth failure it stops; fix your endpoint and use Send test event to confirm it's healthy again.
What's the difference between an API key and an MCP token? An API key (chk_…) is for your own scripts and tools — plain REST, read-only. An MCP token (chmx_…) is for AI assistants — it speaks the Model Context Protocol, can make and publish changes if you allow it, and acts as the admin who created it.
Can the assistant change our website? Yes, if you let it. With Allow changes on it can create and edit pages, news posts and events; with Allow publishing on it can make them public and take them down again. Leave publishing off and everything it writes waits for a person. Either way it previews each change and asks before acting.
Can it email our members? No. Broadcasts are drafted only — they sit unsent in the composer until you choose an audience and send them yourself. Payments, refunds and member records are out of reach too.
Can I give an assistant just one job? Yes. Under Limit what this token can touch, tick only the areas it needs. A token limited to "Publish news" is refused by everything else, and the refusal says which permission was missing — so you can hand one assistant the news page and another the whole website, each with its own token.