Overview#
Webhooks allow your application to receive real-time notifications when events occur in AutomateNexus CRM. Instead of polling the API for changes, webhooks push data to your endpoint as events happen, reducing latency and API usage. Every delivery is signed, failed deliveries are retried, and each webhook keeps a delivery log you can inspect in the app. Webhooks are created and managed under Settings → Integrations → Developer → Webhooks; there is no REST endpoint for managing them. This guide covers creating a webhook, event types, the payload format, signature verification, retries, and troubleshooting.
Create a Webhook#
- Go to Settings → Integrations → Developer and scroll to Webhooks.
- In the Create webhook card, fill in:
- Name (required) — A label for the webhook, for example "Zapier — new leads".
- Endpoint URL (required) — The URL that will receive the POST requests. It must start with
http://orhttps://; use HTTPS in production. - Events (required) — Tick at least one event. Events are grouped by Contacts, Deals, Customers, Companies, Tasks, and Forms.
- Description — Optional.
- Click Create webhook. The Webhook signing secret dialog shows the secret (it starts with
whsec_). Copy it now: it is stored encrypted and cannot be shown again, only rotated.
An organization can have up to 50 webhooks.
Manage Webhooks#
The Active webhooks list shows each webhook's name, an Active or Inactive badge, a badge counting consecutive failures if there are any, the URL, the subscribed events, and the time of the last successful and last failed delivery. Each row offers:
- Test — Sends a real, signed test delivery (see Test a Webhook).
- Deliveries — Opens the delivery log (see Delivery Log).
- The key icon — Rotate signing secret. The new secret is shown once; the old one stops working immediately.
- The on/off switch — Enables or disables the webhook. While a webhook is off, its events are not queued.
- The trash icon — Deletes the webhook and its delivery log.
Available Event Types#
Contact Events#
- contact.created — A contact is created.
- contact.updated — A contact is updated.
Deal Events#
- deal.created — A deal is created.
- deal.updated — A deal is updated.
- deal.stage_changed — A deal moves to a different stage. A stage change also counts as an update, so a webhook subscribed to both events receives both.
Customer Events#
- customer.created — A customer is created.
- customer.updated — A customer is updated.
Company Events#
- company.created — A company is created.
- company.updated — A company is updated.
Task Events#
- task.created — A task is created.
- task.updated — A task is updated.
Task events fire for the CRM tasks that AI agents and meeting action items create. Project tasks — the ones under Projects → Tasks and in the REST API's /api/tasks resource — do not trigger them.
Form Events#
- form.submitted — A form receives a submission.
The .updated events fire on any change to the record. There are no delete events. Test deliveries use the event name webhook.test.
Webhook Payload Format#
Every delivery is a POST request with a JSON body. data holds the full record as it is after the event; previous holds the record as it was before, for .updated and deal.stage_changed events, and is null otherwise. object names the kind of record: contacts, deals, customers, companies, tasks, or form_submissions.
{
"event": "deal.stage_changed",
"occurred_at": "2026-03-21T10:30:00.418512+00:00",
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"object": "deals",
"data": {
"id": "6d2f8b4a-1c3e-4f5a-9b7c-8d9e0f1a2b3c",
"title": "Enterprise License - Acme Corp",
"value": 75000,
"currency": "USD",
"stage": "negotiation",
"probability": 60,
"customer_id": "8a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"updated_at": "2026-03-21T10:30:00.418512+00:00"
},
"previous": {
"id": "6d2f8b4a-1c3e-4f5a-9b7c-8d9e0f1a2b3c",
"title": "Enterprise License - Acme Corp",
"value": 75000,
"currency": "USD",
"stage": "proposal",
"probability": 50,
"customer_id": "8a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"updated_at": "2026-03-18T15:30:00.000000+00:00"
}
}Every delivery carries these headers:
- X-Webhook-Id — The delivery's ID. It stays the same across retries of the same delivery, so use it to deduplicate.
- X-Webhook-Event — The event name.
- X-Webhook-Timestamp — Unix time in seconds when the delivery was signed.
- X-Webhook-Signature — The signature, described below.
- User-Agent —
AutomateNexusCRM-Webhooks/1.0.
Signature Verification#
Every delivery includes an X-Webhook-Signature header of the form t=<timestamp>,v1=<signature>. The signature is the hex-encoded HMAC-SHA256, keyed with your webhook's signing secret, of the timestamp, a period, and the raw request body. Always verify it to ensure the payload is authentic and has not been tampered with.
X-Webhook-Signature: t=1731430000,v1=8f2a1c...How to Verify#
- Split the header on commas and read
tandv1. - Take the raw request body exactly as received (before any JSON parsing).
- Compute HMAC-SHA256 over the string
<t>.<raw body>using your signing secret, hex-encoded. - Compare the result with
v1using a constant-time comparison.
Node.js Example#
const crypto = require('crypto');
function verifyWebhook(rawBody, signatureHeader, secret) {
// signatureHeader looks like "t=1731430000,v1=8f2a1c..."
const parts = Object.fromEntries(
signatureHeader.split(',').map((kv) => kv.split('='))
);
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(parts.v1, 'hex')
);
}Python Example#
import hmac
import hashlib
def verify_webhook(raw_body: bytes, signature_header: str, secret: str) -> bool:
parts = dict(kv.split("=", 1) for kv in signature_header.split(","))
message = parts["t"].encode() + b"." + raw_body
expected = hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])Retry Logic#
A delivery counts as failed when your endpoint returns a non-2xx status code or does not respond within 10 seconds. AutomateNexus CRM makes up to four attempts per delivery — the first attempt and three retries — with growing delays:
- Retry 1: After 1 minute
- Retry 2: After 5 minutes
- Retry 3: After 30 minutes
Retries are picked up by a background job that runs every five minutes, so a retry can arrive a few minutes after its scheduled time. The first attempt is made right after the event; if that send is lost, the same job picks the delivery up within a few minutes.
After the last failed attempt the delivery is marked failed and the webhook's consecutive-failure count goes up; it is shown as a badge in the list and reset by the next successful delivery. AutomateNexus CRM does not turn a webhook off on its own; use the switch in the list if you need to pause it.
Delivery Log#
Click Deliveries on a webhook to open its delivery log. It shows the latest 50 deliveries; rows older than 30 days are removed automatically. Each row shows the event, its status (Pending, Delivered, or Failed), the HTTP status code and response time of the last attempt, the number of attempts made out of the maximum, and when it was created. Expand a row to see the last error, the time of the next retry for a pending delivery, every attempt with its status code, response time, error, and the first 500 characters of your endpoint's response, and the exact payload that was sent.
Test a Webhook#
Click Test on a webhook to send a real, signed delivery of the webhook.test event to its URL. A test is a single attempt with no retries, and it appears in the delivery log like any other delivery. The app reports the HTTP status and response time your endpoint returned.
{
"event": "webhook.test",
"occurred_at": "2026-03-21T10:30:00.000Z",
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"object": "webhook_subscriptions",
"data": {
"subscription_id": "c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
"message": "Test delivery from AutomateNexus CRM. If you can verify the signature, you are set up correctly.",
"test": true
},
"previous": null
}Best Practices#
- Respond quickly: Return a 2xx status code well within the 10-second limit. Process the webhook payload asynchronously (e.g., add to a queue) rather than performing long operations in the request handler.
- Handle duplicates: Use the
X-Webhook-Idheader to deduplicate. A retried delivery carries the same ID, and the same event may occasionally be delivered more than once. - Use HTTPS: Webhook URLs should use HTTPS to protect payload data in transit.
- Monitor the delivery log: Check Deliveries and the consecutive-failure badge regularly to catch failures early.
- Rotate secrets: Rotate the signing secret if it may have leaked. The old secret stops working immediately, so update your verification code at the same time.
Troubleshooting#
- Not receiving webhooks: Check that the webhook is Active, that the event you expect is among its subscribed events, and that the endpoint URL is publicly reachable and accepts POST requests. Open Deliveries: if deliveries are listed as failed, the error column tells you why; if nothing is listed, the event has not fired.
- Signature verification failing: Compute the HMAC over
<t>.<raw body>, not over the body alone, and use the raw request body rather than re-serialized JSON. Make sure you are using the current secret — rotating it invalidates the previous one. - Task events not arriving: Project tasks created under Projects → Tasks or through the REST API do not trigger
task.createdortask.updated; only the CRM tasks created by AI agents and meeting action items do. - Consecutive failures keep growing: Fix the endpoint, then click Test; a successful delivery resets the count.
- Older deliveries missing from the log: The log keeps the latest 50 deliveries and removes rows older than 30 days.