Skip to content

Webhooks

Athena receives two categories of inbound webhooks: RevenueCat (subscription billing events) and media generation (Fal.ai async results). Your server only needs RevenueCat if your workspace uses the credit billing system.


RevenueCat Webhooks

Setup

Configure RevenueCat to send webhooks to:

POST https://athena-dev-api.leanscale.com/webhooks/{workspace_id}/revenuecat

In Workspace Settings → Subscription Providers, copy the RevenueCat Webhook URL and set the Webhook Secret. RevenueCat sends the secret as a bearer token in the Authorization header. Athena validates it with a constant-time comparison.

Also configure the RevenueCat Project ID and Secret REST API Key in the same settings panel. Those values are used for RevenueCat v2 live subscription lookups after sign-in transfers, manual billing refreshes, and plan-change previews. Athena never needs the public SDK key on the server.

Athena fieldRevenueCat valueUsed for
Webhook URLAthena-generated URLRevenueCat webhook delivery
Webhook SecretRevenueCat webhook bearer secretVerifying inbound webhooks
Project IDRevenueCat project ID, e.g. proj...v2 /projects/{project_id}/customers/{customer_id}/subscriptions lookups
Secret REST API KeyRevenueCat secret REST API keyServer-side v2 customer subscription and product lookups

Authentication

POST /webhooks/{workspace_id}/revenuecat
Authorization: Bearer YOUR_WEBHOOK_SECRET
Content-Type: application/json

Missing or mismatched tokens return 401. Unconfigured secret returns 403.

Event Matrix

EventCredit actionSubscription state
INITIAL_PURCHASE (subscription)Grant first period (or first annual monthly tranche). Unfreezes all top-ups.Record subscription, status → Active
INITIAL_PURCHASE (consumable)Grant top-up credits. If no active sub, the grant lands in the frozen bucket.No change
RENEWALForfeit previous period grant, create new one. Idempotent unfreeze.No change
NON_RENEWING_PURCHASESame as consumable initial purchase.No change
PRODUCT_CHANGEInformative only. Never grants or modifies credits. Wait for the companion RENEWAL/INITIAL_PURCHASE.Record pending change (deferred transitions)
CANCELLATION (refund)Void the refunded grant(s) via rc_transaction_id. Balance clamps at zero.Status → Canceled if non-renewing
CANCELLATION (voluntary)No credit changeStatus → Canceled
EXPIRATIONFreezes all top-ups atomically. Subscription credits finish their period and then forfeit.Clear subscription
BILLING_ISSUENo credit changeStatus → BillingIssue, record grace period
UNCANCELLATIONNo credit changeStatus → Active
TRANSFERNo credit changeLive-sync each transferred_to customer from RevenueCat v2

Propagation to /my/credits

After every handled event Athena invalidates the customer-status cache (keyed by external customer ID). The next GET /my/credits — and every authenticated request from that customer — sees the updated bucket balances (active_credits / frozen_credits / inactive_credits) immediately, without waiting for cache TTL.

This applies to every event in the matrix above: purchases, renewals, expirations, cancellations, refunds, plan changes, and billing issues all propagate within a single Redis round-trip of the webhook’s DB write.

GET /my/credits is a local Athena read. It does not call RevenueCat. When the client needs to repair races after Purchases.logIn(...), restore, purchase, or plan change, call GET /my/billing/refresh. That endpoint performs a live RevenueCat v2 lookup first, updates Athena’s local entitlement state, and then returns the same balance shape as /my/credits.

Idempotency

RevenueCat may deliver the same event more than once. Athena deduplicates on event.id (preferred) or transaction_id. It is safe to receive duplicates.

Deduplication Key

Field checkedSource
event.idRevenueCat event envelope
transaction_idRevenueCat transaction object

Duplicate events return 200 without re-applying credit mutations.

Error Responses

HTTPCause
400Missing app_user_id, missing product_id, unknown product, invalid transfer
401Missing or invalid Authorization: Bearer token
403RevenueCat Webhook Secret not configured on workspace
503Credit service not available

Media Generation Webhooks (Fal.ai)

Athena registers webhooks with Fal.ai automatically when a MediaGeneration node submits a job. You do not configure these manually — they are internal to the platform.

When Fal.ai delivers the result, Athena:

  1. Validates the HMAC-SHA256 signature (5-minute timestamp tolerance)
  2. Injects ai_result:{node_id} into the execution variables
  3. Resumes the paused execution from checkpoint

Security

Fal.ai webhooks require these headers:

  • X-Fal-Signature — HMAC-SHA256 of the body
  • X-Fal-Timestamp — Unix timestamp (must be within 5 minutes)

Missing headers return 401. Expired timestamps return 401.

Retry Policy

If the execution is not in WaitingForExternalEvent state when the webhook arrives, Athena returns 400 bad_request. Fal.ai will retry delivery according to its own retry schedule.


General Webhook Best Practices

For webhooks your own server sends to Athena (RevenueCat integration):

  • Sign everything — use the webhook secret, rotate it periodically
  • Idempotency keys — include a stable event ID in the request for dedup
  • Timeouts — Athena processes RevenueCat webhooks within 10 seconds; do not configure RevenueCat timeout below 15 seconds

For webhooks Athena sends outward (Action nodes in workflows):

  • Your endpoint should return 2xx within 30 seconds (default timeout)
  • Maximum timeout is 10 minutes (configurable per Action node)
  • Athena includes an Idempotency-Key header when configured on the node
  • SSRF protection blocks requests to private IP ranges and cloud metadata endpoints

  • Credits — three-bucket credit ledger and RevenueCat event handling
  • Architecture — workflow engine MediaGeneration node
  • Execute — MediaGeneration SSE events in the streaming endpoint
  • Error Codes — webhook error codes