Documentation

The developer API

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 nameMCP toolsAPI routeScope
People → Company documents (Files)list_files, get_file_link, delete_file/api/filesfiles:read / files:write
People → Company documents (Campaigns)list_policies, create_policy_rollout, acknowledge_document/api/documents/policiesdocs:ack
Inbox → Needs actionlist_my_work, complete_process_action/api/my-work, /api/my-tasks/summarytasks:read / tasks:write
Work items (project tasks)list_work_items, create_work_item/api/projects/:id/tasks, /api/tasks/:idtime:read / time:write
Leave → My leaverequest_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.