Developer
API Reference
The VERVE Pulse REST API reads AND writes your gym’s data programmatically. Available on the Pro plan.
https://app.vervepulse.io/api/v1JSONBearer token120 req/min per key1,000 req/hour per keyAuthentication
All API requests must include your API key in the Authorization header (or an X-API-Key header). Generate your developer secret from Settings → Developer API.
curl https://app.vervepulse.io/api/v1/members \ -H "Authorization: Bearer vp_live_xxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json"
Keep your API key secret. Do not expose it in client-side code or public repositories. Refresh compromised secrets from Settings → Developer API. The previous secret stops working immediately; update your integration with the new one.
Choose Lead submissions only for website enquiries. This grants leads:write without access to read gym data. Choose read access or read and write access for broader integrations. Endpoints marked WRITE SCOPE require broad write access. Every request is scoped to the gym that owns the secret.
Endpoints
/api/v1/members/api/v1/members/:id/api/v1/membersWRITE SCOPE/api/v1/membersWRITE SCOPE/api/v1/classes/api/v1/classes/:id/api/v1/bookings/api/v1/bookings/:id/api/v1/bookingsWRITE SCOPE/api/v1/bookings/:idWRITE SCOPE/api/v1/checkins/api/v1/checkins/:id/api/v1/checkinsWRITE SCOPE/api/v1/invoices/api/v1/invoices/:id/api/v1/plans/api/v1/plans/:id/api/v1/leads/api/v1/leads/:id/api/v1/leadsLEADS:WRITE OR WRITEEvery list endpoint takes limit (default 100, max 200) and offset, and responds with{ data, limit, offset, has_more }. The full machine-readable catalogue (columns, filters per resource) is always up to date at GET /api/v1 — no key required.
Website lead submissions
Send POST /api/v1/leads from your server with a lead-submission secret. Include a stable external_id, a name (or first and last name), and an email or phone number. Send child details, lesson preferences or other enquiry information in message. Pulse displays the website message separately from staff notes.
First submission returns 201. Repeating the same external id with the same integration returns 200 and the same lead id; changed fields correct the enquiry, and an identical retry preserves staff edits. Refreshing a secret keeps its submission history. A deleted enquiry returns 410. Send corrections in order and use a new external id for a new enquiry.
Swagger documentation and test requests · OpenAPI specification
Writing data
Member, booking and check-in writes run through the same validation as the matching action in the Pulse dashboard — the same required fields and guardrails (e.g. a member’s plan/location must belong to your gym, a banned member cannot be booked or checked in) — never a separate, looser path. A foreign id (someone else’s member, class or booking) always returns a plain 404, never another gym’s data.
curl -X POST https://app.vervepulse.io/api/v1/members \
-H "Authorization: Bearer vp_live_xxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"first_name": "Alex", "last_name": "Johnson", "email": "alex@example.com"}'A member created this way starts as pending, the same rule as the dashboard’s Add Member wizard: they cannot check in or sign in to the member app until a subscription is linked to them. For a member you bill outside Pulse, send "pays_manually": true and they start active straight away.
Webhooks
VERVE Pulse can push webhook events to your own HTTPS endpoint when things happen in the gym. Configure endpoints in Settings → Developer API → Outbound webhooks. Deliveries are queued and sent by a job that runs about once a minute, not the instant the event happens.
member.createdA new member was addedmember.updatedA member’s profile, plan or status was changedbooking.createdA member was booked into a class (booked or waitlisted)booking.cancelledA booking was cancelledcheckin.createdA member checked ininvoice.paidA membership invoice was paidinvoice.payment_failedA membership invoice payment failedExample payload:
{
"event": "member.created",
"created_at": "2026-07-28T06:00:00Z",
"data": {
"id": "8f1e2c3a-...",
"first_name": "Alex",
"last_name": "Johnson",
"email": "alex@example.com",
"status": "active",
"created_at": "2026-07-28T06:00:00Z"
}
}Every delivery carries X-Pulse-Timestamp (unix seconds) andX-Pulse-Signature: a hex HMAC-SHA256 oftimestamp + "." + rawBody, signed with the endpoint’s own secret (shown once, when you create the endpoint). Check both before trusting the payload: the signature proves it came from us, the timestamp proves it is not an old delivery being replayed at you.
const crypto = require('crypto');
function isValidPulseDelivery(rawBody, timestampHeader, signatureHeader, endpointSecret) {
// Reject anything older than 5 minutes so a captured delivery can't be replayed.
const ageSeconds = Math.abs(Math.floor(Date.now() / 1000) - Number(timestampHeader));
if (!Number.isFinite(ageSeconds) || ageSeconds > 300) return false;
const expected = crypto.createHmac('sha256', endpointSecret)
.update(timestampHeader + '.' + rawBody)
.digest('hex');
// Constant-time comparison — never a plain === on secret-derived values.
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}A failed delivery (timeout, non-2xx response, or a redirect — redirects are never followed) is retried with backoff: 1 minute, 10 minutes, 1 hour, then 6 hours, for up to 5 attempts total. After that it’s marked failed. An endpoint that racks up 10 failed deliveries in a row (no successful delivery in between) is automatically disabled and the gym is emailed about it — re-enable it from Settings → Open API once the receiving end is fixed. Recent deliveries and a “Redeliver” button live on the same settings page.
Endpoint URLs must be https:// and cannot resolve to localhost or a private/internal IP address — checked both when you save the endpoint and again on every send, so a hostname that gets re-pointed at internal infrastructure later is still blocked.
Rate Limits & Errors
Reads are capped at 120 requests per minute per API key; writes at 1,000 requests per hour per API key. Every response carriesX-RateLimit-Limit,X-RateLimit-Remaining andX-RateLimit-Reset headers. Exceeding the limit returns 429 Too Many Requests.
200OK — request succeeded201Created — resource was created400Bad Request — invalid or missing parameters401Unauthorized — invalid, missing or revoked API key403Forbidden — API not on your plan, or this key lacks the write scope404Not Found — resource does not exist, or belongs to another gym409Conflict — e.g. member already booked, or that spot is taken429Too Many Requests — rate limit exceeded500Server Error — contact supportNeed help with the API?
API access is available on the Pro plan ($549/mo plus GST). Contact us for enterprise rate limits or custom integration support.
Contact developer support →