Skip to main content

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 HTTPS POST 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

  1. Create a subscription with POST /v1/webhooks. Choose an event type and your HTTPS endpoint URL.
  2. 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.
  3. In your endpoint, read the raw request body, then verify the signature using the secret. See verifying signatures below.
  4. Return a 2xx response within 10 seconds. See the retry policy for responses that Aura retries.
  5. 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 include data.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, attribution is null.
  • 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 attribution for 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 as theme.
  • 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 attribution is not a breaking change.
The flat fields (utm_source, utm_medium, and the others) stay in every payload, so existing integrations keep working without changes. Every lead payload carries consent: 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.
  • agreed is 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.
  • phone is the number they agreed for. If it is not the lead’s phone now, agreed is false. It is null for a choice made before Aura kept the number, and that choice counts for the lead’s phone now.
  • state says what happened. granted means they ticked the box. withdrawn means they agreed, then took it back, by unticking the box or replying STOP. declined means they finished the form without ticking the box.
  • source says 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) or import. It is null when Aura did not record how.
  • at is when they made that choice. For a withdrawn box, granted_at is when they first agreed. It is null for every other state.
  • label, disclosure and links are 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 on label. id stays the same when your team changes the wording: it is the box’s own id for a box you added, aura_sms for Aura’s box for appointment texts, and data_consent for the data-processing box. kind says 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 declined entry yet.
  • Consent can change after the webhook is sent. For example, the person may reply STOP. Read the lead again with GET /v1/leads/:id right before you text them.
  • The phone number is sent either way. Aura never hides it. Check consent before you text.
The REST API returns the same 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: 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.
During the 24-hour secret rotation grace window, the header will carry two 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):
During rotation it becomes 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

Then in your handler (Next.js / Hono / Express all the same pattern):

Python signature verification

Why the raw body?

HMAC must cover the exact bytes Aura signed. If you JSON.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 most 4xx 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 in 169.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.timingSafeEqual in Node, hmac.compare_digest in 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_message for 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:
  1. Call POST /v1/webhooks/{id}/rotate-secret. Aura generates a new secret and shows it to you once.
  2. For the next 24 hours, Aura sends both the new and old signatures in X-Aura-Signature-V1 (two v1= segments) and X-Aura-Signature (comma-separated).
  3. Deploy the new secret to your endpoint. Your existing verification code continues to work during the grace window because it accepts either signature.
  4. 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, in webhook_delivery_logs with its status, HTTP status code, response time, attempt count, and (sanitized) error message. Retrieve delivery attempts programmatically:
Logs are retained for 30 days.

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