Webhooks — Get Notified When Things Happen in CRMown
Webhooks send an HTTPS POST to your URL when something happens — a contact is created, a deal is won, a form is submitted. Each delivery is signed so you can verify it came from CRMown.
Steps
- Settings → Integrations → Developer → Webhooks → add an endpoint: your HTTPS URL and the events you want.
- Copy the signing secret (starts with whsec_). Rotate it any time from the same screen.
- Click Test to send a "ping" event, and use the delivery log to see each attempt, its status code and response.
- In your receiver, verify the X-Webhook-Signature header against the raw request body, then return any 2xx status within 10 seconds.
Payload shape
{
"event": "contact.created",
"timestamp": "2026-10-10T14:03:22.115Z",
"source": "app",
"data": { "contact": { "id": "…", "firstName": "Ada", "email": "ada@example.com" } }
}Verify the signature (Node.js)
import crypto from 'node:crypto'
// rawBody = the exact request body string, BEFORE JSON parsing
const expected = crypto.createHmac('sha256', process.env.CRMOWN_WEBHOOK_SECRET).update(rawBody).digest('hex')
const received = req.headers['x-webhook-signature']
const ok = received && expected.length === received.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))Good to know
- Events — contacts: contact.created, contact.updated, contact.deleted, contact.stage_changed, contact.score_changed, contact.tag_added. Deals: deal.created, deal.updated, deal.won, deal.lost, deal.stage_changed. Messages: message.sent, message.received, conversation.created. Tasks: task.created, task.completed. Bookings: booking.created, booking.cancelled. Forms: form.submitted. CRMown AI: ai_action.created, ai_action.approved, ai_action.rejected. Tracking: tracking.email_opened, tracking.link_clicked, tracking.page_visited. Nonprofit: nonprofit.verification_recorded, nonprofit.activated.
- "source" tells you what caused the event: app, api, zapier, import, sync, automation, ai, inbound, system or partner — useful to ignore changes your own integration made.
- Headers on every delivery: X-Webhook-Signature (hex HMAC-SHA256 of the raw body, using your secret — no "sha256=" prefix), X-Webhook-Event, X-Webhook-Timestamp, X-Webhook-Id, User-Agent: CRMown-Webhooks/1.0.
- Retries: a non-2xx or timeout is retried up to 5 attempts in total — about 30 seconds, 2 minutes, 8 minutes and 32 minutes after the first try.
- X-Webhook-Id is new on every attempt, so don't use it to de-duplicate — use the event name, timestamp and the record id inside data.
- An endpoint is switched off after 10 failures in a row, or straight away if its URL points at a private/internal address. Fix the receiver, then turn it back on in the list.
- Up to 1,000 deliveries per hour per workspace. Events beyond that in the same hour are not delivered, so use the API to reconcile after very large bulk operations.
- The test ping looks like { "event": "ping", "timestamp": …, "data": { "message": "This is a test webhook from CRMown" } } and has no "source".
Troubleshooting
Signature never matches
The signature is computed over the exact raw body; re-serialising parsed JSON changes spacing and key order.
Fix: Verify against the raw request body bytes (e.g. express.raw() / request.text()) before parsing.
The endpoint shows as disabled
It failed 10 times in a row, or its URL resolves to a private address.
Fix: Check the delivery log for the error, fix the receiver or URL, then re-enable it — re-enabling resets the failure count.