REST API

Headcount exposes JSON endpoints for plans, subscriptions, checkout sessions, the customer portal, and member info. All endpoints are prefixed with /actions/headcount/api/.

Authentication

Endpoints use one of two auth modes:

  • Session — the request rides on a logged-in Craft user’s session cookie. Best for first-party templates and SPAs.
  • API key — pass a Headcount-issued key in the X-Headcount-Api-Key header. Best for server-to-server, mobile apps, and external integrations.

Issue and revoke keys from Headcount → Settings.

The ?apiKey= query parameter was removed in 5.2.0, because it left keys in web server logs, browser history and outbound Referer headers. POST /checkout also requires a CSRF token from 5.2.0 — it is session-authenticated, so front-end JS must send CRAFT_CSRF_TOKEN or the X-CSRF-Token header.

Endpoints

MethodEndpointAuthDescription
GET/plansPublicList all enabled plans
GET/plan?handle=xxxPublicGet a specific plan
GET/subscriptionsSession / API keyList the current user’s subscriptions
GET/subscription?id=xxxSession / API keyGet a specific subscription
POST/checkoutSessionCreate a checkout session
GET/portalSessionGet a Stripe Customer Portal URL
GET/memberSession / API keyGet current member info with subscriptions

Example: List Plans

curl https://yoursite.com/actions/headcount/api/plans

Alongside the usual name, price and billing interval, each plan carries its term shape. seasonStart and seasonEnd describe the season currently being sold; currentPrice is what a member would actually be charged if they joined right now, which is lower than price once a pro-rata season is under way:

{
  "plans": [
    {
      "handle": "club-membership",
      "price": 120,
      "currency": "GBP",
      "termType": "fixed",
      "seasonStart": "2026-07-01T00:00:00+01:00",
      "seasonEnd": "2027-06-30T23:59:59+01:00",
      "currentPrice": 90
    }
  ]
}

termType is recurring or fixed; the season fields are null for recurring plans.

Example: Get Member Info

curl -H 'X-Headcount-Api-Key: your-key' \
  https://yoursite.com/actions/headcount/api/member

Example: Create Checkout Session

curl -X POST \
  -H 'X-CSRF-Token: ...' \
  -d 'planHandle=pro-monthly' \
  https://yoursite.com/actions/headcount/api/checkout

The response includes checkoutUrl and sessionId — redirect the user to checkoutUrl to complete payment. A season plan whose one-off window has ended answers 410 Gone.

Wallet Card Verification

The QR code on a wallet card points at a site URL rather than an API endpoint, so a shop assistant’s phone camera can open it directly. Requesting it with Accept: application/json returns the same answer as JSON, for building a scanner of your own:

curl -H 'Accept: application/json' \
  'https://yoursite.com/headcount/wallet/verify?serial=...&token=...'

{
  "valid": true,
  "memberName": "Sam Okafor",
  "planName": "Club Membership",
  "expiryDate": "2027-06-30T23:59:59+01:00"
}

Both parameters come from the card itself; a wrong or missing token answers valid: false rather than an error, so a caller probing serial numbers learns nothing.

Webhook Endpoints

Inbound payment provider webhooks live at separate URLs (no auth headers, signature-verified):

  • POST /actions/headcount/webhook/stripe
  • POST /actions/headcount/webhook/paypal

See the Configuration guide for the events Headcount expects.

More in Site Features

Pairs well with Headcount

The bigger pieces a client asks for by name. Events, bookings, memberships, courses, reviews, a support desk, job listings and the rest.