Browse the docs

Connect

Webhooks

Signed HTTPS calls to your server when something happens in a workspace.

Webhooks are an automation action. In Settings → Automations, pick a trigger, add a Send a webhook step and paste your HTTPS address. Atrix POSTs the event as JSON and signs every delivery.

The request

http
POST /your/endpoint HTTP/1.1
Content-Type: application/json
X-Atrix-Event: deal.won
X-Atrix-Signature: t=1761552000,v1=5f2b…

{
  "event": "deal.won",
  "payload": {
    "dealId": "deal_1",
    "title": "Brand refresh for Northwind",
    "companyName": "Northwind",
    "stage": "WON",
    "fromStage": "PROPOSED",
    "ownerId": "mem_ines",
    "ownerName": "Ines Carvalho",
    "href": "/dashboard/customers/deals/deal_1",
    "_rule": {
      "id": "rul_1",
      "name": "Tell finance"
    },
    "_workspace": {
      "name": "Acme"
    }
  }
}

Verify the signature

The signature is an HMAC-SHA256 of {t}.{raw body} with the rule’s signing secret (whsec_…, shown when you create or rotate it). Compare in constant time, and reject old timestamps to stop replays.

import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret: string, header: string, rawBody: string) {
  const t = /t=(\d+)/.exec(header)?.[1];
  const v1 = /v1=([0-9a-f]+)/.exec(header)?.[1];
  if (!t || !v1) return false;
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return expected.length === v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

Delivery

  • HTTPS on port 443 only. Private and loopback addresses are refused, and redirects are never followed.
  • Answer with any 2xx within 8 seconds. Failures are retried with backoff, up to 3 attempts.
  • Bodies are at most 64 KB and carry ids, names and dates, never message contents.

Events

EventFires whenPayload fields
offer.signedan offer or agreement is signedagreementId, memberId, memberName, kind, title
employment.updatedsomebody's employment record changesmemberId, memberName, fields
member.joinedsomebody joins the workspacememberId, memberName, role, jobTitle, href
member.offboarding.startedoffboarding starts for somebodymemberId, memberName, lastDay
leave.requestedsomebody requests leaveleaveId, memberId, memberName, leaveType, startDate, endDate, days, href
leave.decideda leave request is approved, declined or cancelledleaveId, memberId, memberName, leaveType, startDate, endDate, days, href, decision, deciderName
policy.publisheda policy is publishedpolicyId, title, version, href
policy.ack.overduesomebody has not acknowledged a policy by its due datepolicyId, title, memberId, memberName, dueDate, href
profile_request.createdan admin asks somebody to complete their detailsrequestId, memberId, memberName, requestKind, dueOn
profile_request.overduea request for somebody's details is overduerequestId, memberId, memberName, requestKind, dueOn
timesheet.submitteda timesheet is submitted for approvalsheetId, memberId, memberName, weekStart, href
timesheet.approveda timesheet is approvedsheetId, memberId, memberName, weekStart, deciderName, href
expense.submittedan expense claim is submittedclaimId, memberId, memberName, title, href
expense.approvedan expense claim is approvedclaimId, memberId, memberName, title, deciderName, href
invoice.sentan invoice is sentinvoiceId, number, clientName, amountMinor, currency, href
invoice.paidan invoice is paidinvoiceId, number, clientName, amountMinor, currency, href
invoice.overduean invoice goes overdueinvoiceId, number, clientName, amountMinor, currency, href
deal.createda deal is createddealId, title, companyName, stage, fromStage, ownerId, ownerName, href
deal.stage_changeda deal moves to another stagedealId, title, companyName, stage, fromStage, ownerId, ownerName, href
deal.wona deal is wondealId, title, companyName, stage, fromStage, ownerId, ownerName, href
deal.losta deal is lostdealId, title, companyName, stage, fromStage, ownerId, ownerName, href
issue.createdan issue is createdissueId, title, projectId, projectName, projectSlug, status, fromStatus, priority, assigneeId, assigneeName, actorName, href
issue.status_changedan issue changes statusissueId, title, projectId, projectName, projectSlug, status, fromStatus, priority, assigneeId, assigneeName, actorName, href
issue.assignedan issue is assigned to somebodyissueId, title, projectId, projectName, projectSlug, status, fromStatus, priority, assigneeId, assigneeName, actorName, href
project.stage_changeda project moves to another stageprojectId, projectName, projectSlug, stage, fromStage, href
approval.requesteda client approval is requestedapprovalId, projectId, projectName, projectSlug, title, href
approval.decideda client approves or asks for changesapprovalId, projectId, projectName, projectSlug, title, decision, clientName, href
feed.post_createdsomebody posts to the feedpostId, authorId, authorName, postKind, href