Webhooks
How inbound webhooks authenticate — RevenueCat Bearer token, Superwall HMAC-SHA256, and the generic retry contract.
Amba accepts inbound webhooks from third-party platforms on /webhooks/:provider?project_id=.... Every endpoint verifies authenticity before parsing JSON, using timingSafeEqual so secret length cannot leak via timing side-channels. Unverified requests get 401.
Today there are two inbound webhook providers: RevenueCat (subscription events) and Superwall (paywall events). Each has its own auth mechanism mirroring the provider's wire format.
RevenueCat
RevenueCat authenticates webhooks with a bearer token you choose at integration-configure time.
Endpoint
The secret is what you passed as config.webhook_secret to POST /admin/integrations.
Verification (server-side)
Retry contract
On success Amba returns 200 { "data": { "received": true } }. If background processing can't accept the event, the endpoint returns 500 so RevenueCat retries per its webhook contract. Swallowing the failure as a 200 would cause silent event loss, so Amba deliberately surfaces the problem.
Superwall
Superwall signs the raw body with HMAC-SHA256 using a shared secret, and sends the hex digest in x-superwall-signature.
Endpoint
Verification (server-side)
Reading the raw body before JSON parsing is critical — HMAC validates the bytes on the wire, not a re-serialized object. Parsing first would produce a different digest and invalidate every request.
Engagement mirror
Superwall events that include user_id + event are mirrored into your project's engagement_events table as superwall_<event> so paywall interactions feed into streaks, XP rules, and segment membership alongside first-party events.
Rate limits
Each provider has a per-project, per-second rate limit (bucket keyed by project_id query param):
| Provider | Limit |
|---|---|
| RevenueCat | 100/sec |
| Superwall | 100/sec |
Bursts above the limit return 429. Providers batch events before delivery, so sustained load above this ceiling is more likely replay / abuse than normal traffic.
Error codes
| Code | HTTP | Meaning |
|---|---|---|
MISSING_PROJECT | 400 | project_id query param was not provided |
NOT_CONFIGURED | 404 | No active integration row for this project |
INVALID_SIGNATURE | 401 | Header missing or failed timingSafeEqual |
INVALID_BODY | 400 | Malformed JSON (after signature verified) |
WEBHOOK_PROCESSING_FAILED | 500 | Background processing could not accept the event (retry-safe for RC) |
Configuration
Both providers are configured identically, via the admin API:
The API responds with a webhook_url containing the exact URL to register in the provider dashboard. Push providers (apns, fcm) don't have webhooks and get no webhook_url — they only send.
Rotating a secret
The new secret takes effect immediately. Update the provider's webhook setting to match before rotating — mismatched secrets produce 401 INVALID_SIGNATURE until both sides agree.
Outbound webhooks
There is no first-class outbound webhook feature yet. Applications that need to dispatch webhooks from Amba events should subscribe to engagement events via a server-side consumer and dispatch themselves.
Next
- RevenueCat integration — end-to-end subscription sync.
- Superwall integration — end-to-end paywall wiring.
- Entitlements — what subscription state looks like in Amba.