The developer API
API keys, the REST surface, webhooks and how they differ from the MCP connector
This is for code that talks to SquadBear directly — a script, a backend job, a webhook listener — rather than a person chatting with an agent.
API keys
Create one at Admin → API keys. The full token is shown exactly once
at creation time ("Copy this token now — it is shown only once and cannot be retrieved
later") and starts with sbk_; only its display prefix is kept afterward, so if you
lose the token you generate a new one.
A key carries whatever subset of scopes you check when creating it — people:read,
teams:read, leave:read, leave:write, balances:read, reports:read, time:read,
time:write, events:read, webhooks:write, admin:write. Requesting an endpoint that
needs a scope your key doesn't have fails with a 403 naming the missing scope, not a
silent partial result.
A key acts as the workspace, not as any one person — it isn't tied to whoever created it, and losing that person doesn't break it. Internally it resolves to a tenant-scoped service actor; the scopes you granted it are what actually bound what it can do, not a role.
The REST API
Base path is /api/v1. It authenticates with Authorization: Bearer <token> — not a
custom header — and returns a 401 with WWW-Authenticate: Bearer if that's missing or
malformed. Interactive, always-current docs (generated from the same route definitions
the server runs) are live at
app.squadbear.com/api/v1/docs — no separate
spec to keep in sync by hand.
Webhooks
Manage subscriptions at Admin → Webhooks. When adding one you choose between
every event and a specific set of event types; the same choice is available via the API
or MCP by passing an eventTypes list at creation (omit it for every event). Event names
include request.created, request.approved, employee.created, employee.offboarded,
checklist.completed, ack.signed, and more, visible as they happen in the "Recent
events" panel on that same page. Unknown event names are rejected at creation.
Every delivery is a signed POST: header X-Squadbear-Signature, value
sha256=<hex-hmac>, computed as HMAC-SHA256 of the raw request body using the secret
shown once when you create the webhook. Verify it before trusting a payload. Deliveries
that fail are retried on a short cron cycle, up to 3 attempts total, then dropped — treat
a webhook as at-least-once-if-your-endpoint-is-usually-up, not guaranteed.
The MCP connector, briefly
There's a third surface: connect an agent over MCP and it can act in the workspace using
your own permissions in a chat, no code required — see
Connect Claude to SquadBear for setup. Its
scopes (leave:read, time:write, health:write, and the rest) are consented per
connection when someone approves it, and are a completely separate list from the API-key
scopes above — a workspace's API keys don't grant anything to a connected agent, and
vice versa.
UI names vs. wire names
The names you see in the app and the identifiers you use over the API and MCP deliberately diverge — the wire names are a stable contract, the UI names are tuned for humans. Where they differ, this is the map:
| UI name | MCP tools | API route | Scope |
|---|---|---|---|
| Documents (Company documents) | list_files, get_file_link, delete_file | /api/files | files:read / files:write |
| My Tasks (personal to-dos) | list_my_checklist_items, list_checklists | /api/my-tasks, /api/checklists | tasks:read / tasks:write |
| Work items (time-tracking rows) | list_work_items, create_work_item | /api/tasks | time:read / time:write |
| Acknowledgments | list_ack_campaigns, create_ack_campaign, acknowledge_document | /api/ack-campaigns | docs:ack |
| Health checks | list_health_runs, create_health_run | /api/health/runs | health:read / health:write |
| My Leave | request_time_off, list_my_requests | /api/v1/leave-requests (public) · /api/requests (internal) | leave:read / leave:write |
One historical wrinkle: the tasks:* scopes and the /api/tasks REST route predate the
task→work-item rename, so the MCP tools say work_item (list_work_items,
create_work_item) while the REST route and taskId params keep the old noun. Checklist
to-dos live under list_my_checklist_items and the tasks:* scopes. And "files" is the
wire umbrella: the Documents library the UI shows is the scope='tenant' slice of it.
Worked example
Northlake's ops engineer, Bartek, wants new leave requests posted into their team chat
the moment someone files one. He creates an API key scoped to webhooks:write at Admin →
API keys, then calls the webhook endpoint with
{"url": "https://hooks.example/northlake", "eventTypes": ["request.created"]} to skip
subscribing to everything. His endpoint checks X-Squadbear-Signature against the
one-time secret before it trusts a payload, then posts the requester's name and dates
into chat.