The developer API
Ask AI
API keys, the REST surface, webhooks and how they differ from the MCP connector
For code that talks to SquadBear directly - a script, a backend job, a webhook listener. MCP is the other surface (setup): a connected agent acts as the person who connected it, on scopes consented per connection and separate from the API-key scopes below.
API keys
Create one at Developer → API keys
(app.squadbear.com/workspace/api-keys),
admin and owner only. The token starts with sbk_ and is shown once; only its display
prefix is stored, so a lost token is replaced, not recovered.
A key carries the scopes checked at creation - people:read, teams:read, leave:read,
leave:write, balances:read, reports:read, time:read, time:write,
attendance:read, events:read, webhooks:write, admin:write. An endpoint whose scope the
key lacks returns 403 naming it, not a partial result.
A key belongs to the workspace, not a user account: a tenant-scoped service actor reading the whole tenant, audited to the employee who minted it. Scopes bound its reach, not a role.
The REST API
Base path is /api/v1, authenticated with Authorization: Bearer <token> - not a custom
header; a missing or malformed one returns 401 with WWW-Authenticate: Bearer.
Interactive docs are public at
app.squadbear.com/api/v1/docs, the same document
as JSON at /api/v1/openapi.json.
Attendance is read-only: GET /attendance/shifts and GET /attendance/presence. No bearer
key writes a punch - an agent punches over MCP (punch, attendance:write), a paired
kiosk posts to /api/kiosk with its device token.
Webhooks
Manage subscriptions at Developer → Webhooks
(app.squadbear.com/workspace/webhooks);
its "Recent events" panel lists deliveries. Subscribe to every event, or pass an
eventTypes list at creation over API or MCP. Event names include request.created,
request.approved, employee.created and process.run_completed; unknown ones are
rejected. Webhooks are Team and Enterprise only - creating one on Free returns 402
upgrade_required, and a downgraded workspace's subscriptions go dormant, not deleted.
Every delivery is a signed POST: X-Squadbear-Signature: sha256=<hex-hmac>, HMAC-SHA256 of
the raw body keyed by the secret shown once at creation - verify it before trusting a
payload. Failed deliveries retry on the five-minute cron, 3 attempts per event; the third
failure is terminal. Delivery is at-least-once - backfill from GET /api/v1/events with
the last seq you processed as after, within the plan's retention window - 30 days on
Free, 365 on Team and Enterprise. Anything older is pruned nightly and cannot be replayed.
UI names vs. wire names
Wire names are the stable contract; UI labels change. Where they differ:
| UI name | MCP tools | API route | Scope |
|---|---|---|---|
| People → Company documents (Files) | list_files, get_file_link, delete_file | /api/files | files:read / files:write |
| People → Company documents (Campaigns) | list_policies, create_policy_rollout, acknowledge_document | /api/documents/policies | docs:ack |
| Inbox → Needs action | list_my_work, complete_process_action | /api/my-work, /api/my-tasks/summary | tasks:read / tasks:write |
| Work items (project tasks) | list_work_items, create_work_item | /api/projects/:id/tasks, /api/tasks/:id | time:read / time:write |
| Leave → My leave | request_time_off, list_my_requests | /api/v1/leave-requests (public) · /api/requests (internal) | leave:read / leave:write |
The tasks:* scopes and /api/tasks/:id predate the task→work-item rename, so MCP says
work_item while the parameter noun stays task - log_time takes a taskId,
get_time_report groups by task. files is the wire umbrella; Company documents is
its scope=tenant slice.
Worked example
Bartek, Northlake's ops engineer, mirrors new leave requests into a team chat: a key
scoped to webhooks:write, a subscription
{"url": "https://hooks.example/northlake", "eventTypes": ["request.created"]}, and a
receiver checking X-Squadbear-Signature against the one-time secret.