Webhooks
Aura offers two webhook setup paths. Choose the one that supports the events your integration needs.lead.interested and lead.qualified are not subscription options on the
Partner API’s /v1/webhooks endpoint. A Partner API key lets you retrieve the
lead’s intent and answers through Calls,
regardless of which webhook setup path you use.
Lead lifecycle events in Settings and Zapier
A lead is the person. A booking intent records their progress before they book a meeting. Each has its own ID. A lead can finish the form and qualify without booking a meeting.
Use
intent_id to request
GET /v1/calls/{id}/answers. The response
contains data.answers, with stable field_id values, question labels and
answers. If you only have a lead ID, list that lead’s intent calls
first. GET /v1/leads/{id} does not expose the intent ID or its answers.
The lifecycle payload’s form_answers array uses the same answer fields.
Ordinary question-label fields remain in the payload for existing integrations.
Use form_answers when you need stable field IDs or a question label matches a
reserved key such as intent_id.
Webhooks capture a point in time. Retrieve the intent again when you need
answers entered after the event fired.
Partner API webhook delivery
Partner API webhooks send signed HTTPSPOST requests to your endpoint.
Verify the signature, process the event and return a 2xx response. The setup,
signature format and retry policy below describe Partner API webhook deliveries.
Quick start
- Create a subscription with
POST /v1/webhooks. Choose an event type and your HTTPS endpoint URL. - Aura shows you a signing secret once. Copy it into a secure store on your side, such as an environment variable or secret manager. You cannot retrieve it again. Rotate it if you lose it.
- In your endpoint, read the raw request body, then verify the signature using the secret. See verifying signatures below.
- Return a
2xxresponse within 10 seconds. See the retry policy for responses that Aura retries. - Deduplicate by your subscription ID and the payload’s
idempotency_key. The same event can arrive more than once. Retries keep the same key.
Partner API event catalog
Payload shape
Every delivery uses the same top-level wrapper:data carries the full entity with attribution fields (utm_source,
utm_medium, utm_campaign, booking_link_id, referral) so you don’t
have to call the API to figure out where a lead came from.
Example: call.booked
Form answers on call events
Partner call event payloads includedata.form_answers, the same normalized
answers returned by Calls.
These answers belong to that call or booking intent. Each entry includes
field_id, label and value, with an optional type. Values can be a string,
an array of strings or a boolean. Basic contact and internal fields are excluded.
New call event deliveries include an array, with [] when there are no custom
answers. Historical payloads may omit form_answers. Settings and Zapier lead
lifecycle events use the form_answers array described above; historical
lifecycle payloads may also omit it. Retrieve the call when you need its current
answers.
The attribution map
Every lead and call payload also carries attribution: one object holding
every attribution parameter Aura kept for that record. It includes the known
fields above and any other parameter your landing page passed, such as a
commerce or affiliate identifier (am_wuid in the example). Use it when you
need a value Aura has no dedicated field for.
- Values are strings. Aura only includes parameters that have a value.
When nothing was captured,
attributionisnull. - Booking events keep what was captured at booking. A call’s attribution comes from the booking itself. If the lead’s details change later, the attribution on that call does not. Where the booking did not capture a value, Aura fills it from the lead.
- A call reports the same map everywhere. Webhooks, the REST API,
GraphQL, and Zapier all return the same
attributionfor a given call. - Aura returns what your links pass. Every parameter in the booking URL
is kept and returned under its own name, whatever it is called. If a link
carries an email address or a token, it comes back in
attribution, so only put in your links what you are happy to receive in your webhooks. The only parameters left out are the embed’s own display settings, such astheme. - There are limits. Up to 25 parameters beyond the known fields are kept. Parameter names can be up to 100 characters and values up to 500. Longer values are shortened.
- New keys can appear at any time. Read the keys you need and ignore the
rest. A new key in
attributionis not a breaking change.
utm_source, utm_medium, and the others) stay in every
payload, so existing integrations keep working without changes.
Consent on lead events
Every lead payload carriesconsent: each consent box the person saw and
what they chose. This includes your own boxes on the booking form. Aura’s box
for appointment texts is in the list once the person has seen it, or replied
STOP or START. Use it to see which phone numbers agreed to get
texts before you send any. Phone companies and CRMs often ask for this
before they let you send texts.
agreedis true only while they agree, for the lead’s phone now. It is false if they never ticked the box, or took it back later. It is also false if they agreed for a different phone number.phoneis the number they agreed for. If it is not the lead’s phone now,agreedis false. It isnullfor a choice made before Aura kept the number, and that choice counts for the lead’s phone now.statesays what happened.grantedmeans they ticked the box.withdrawnmeans they agreed, then took it back, by unticking the box or replying STOP.declinedmeans they finished the form without ticking the box.sourcesays how the choice reached Aura:booking_form,sms_reply_stop,sms_reply_start,carrier_block(their phone company blocked texts),admin(someone on your team changed it) orimport. It isnullwhen Aura did not record how.atis when they made that choice. For awithdrawnbox,granted_atis when they first agreed. It isnullfor every other state.label,disclosureandlinksare what the person saw: the words on the box, the fine print beside it (Aura’s box for appointment texts has one), and the links next to it.- Match a box on
id, not onlabel.idstays the same when your team changes the wording: it is the box’s own id for a box you added,aura_smsfor Aura’s box for appointment texts, anddata_consentfor the data-processing box.kindsays which of the three it is. - An empty list means no consent is on file. That is not the same as
declined, but it is still not a yes. Treat it as no consent. - A box they did not tick is only recorded once they finish the form.
Someone who leaves halfway has no
declinedentry yet. - Consent can change after the webhook is sent. For example, the person
may reply STOP. Read the lead again with
GET /v1/leads/:idright before you text them. - The phone number is sent either way. Aura never hides it. Check
consentbefore you text.
consent on every lead, and GraphQL returns it
as Lead.consent (with grantedAt).
Verifying signatures
Aura signs every request with two headers so you can pick the verification scheme you prefer:X-Aura-Signature-V1 (recommended)
Stripe-style format:
t=<unix_ms>contains the delivery timestamp. You must reject requests whose timestamp drifts more than 5 minutes from your clock, or an attacker who captures one delivery could replay it indefinitely.v1=<sha256_hex>contains HMAC-SHA256 of the string${t}.${rawBody}, hex-encoded.
v1= segments. One uses the current secret and the other uses the previous
secret. Accept the request if either verifies. That lets you rotate secrets
without dropping in-flight deliveries.
X-Aura-Signature (legacy, deprecated)
Bare hex digest of HMAC-SHA256(rawBody):
current,previous. Split on , and accept if
either matches. This header is kept for backwards compatibility and offers
no replay protection. New integrations should verify X-Aura-Signature-V1.
X-Zapier-Signature (deprecated, do not rely on)
Subscriptions created through the booking funnel also receive
X-Zapier-Signature. Its value is the subscription secret itself, echoed
back to you.
It is not a signature. It proves nothing about the body, and it discloses a
working credential on every delivery: any proxy, APM, or request log between
Aura and your handler captures a secret that can be used to forge deliveries.
Every one of these deliveries now also carries X-Aura-Signature-V1.
Verify that instead. The raw-secret header will be removed once delivery
telemetry shows subscribers have migrated; comparing it to your stored secret
is not a safe substitute in the meantime.
Node.js signature verification
Python signature verification
Why the raw body?
HMAC must cover the exact bytes Aura signed. If youJSON.parse and then
re-stringify, key ordering or whitespace may drift and your signature will
silently fail to verify. Always capture the raw body first (await request.text()
in modern Node, request.body as a string/buffer elsewhere), verify, then
parse.
Retry policy
If your endpoint doesn’t return a 2xx within 10 seconds, Aura retries with exponential backoff.
Retries happen at
1s, 5s, 15s after the first failure (3 retries max,
4 attempts total). After the last retry, the delivery is marked failed in
webhook_delivery_logs. There is no automatic re-send. Fix your endpoint
and use POST /v1/webhooks/test to send a fresh
test delivery. To recover the missed events themselves, re-fetch the affected
records via the REST API (e.g. GET /v1/calls, GET /v1/payments) and
reconcile against your own store.
Permanent 4xx responses are NOT retried
Aura treats most4xx responses as permanent failures and does not retry.
Returning 404 because your endpoint path is wrong, or 401 because your
signature check rejected the request, tells Aura the delivery can never
succeed. Retrying waits another 21 seconds and creates noise in your logs.
If you want a retry, return 500 or 503.
Security model
- HTTPS only. Aura refuses to create subscriptions for non-HTTPS URLs.
- SSRF-guarded target URLs. Aura rejects subscriptions pointing at
localhost, any RFC1918 private range (10/8,172.16/12,192.168/16), link-local addresses in169.254/16, including AWS IMDS, GCP and Azure metadata endpoints, carrier-grade NAT, multicast, and broadcast ranges. DNS is re-resolved at delivery time to defend against DNS-rebinding attacks. - HMAC-SHA256. Signing secrets are 32 bytes of
crypto.randomBytes(256 bits of entropy). Compare signatures with a timing-safe comparison (crypto.timingSafeEqualin Node,hmac.compare_digestin Python). - One-time secret reveal. The signing secret is shown once at subscription create time and once at rotation time. If you lose it, rotate to generate a new one.
- Per-host concurrency limit. Aura caps outbound deliveries to a single partner hostname at 5 concurrent requests so a burst of events can’t accidentally DoS your endpoint.
- PII-scrubbed error logs. Your endpoint’s error response text is
stored in
webhook_delivery_logs.error_messagefor debugging, but Aura scrubs email addresses and phone numbers from the message and truncates to 500 chars before persisting.
Secret rotation
To rotate a secret without downtime:- Call
POST /v1/webhooks/{id}/rotate-secret. Aura generates a new secret and shows it to you once. - For the next 24 hours, Aura sends both the new and old signatures
in
X-Aura-Signature-V1(twov1=segments) andX-Aura-Signature(comma-separated). - Deploy the new secret to your endpoint. Your existing verification code continues to work during the grace window because it accepts either signature.
- After 24 hours, Aura stops sending the old signature. Deliveries signed only with the old secret will fail verification.
Delivery logs
Aura records every delivery attempt, successful or failed, inwebhook_delivery_logs with its status, HTTP status code, response time,
attempt count, and (sanitized) error message. Retrieve delivery attempts programmatically:
Webhooks vs. the Partner API
These two surfaces complement each other:
Most integrations want both. For example: on
lead.created react by
enriching the lead in your CRM; then call
GET /v1/leads/:id on-demand to pull the full
history and attribution chain.
Reference
POST /v1/webhooks- create a subscription programmaticallyGET /v1/webhooks- list subscriptionsPATCH /v1/webhooks/{id}- update target URLDELETE /v1/webhooks/{id}- delete a subscriptionPOST /v1/webhooks/test- send a test event to an endpoint