Webhooks
Avero POSTs a signed JSON payload to your endpoint whenever CRM records change — created, updated or deleted. Manage subscriptions in your workspace under Settings → Personal preferences → API.
Events
Subscribe to any combination of these events. A ping event is sent by the Test button.
deals.createddeals.updateddeals.deletedpersons.createdpersons.updatedpersons.deletedorganizations.createdorganizations.updatedorganizations.deletedactivities.createdactivities.updatedactivities.deletednotes.creatednotes.updatednotes.deletedDeal updates that only touch board ordering (position and other derived fields) are suppressed. Every update payload carries changed_fields so you can filter further.
Payload
data is the full record as stored. For *.deleted events it is the record as it was before deletion.
{
"id": "184",
"event": "deals.updated",
"created_at": "2026-07-15T09:12:33.412Z",
"data": {
"id": "9b2f7e34-1c5d-4a8e-b7f0-3d2a6c9e8f10",
"title": "Horvat–Marin wedding — 40-room block",
"stage": "proposal",
"…": "…"
},
"changed_fields": [
"stage",
"updated_at"
]
}Verifying signatures
Every delivery carries an x-avero-signature header of the form t=<unix>,v1=<hex>, where v1 is the HMAC-SHA256 of `${t}.${rawBody}`using your endpoint's signing secret (shown once when you create the webhook). Always verify before trusting a payload:
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyAveroSignature(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=")));
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (age > 300) return false; // reject replays older than 5 minutes
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Delivery & retries
Delivery is at-least-once — rare duplicates are possible, so deduplicate on the payload id. Your endpoint should respond with a 2xx within 8 seconds. Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours (6 attempts total), then dropped. After 20 consecutive failed retries the subscription is disabled automatically (first attempts don't count, so a brief outage never disables you) — re-enable it from Settings once your endpoint is healthy.
Events from API writes usually arrive within a second; events from in-app edits and imports arrive within a minute. Deletions are only visible through webhooks — polling with updated_sincecan't see removed records, so pair the two for a full sync.