Developers

Webhooks

Webhooks push an event to your own URL the moment something happens in your workspace, so you never have to poll for changes. Add an endpoint under Developer > Webhooks, choose the events you want or pick 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.

  • X-WorkBOS-Event names the event that fired, using the same names as the table above.
  • X-WorkBOS-Signature-V1 is what to verify: t=<unix seconds>,v1=<lowercase hex>. Check t is within five minutes of your clock, then recompute HMAC-SHA256 over the exact string "<t>.<raw body>" with your endpoint secret and compare it to the v1 half. Because the timestamp is signed, a delivery somebody captured yesterday stops verifying.
  • X-WorkBOS-Signature is the form integrations written before this used, and is still sent, unchanged: "sha256=" followed by the HMAC-SHA256 of the raw body alone. It verifies the same bytes for ever, so treat it as a fallback while you move to the timestamped one. An endpoint with no secret set is never delivered to, so neither header is ever empty.
  • X-WorkBOS-Timestamp repeats the t value for readability. X-WorkBOS-Delivery repeats the delivery id, which is also inside the body as workbos_delivery_id. Read the body one, because the body is signed and headers are not. A delivery that fails is retried automatically with backoff, up to six attempts, so the same id can arrive more than once: treat it as an idempotency key.

Verify the raw bytes of the body, before any JSON.parse or framework body-parsing reshapes them: recompute the signature over exactly what you received and compare it to X-WorkBOS-Signature. Slack, Microsoft Teams and Discord endpoints are the exception: they receive a pre-formatted chat message instead of raw JSON, so there is nothing to verify by hand.

const crypto = require('crypto');

function isValidWorkBOSSignature(rawBody, headerValue, endpointSecret) {
  const expected = 'sha256=' + crypto.createHmac('sha256', endpointSecret).update(rawBody).digest('hex');
  const a = Buffer.from(headerValue || '');
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// rawBody must be the unparsed request body, for example from express.raw().
app.post('/webhooks/workbos', express.raw({ type: 'application/json' }), (req, res) => {
  const header = req.get('X-WorkBOS-Signature') || '';
  if (!isValidWorkBOSSignature(req.body, header, process.env.WORKBOS_ENDPOINT_SECRET)) {
    return res.status(401).send('invalid signature');
  }
  res.status(200).send('ok');
});

Return any 2xx status to acknowledge a delivery. A failed or timed-out delivery retries automatically with exponential backoff, up to six attempts, and delivery status and attempt history are visible under Developer > Webhooks. If an endpoint fails every attempt on twenty deliveries inside a day, WorkBOS switches it off and tells the workspace owners why, rather than retrying a dead address for ever. Fix the address and switch it back on from the same page.

Endpoints must be https. A plain http address is refused, because every deal, invoice, voicemail and text-message payload would otherwise travel in the clear where anything on the network path can read it.

Was this page useful?