Amba

Webhooks

Provider-verified callbacks for RevenueCat subscription events and Superwall paywall events.

Webhooks live at the top level (not under /client or /admin) because they're authenticated by provider-specific signatures, not by API keys or developer tokens. The project_id a given payload belongs to is supplied as a query-string parameter.

Both providers:

  • Verify signatures with timingSafeEqual (no length-based timing leaks).
  • Require the project to have an active integration for the provider with a configured webhook_secret.
  • Rate-limit per project: 100 requests / second (bucket key = project_id).

POST /webhooks/revenuecat

Receive and process a RevenueCat subscription event (initial purchase, renewal, cancellation, billing issue, etc.). Processing happens asynchronously — the handler verifies the request, queues the event for processing, and returns.

Request

POST /webhooks/revenuecat?project_id=<uuid>
Authorization: Bearer <webhook_secret>
Content-Type: application/json
PieceWhereDescription
project_idquery stringThe Amba project the event belongs to.
Authorization: Bearer <secret>headerMust exactly equal the webhook_secret configured in the project's revenuecat integration. Constant-time compared.
JSON bodybodyRevenueCat's standard WebhookEvent payload. Forwarded unmodified to the processor.

Response 200

{ "data": { "received": true } }

Retry semantics

RevenueCat treats any 2xx as "delivered" and never retries. Any 5xx triggers retries on RevenueCat's side. We return 500 if processing can't be queued so the event isn't silently dropped.

Errors

  • 400 MISSING_PROJECTproject_id query param missing.
  • 401 INVALID_SIGNATUREAuthorization header missing or wrong secret.
  • 404 NOT_CONFIGURED — project has no active revenuecat integration or no webhook_secret on file.
  • 429 — rate-limited (100/sec per project).
  • 500 WEBHOOK_PROCESSING_FAILED — processing could not be queued. RevenueCat will retry.

Called by RevenueCat — you can't invoke this directly. The curl below shows the expected shape for local testing.

Curl:

# Called by RevenueCat — you don't call this directly.
curl -X POST '${BASE_URL}/webhooks/revenuecat' \
  -H 'Authorization: Bearer ${WEBHOOK_SECRET}' \
  -H 'Content-Type: application/json' \
  -d '{}'

POST /webhooks/superwall

Receive a Superwall paywall event. If the payload includes event + user_id, Amba writes a single engagement event (event_name = "superwall_<event>", full payload stored in properties). No follow-on processing fires.

Request

POST /webhooks/superwall?project_id=<uuid>
x-superwall-signature: <hex-hmac-sha256-of-raw-body>
Content-Type: application/json
PieceWhereDescription
project_idquery stringAmba project.
x-superwall-signatureheaderHex-encoded HMAC-SHA256 of the raw request body using the project's webhook_secret. Verified BEFORE JSON parsing so malformed/forged payloads never reach any side effects.
JSON bodybodySuperwall event object. Minimum fields the handler reads: event (string), user_id (app user id). Any other fields are stored verbatim in the engagement event's properties.

Response 200

{ "data": { "received": true } }

Retry semantics

Superwall retries on non-2xx. The handler returns 200 even if the engagement-event INSERT fails (non-fatal, logged) to avoid replay loops for write-level issues.

Errors

  • 400 MISSING_PROJECTproject_id missing.
  • 400 INVALID_BODY — body is not valid JSON.
  • 401 INVALID_SIGNATUREx-superwall-signature missing or doesn't match the HMAC of the raw body.
  • 404 NOT_CONFIGURED — project has no active superwall integration or no webhook_secret.
  • 429 — rate-limited.

Called by Superwall — you can't invoke this directly. The curl below shows the expected shape for local testing.

Curl:

# Called by Superwall — you don't call this directly.
curl -X POST '${BASE_URL}/webhooks/superwall' \
  -H 'x-superwall-signature: ${HMAC_SHA256_HEX}' \
  -H 'Content-Type: application/json' \
  -d '{}'

Configuring the secret

Both providers take their shared secret from the active integration on your project. Use POST /admin/projects/:projectId/integrations to create or update the integration; supply the secret as config.webhook_secret.

On this page