Developers
API Documentation
A REST API to integrate external systems: sync members and customers, verify members who sign up publicly, read shifts and bookings, and subscribe to real-time webhook events.
Introduction
The Public API lets you integrate external systems with your organization: sync members and customers, verify members who sign up publicly, read shifts and bookings, and subscribe to real-time webhook events.
It is a REST API over HTTPS. All requests and responses are JSON. Every endpoint is scoped to your organization — you can only ever read or write your own data.
Base URL: all endpoints are served under /api/v1. Download the machine-readable contract from the OpenAPI link at the top of this page.
Authentication
Authenticate every request with an API key as a bearer token in the Authorization header. Create keys in the Developer area of your organization portal.
Keys come in two modes. **Live keys** (fcn_live_…) act on your production data. **Test keys** (fcn_test_…) resolve to your sandbox organization, so you can build and test integrations against disposable demo data without touching production.
Keys are shown once at creation — store them securely. A key carries a fixed set of scopes; requests outside a key’s scopes are rejected with insufficient_scope.
curl "https://YOUR-DOMAIN/api/v1/organization" \
-H "Authorization: Bearer $API_KEY"Errors
Errors use standard HTTP status codes and a consistent body: { "error": { "code", "message", "details?" } }. The code is stable and safe to branch on; message is human-readable and may change.
Validation failures (400 invalid_request) include a details array of { path, message } entries pinpointing each offending field.
Every response carries an X-Request-Id header — include it when contacting support.
400 invalid_request — The request body, query, or path parameters failed validation. details lists each offending field.
401 invalid_api_key — The Authorization header is missing, malformed, or the key does not exist.
401 revoked_api_key — The key was revoked. Create a new key in the Developer portal.
401 expired_api_key — The key is past its expiry date (rolled keys expire 24 hours after rolling).
403 insufficient_scope — The key is valid but lacks the scope this endpoint requires.
403 module_not_enabled — The endpoint belongs to a module that is not active for this organization.
403 sandbox_unavailable — A test-mode key was used but the organization has no sandbox. Create one from the portal, then retry.
404 not_found — Unknown route, or the resource does not exist in your organization.
405 method_not_allowed — The path exists but not for this HTTP method. The Allow header lists valid methods.
409 idempotency_conflict — This Idempotency-Key was already used with a different request payload.
409 idempotency_in_progress — The original request with this Idempotency-Key is still processing. Retry shortly.
409 conflict — The request conflicts with the current state of the resource.
429 rate_limited — Rate limit exceeded. Honor Retry-After and the X-RateLimit-* headers.
500 internal_error — Something went wrong on our side. The X-Request-Id header identifies the request for support.
{
"error": {
"code": "invalid_request",
"message": "Invalid request body.",
"details": [
{
"path": "body.capacity",
"message": "Expected number, received string"
}
]
}
}Rate limits
Requests are rate limited per key. Standard keys allow 120 requests per minute; a few expensive endpoints have a lower strict-tier limit.
Every authenticated response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (epoch seconds). When you exceed the limit you receive 429 rate_limited with a Retry-After header — back off until the window resets.
Idempotency
Safely retry write requests by sending an Idempotency-Key header (any unique string). If a request with the same key succeeds, replays within 24 hours return the original response with Idempotency-Replayed: true instead of performing the action again.
Reusing a key with a different request body returns 409 idempotency_conflict. A replay while the original is still in flight returns 409 idempotency_in_progress — retry shortly.
curl -X POST "https://YOUR-DOMAIN/api/v1/members" \
-H "Authorization: Bearer $API_KEY" \
-H "Idempotency-Key: 6f9c1e2a-..." \
-H "Content-Type: application/json" \
-d '{"business_name":"Example Care Services"}'Pagination
List endpoints return { "data": [...], "has_more": boolean, "next_cursor": string | null }, newest first.
Pass ?limit= (1–100, default 25) to size a page. When has_more is true, pass next_cursor as ?cursor= to fetch the next page. Cursors are opaque — do not construct them yourself.
Scopes
Each key grants a set of scopes. A :write scope implies the matching :read scope. Grant the minimum a given integration needs.
members:read — List and read members.
members:write — Create, update, verify, and deactivate members.
customers:read — List and read customers and sites.
customers:write — Create and update customers and sites.
shifts:read — List and read shifts and their assignments.
timesheets:read — List and read timesheets (worked/billable time per assignment).
invoices:read — List and read invoices and their line items.
bookings:read — List and read bookings.
organization:read — Read your organization profile.
webhooks:read — List webhook endpoints and delivery history.
webhooks:write — Manage webhook endpoints, secrets, and redeliveries.