Developers

Developer API & webhooks

WorkBOS exposes a REST API and outgoing webhooks so you can read and write your workspace data from your own code or no-code tools. Every request is scoped to your workspace by the API key, so you can never reach another workspace. Set both up under Developer.

  1. 1

    Create an API key

    Developer > API keys > Create. Choose what the key may do: Read only (the default) or Read and write. The full key (snrp_...) is shown once. Copy it immediately. Send it on every request as a bearer token: Authorization: Bearer snrp_... Keys are workspace-scoped. You can change a key between read-only and read-write later without reissuing it, and revoke any key instantly, from the same screen.

  2. 2

    Read data

    Base URL: https://dkjdtyzjdkumnpdyezbs.supabase.co/functions/v1/api-v1. GET /api-v1/<resource> lists records (use ?limit= up to 200 and ?offset= to page); GET /api-v1/<resource>/<id> returns a single record.

  3. 3

    Write data

    POST /api-v1/<resource> creates from a JSON body, PATCH /api-v1/<resource>/<id> updates, DELETE /api-v1/<resource>/<id> removes. Only a key with the write scope may do any of it: a read-only key is refused with 403 on every non-GET. Only the documented fields are accepted and your workspace is applied automatically. You never send an org id.

  4. 4

    Stay inside the rate limits

    600 requests per minute per key and 1,200 per minute per calling IP address, counted in a sliding 60-second window. Going over returns 429 rate_limited and the request is refused, not queued, so retry it after backing off. The per-IP ceiling is counted before the key is read, so a shared egress address (a cloud function, an office NAT) shares one bucket. Both numbers are shown on Developer > API keys.

ResourceAccessWritable fields
tasksRead + writename, status, priority, project_id, assignee_id, due_date, description
projectsRead + writename, status, company_id, pm_id, description
dealsRead + writetitle, stage (Lead/Qualified/Proposal/Negotiation/Won/Lost), value, company_id, contact_id, owner_id, expected_close, notes
contactsRead + writefull_name, email, phone, title, status, company_id, owner_id, notes
accountsRead + writeCRM companies: name, industry, website, phone, owner_id, notes
companiesRead + writeWorkspace companies (the Company layer): name, description
invoicesRead onlyFinancial records cannot be written through the API, whatever scope the key holds

What “Read and write” actually covers: tasks, projects, deals, contacts, accounts and companies. Invoices are readable and never writable, for every key. Narrowing a key that something is already using takes effect immediately, so the console asks you to confirm and tells you when the key was last used before it does it.

company_id on deals and contacts points to an accounts record (a CRM company), not the workspace companies layer. Create or look up the account first, then pass its id.

Webhooks push events to your own URL the instant something happens, so you never have to poll. Add an endpoint under Developer > Webhooks, choose the events you care about (or All events), and each matching change sends a signed POST.

Every event WorkBOS can send is listed below. It is the same list WorkBOS itself holds, and a build check fails when the platform starts sending a type this table does not name, so it cannot quietly fall behind again: it listed eight events while nineteen were being sent.

EventFires whenCan contain text from outside your workspace
task.createdA task is createdNo
project.createdA project is createdNo
client.createdA client is createdNo
deal.createdA deal is createdNo
deal.stage_changedA deal moves to a different stageNo
deal.wonA deal is marked WonNo
invoice.createdAn invoice is createdNo
invoice.paidAn invoice is marked paidNo
tag.addedA tag is added to a recordNo
lead.became_hotA lead crosses the hot thresholdNo
form.submittedSomeone submits a published formYes
survey.completedSomeone completes a surveyYes
sms.receivedAn inbound text message arrivesYes
voicemail.receivedA voicemail is left, with its transcriptYes
call.completedA call endsYes
call.missedA call is missedYes
call.ai_intakeThe AI receptionist takes an enquiryYes
chief.flagsThe assistant flags something for attentionNo
chief.dream.completedAn overnight assistant run finishesNo
webhook.secret_rotatedAn endpoint signing secret is rotatedNo

Choose your events rather than taking All events. “All events” means every event that exists now AND every one added later, so an endpoint set up for deals and invoices started receiving voicemail transcripts and inbound text messages the day those events shipped: words a member of the public typed or spoke, arriving in whatever channel you pointed at us. New endpoints now start with the eight business events above; the last column tells you which of the rest carry outside text before you add them.

Verify every delivery. Prefer X-WorkBOS-Signature-V1, which looks like t=1767225600,v1=<hex> : take the t value, check it is within five minutes of your own clock, then recompute HMAC-SHA256 over the exact string "<t>.<raw body>" using your endpoint secret and compare the v1 half. X-WorkBOS-Signature is still sent, byte for byte, and is "sha256=" followed by the HMAC of the body alone. That one verifies the same delivery for ever, which is why the timestamped one exists. An endpoint with no secret is never delivered to at all, so a signature header is never empty. Slack, Teams and Discord endpoints receive a pre-formatted message instead of raw JSON.

Verify against X-WorkBOS-Signature and X-WorkBOS-Event. Deliveries also carry a deprecated older pair with identical values, kept only so integrations written before the rename keep verifying; it stops being sent after the date named in the X-WorkBOS-Deprecation header on every delivery. Nothing else about the signature has changed, so moving across is a one-line edit.

  • Deduplicate: every body now carries workbos_delivery_id and workbos_timestamp, in every format including Slack, Teams, Discord and automation webhooks, which used to carry neither. The same id is in the X-WorkBOS-Delivery header for convenience, but read it from the BODY: the body is signed and the header is not. A failed delivery is retried up to six times with backoff, so the same id can arrive more than once.
  • Replay: check the timestamp. X-WorkBOS-Timestamp and the t= value inside X-WorkBOS-Signature-V1 are both covered by that signature, so a captured delivery stops verifying once it is older than your tolerance. Five minutes is a sensible one. The older body-only signature has no expiry at all, which is the reason to move across.

Reliable delivery: a server-side dispatcher POSTs each event and records the real HTTP status. Failed or timed-out deliveries retry automatically with exponential backoff (up to 6 attempts); return any 2xx to acknowledge. Delivery status + attempts are visible under Developer > Webhooks.

Was this page useful?