Webhooks (Outbound)
Webhooks let SalesOS call your system when something happens (a lead is offered, a ranking is published, a message is dispatched). You give us an HTTPS URL and a secret; wePOST a signed JSON
payload to it whenever a subscribed event fires.
This page covers outbound webhooks (SalesOS → your endpoint). For sending data into SalesOS,
see Activities and API Keys.
Configure a webhook in the Dashboard
Go to Integrations → Webhooks → New Webhook and fill in the form.Basic
- Name (required) — e.g.
Notify CRM. - Key — a stable identifier (e.g.
notify_crm). - Description — optional.
Events (triggers)
Pick a Category, then select the events that fire this webhook — this is your
subscription (see the catalog). Leave it empty for a webhook you only
trigger manually (the Test button) or from a workflow.
Destination
- Method —
POST(default),PUTorPATCH. - Timeout (seconds) — how long we wait for your
2xx(default 30). - URL (required) — your endpoint. Must be
https://and public (internal/loopback hosts are blocked). - Authentication — choose HMAC to sign every delivery, then in Auth Config (JSON) set your secret:
(Other schemes are available: Bearer, API Key, Basic, OAuth2.)
Advanced
- Custom Headers (JSON) — extra headers sent with every delivery.
- Payload Template (JSON with
{{ }}variables) — shapes the envelopedatafrom the event context, e.g.{ "id": "{{event.id}}", "type": "{{event.type}}", "to": "{{event.to}}" }.
The envelope
On the event path, every delivery is a single JSON object:| Field | Description |
|---|---|
id | Unique event id, stable across retries → your idempotency key. |
type | Event type (see catalog). |
version | Schema version of data. Evolves additively. |
ts | When the event occurred (ISO 8601, UTC). |
tenant | Origin tenant (uuid). |
data | Event-specific payload. |
Headers
| Header | Description |
|---|---|
X-SalesOS-Event | Event type — route without parsing the body. |
X-SalesOS-Event-Id | Equals id; stable across retries (idempotency). |
X-SalesOS-Delivery-Id | Per-attempt id (changes on retry; for debugging). |
X-SalesOS-Timestamp | Unix seconds — part of the signature (anti-replay). |
X-SalesOS-Tenant | Origin tenant id. |
X-SalesOS-Signature | sha256=<hmac> — see Verify. |
Verify the signature
On the event path, the signature covers{id}.{timestamp}.{rawBody} (Standard-Webhooks style),
so it authenticates the payload and the timestamp (anti-replay). Recompute the HMAC with your
secret, compare in constant time, and reject if the timestamp is more than 300s off.
Test deliveries (the “Test Webhook” button) and legacy workflow triggers currently use a simpler
scheme: the body is the raw rendered template (not the envelope) and the signature header is
X-Signature: <prefix><hmac(body)> — without the id.timestamp. prefix, so it has no
anti-replay. Prefer the event path above for production.Event catalog
type | When it fires | data |
|---|---|---|
lead.offer_created | A lead is offered to a broker | lead/opportunity payload |
lead.accepted | A broker accepts a lead | lead/opportunity payload |
lead.offer_expired | An offer expires before acceptance | lead/opportunity payload |
mission.completed | A mission is completed | mission payload |
ranking.weekly.published | Weekly ranking is published (scheduled) | leaderboard (rows by email) |
ranking.consolidated.published | Consolidated ranking is published | leaderboard (rows by email) |
message.dispatch | A message is dispatched to a user/group | { to, m } |
notification.push.dispatch | A push is dispatched to a user/group | { to, n } |
data for ranking.weekly.published:
Reliability
Respond fast. Return a2xx status quickly (before heavy processing) to acknowledge receipt.
| Your response | What SalesOS does |
|---|---|
2xx | Marks the delivery completed. No retry. |
4xx (400, 401, 422…) | Treated as a permanent rejection. No retry (except 429, which respects backoff). |
5xx / timeout / connection error | Retried with exponential backoff (2^n minutes, up to 5 attempts), then dead-lettered. |
X-SalesOS-Event-Id — it stays the same across retries.

