TeamPilot API v1
Updated 8 July 2026
The TeamPilot API is a read-only REST API scoped to a single workspace. Use it to pull people, shifts, timesheets, time-off and tasks into payroll, BI or your own tools, and subscribe to webhooks to react to events as they happen.
Base URL
Every request goes to your own workspace subdomain, under /api/v1:
https://your-workspace.teampilot.app/api/v1
A token minted in one workspace is valid only for that workspace. There is no cross-workspace endpoint.
Authentication
Create a token under Settings -> API & webhooks. Send it as a bearer token:
Authorization: Bearer 42|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Tokens carry the read ability. Pay figures (hourly_rate_pence, payroll_number) are only returned to tokens that also hold the people:pay ability, which you opt into when you create the token.
The API requires a plan that includes API access. Requests on a plan without it receive 403 forbidden.
Pagination
List endpoints are cursor-paginated:
{
"data": [ ... ],
"meta": { "next_cursor": "eyJpZCI6NDJ9", "per_page": 50 }
}
Pass ?cursor=<next_cursor> to fetch the next page and ?per_page=<n> to size it (default 50, maximum 100). When next_cursor is null you have reached the end.
Filtering
updated_since=<ISO8601>returns only records changed on or after that instant. Use it to poll for changes cheaply./shiftsdefaults to the current week and acceptsstatus=<status>.
Endpoints
| Method | Path | Returns |
|---|---|---|
| GET | /people |
Workspace members, including archived |
| GET | /people/{id} |
A single person |
| GET | /shifts |
Scheduled shifts (current week by default) |
| GET | /time-entries |
Clock-in/out timesheet entries |
| GET | /leave-requests |
Time-off requests |
| GET | /tasks |
Tasks |
All money is returned in integer pence with a _pence suffix; all times are ISO 8601 in UTC.
Errors
Errors use a consistent envelope:
{ "error": { "code": "validation_failed", "message": "The given data was invalid.", "fields": { "per_page": ["..."] } } }
| Status | Code |
|---|---|
| 401 | unauthenticated |
| 403 | forbidden / account_suspended |
| 404 | not_found |
| 422 | validation_failed |
| 429 | rate_limited |
| 500 | server_error |
Rate limits
Requests are limited to 60 per minute per token. Over the limit you receive 429 rate_limited with a Retry-After header.
Webhooks
Add an https endpoint under Settings -> API & webhooks and subscribe it to events. When an event fires we POST a JSON envelope:
{
"id": "018f...",
"event": "shift.published",
"created_at": "2026-07-08T12:00:00Z",
"data": { ... }
}
Headers on each delivery:
X-TeamPilot-Eventthe event nameX-TeamPilot-Deliverya unique id for this deliveryX-TeamPilot-Signaturean HMAC-SHA256 of the raw request body, keyed with your endpoint's signing secret
Verifying the signature
Compute the HMAC over the exact bytes you received and compare, in constant time, to the header:
$expected = hash_hmac('sha256', $rawBody, $signingSecret);
if (! hash_equals($expected, $request->header('X-TeamPilot-Signature'))) {
abort(400);
}
Respond with any 2xx to acknowledge. Non-2xx responses are retried with backoff; persistently failing deliveries can be replayed from the webhook's delivery history.
Events
| Event | Fires when |
|---|---|
shift.published |
A shift is published to the schedule |
timesheet.approved |
A timesheet entry is approved |
leave.approved |
A time-off request is approved |
person.joined |
A person accepts an invitation and joins |