Webhooks
AppDNA sends HTTP POST requests to your server when events occur in your app. Webhooks let you sync data to your backend, trigger workflows, update CRMs, feed events into data warehouses, or power any custom integration.Overview
When you create a webhook endpoint in the AppDNA dashboard, you choose which event types to subscribe to. When a matching event occurs, AppDNA delivers a signed JSON payload to your endpoint URL via HTTP POST. Every delivery includes an HMAC-SHA256 signature so you can verify the payload originated from AppDNA and was not tampered with.Setup
1
Navigate to Webhooks
Go to Console > Settings > Webhooks in the AppDNA dashboard.
2
Add an endpoint
Click Create Endpoint and enter your HTTPS URL. Non-HTTPS URLs are not supported in production.
3
Select event types
Choose which event types this endpoint should receive. You can select individual events or subscribe to all events.
4
Save and copy the signing secret
After saving, the dashboard displays your signing secret. Copy it immediately — it is only shown once. You will use this secret to verify webhook signatures.
Event Types
AppDNA supports 16 webhook event types organized by category:Onboarding
Surveys
Billing
Push
In-App Messages
Journeys
Experiments
SDK
System
Payload Structure
Every webhook payload follows a consistent structure:The
data object contents vary by event type. Refer to the specific event type documentation in the dashboard for the full schema of each event.Payment completed data
A store-driven webhook is recorded in the same database write as the store notification it belongs to, and handed to
webhook delivery once that write has committed: on the App Store before AppDNA’s other follow-up work (the entitlement
sync and the internal billing event), on Google Play after the entitlement sync and before the internal events. It is marked
sent only when its delivery records are stored, so it is not lost when the hand-off or the work after it fails: the
store redelivers the notification and a webhook still pending is handed off then. This is at-least-once: if the hand-off
succeeded but marking it sent did not, the redelivery hands it off again, as a new delivery with a new
id — so your
endpoint can, rarely, see the same store event twice (for payment.completed, the same data.transaction_id), never
none — unless it is still undelivered 24 hours after it was recorded, or every allowed attempt was started and the
last one never finished, in which case it is marked failed (see Retry Policy) and can be retried by
hand.
Purchases that reach AppDNA only through an inbound provider webhook (RevenueCat, Adapty and similar) send no payment.completed.
Signature Verification
Every webhook request includes anx-appdna-signature header containing an HMAC-SHA256 signature of the raw request body. Always verify this signature before processing the payload.
The signature is computed as:
sha256=:
Verification Examples
- Node.js
- Python
- Ruby
Retry Policy
If your endpoint returns a non-2xx status code or does not respond within 30 seconds, AppDNA retries the delivery with exponential backoff:
After the initial attempt plus 4 retries (5 attempts total), that delivery is marked as failed. You can view failed deliveries and manually retry them from the dashboard.
A delivery whose first attempt never started (the AppDNA process that recorded it stopped before sending it) is sent by
the retry schedule once it has waited 5 minutes, and that send is its first attempt; if the process stopped in the
middle of the first send, the retry schedule’s send is its second. The attempts toward the 5 are counted like this: the
first when the delivery is recorded, each retry when it is scheduled after a failed attempt, and a send that the sending
process stopped in the middle of when the retry schedule picks the delivery up again — so such a send still counts, and
no delivery is ever sent more than 5 times: once all 5 have started and the last one never finished, the delivery is
marked failed. One AppDNA process sends a given attempt: it re-confirms that it still holds the delivery just before
sending and records the result only while it still does, so two overlapping retry runs never send the same delivery
twice. A test delivery (sent once, never retried) whose one send recorded no answer is marked failed after 5 minutes.
The delivery log shows the attempt the delivery is on, or waiting for, as
n/5 (a test delivery, sent once and never
retried, as 1/1). One that is still pending or waiting to retry 24 hours after it was recorded or last
retried by hand is marked failed too, instead of being delivered late. Either way the reason is in its delivery log, and
it says which case it was: never sent (Never sent: …), attempted and still waiting to retry (Given up: attempted, and still waiting …),
attempted with the last attempt never finished, so there is no answer to show (Given up: attempted, and the last attempt never finished …),
or out of attempts (Given up: every allowed attempt was started, … or Given up: no attempt is left, …);
retry it by hand to send it (that starts a new 5 attempts and a new 24 hours).
After 50 consecutive failures across any events, the webhook endpoint is automatically disabled. You can re-enable the endpoint from the dashboard after fixing the issue.
Best Practices
-
Respond with 2xx quickly. Return a
200 OKas soon as you receive the payload. Process the event asynchronously in a background job or queue. If your endpoint takes too long to respond, the delivery will be marked as failed and retried. -
Verify signatures on every request. Never process a webhook payload without verifying the
x-appdna-signatureheader. This protects you from forged requests. -
Handle idempotency. Webhook deliveries can be retried, which means your endpoint may receive the same event more than once. Use the
idfield to deduplicate events. Store processed event IDs and skip duplicates. -
Use HTTPS. AppDNA only delivers webhooks to HTTPS endpoints, in every environment (sandbox and development included); an
http://URL is refused when you save the endpoint. The host must resolve to a public address: a URL that does not (a private or loopback address, an internal host name, a host that does not resolve) is refused when you save the endpoint, with the reason, and the same check runs again before every delivery. A redirect is not followed (it counts as a failed delivery). During development, use a tunneling tool like ngrok, which gives a local endpoint a public HTTPS URL. - Monitor delivery health. Check the webhook delivery logs in the dashboard periodically. If you see a pattern of failures, investigate your endpoint’s availability and response times.
To test webhooks locally during development, use a tunneling tool like ngrok and point your webhook endpoint to the tunnel URL. Sandbox webhooks (
adn_test_ environment) are fully functional and isolated from production.
