Webhook Security
Every outbound webhook includes anX-Revtain-Signature header containing an HMAC-SHA256 signature computed using your webhookSigningSecret.
Step-by-Step Verification
Store your webhook signing secret
Save
webhookSigningSecret securely. This is different from your API key.Verify every incoming request
Compute HMAC-SHA256 using your secret, compare with
X-Revtain-Signature.Code Examples
Always respond with
200 within 10 seconds. If your endpoint is down or slow, Revtain retries delivery automatically — three immediate attempts (0s, 2s, 8s), then, if those fail, a durable retry schedule over the following 24 hours. A brief outage on your side will not lose an event.Event Reference
recovery.success
Sent when a previously failed payment has been successfully recovered.
Correlating a webhook back to your invoice. When you call
/api/recovery/execute, the response includes a transactionId. Store it against your invoice. The webhook’s revtainTransactionId is the same value — match on it to know which invoice this outcome belongs to. If you prefer to drive everything from your own key, pass an idempotencyKey (your invoice ID) on /execute and poll GET /api/recovery/status?idempotencyKey=... instead of waiting on the webhook.recovery.success and recovery.failed may carry an optional trigger field identifying what produced the outcome — for example a scheduled retry, or "reconciliation" when Revtain’s automatic reconciliation resolved a payment whose original gateway response was lost in transit. Handle both events the same way regardless of trigger; the field exists for your telemetry.recovery.failed
Sent when all retry strategies have been exhausted.
recommendedAction (see table below) and run your own customer outreach from your domain — see the dunning guide. Revtain never emails or messages your customers directly.
recommendedAction values
recovery.failed and recovery.blocked both carry a recommendedAction field, so your dunning logic can branch on a stable signal instead of parsing raw decline codes. recommendedActionReason is a human-readable explanation of the same signal.
| Value | What to do |
|---|---|
retry_later | A soft or transient decline. A later retry — or a card update — may succeed. |
request_card_update | The card is expired, invalid, or needs customer authentication. Ask the customer to update or re-confirm their payment method. |
manual_review | The issuer flagged the card for fraud. Do not retry — review the account. |
monitor | The reason is ambiguous (for example a suspected duplicate). Verify before retrying. |
recovery.blocked
Sent when a transaction is stopped before any retry — either by the Risk Engine (fraud, lost card) or because the issuer requires the customer to authenticate.
recommendedAction. A manual_review value means flag the account and do not retry. A request_card_update value — for example when declineCode is authentication_required — means route the customer through your checkout to re-confirm their card.
card.updated
Sent when a customer updates their payment method via a hosted card update link.
| Field | Type | Description |
|---|---|---|
oldPaymentMethodToken | string | The token that was dead and triggered the update flow. Stop using it. |
newPaymentMethodToken | string | The new gateway-native token. Use this on all future recovery calls. |
paymentMethodType | string | card, apple_pay, google_pay, paypal, or venmo — what the customer chose on the update page. Pass this back as paymentMethodType on future /api/recovery/execute calls so the engine applies the right strategy. |
eligibleGateways | string[] | The list of your configured gateways the new token can be charged on. For card tokens this is usually every gateway; for wallet tokens it’s gateway-locked (the wallet sheet binds the token to whichever gateway received it). |
clientId | string | Your Revtain client ID — same value Revtain assigned at onboarding. |
timestamp | string | ISO 8601 timestamp of the update event. |
newPaymentMethodToken and paymentMethodType against the customer record in your system. Use them on all future POST /api/recovery/execute calls for this customer. The old token is no longer valid for recovery — discard it.
newPaymentMethodToken is a gateway-native token (for example a Stripe pm_xxx or a Checkout.com src_xxx), issued by the same gateway that holds the customer’s account. You can pass it to your gateway directly as well — it’s a real gateway token, not a Revtain reference.predict.risk.high
Sent when the pre-failure risk prediction engine detects a high-risk payment before it fails. Enables proactive intervention.
/api/recovery/update-card/generate.
recovery.skipped_high_risk
Sent when the predictor scored the failure so low that attempting recovery would burn issuer velocity budget without a realistic chance of success. Revtain skips the retry entirely and surfaces a pre-generated card update link so you can route the customer straight to re-entering payment details.
| Field | Type | Description |
|---|---|---|
riskScore | number | 0–100, where higher = more likely to fail. The configured skip threshold lives on your client record. |
confidence | number | 0–100 confidence in the prediction. Useful for filtering — a high score with low confidence is a weaker signal. |
reasoning | string | Human-readable explanation of why this card was skipped. |
cardUpdateUrl | string | A ready-to-send card update link the customer can use to provide new credentials. |
skippedChargeId | string | Internal ID for the skip event — quote this if you need to ask support about a specific decision. |
cardUpdateUrl to the customer via your existing channel (email, in-app message). Treating a skip as “the recovery failed” lets you maintain a clean state machine in your dunning logic.
recovery.holdout
Sent only on accounts running Proof Mode. The request landed in the measurement control group: Revtain made no recovery attempt and will not make one.
success: false, so your existing failure path already does the right thing.
recovery.proactive_retention
Sent when Revtain detects that a single customer has hit multiple recovery failures in a short window — a strong signal they’re at risk of churning. The event carries a pre-generated cancel-flow URL you can offer the customer to retain them with a pause, discount, or downgrade option before they actively cancel.
| Field | Type | Description |
|---|---|---|
customerEmail | string | The customer whose failure pattern triggered the proactive retention flag. |
recentFailureCount | number | How many failed recoveries this customer has had in the recent window. |
cancelFlowUrl | string | A hosted cancel-flow URL the customer can use to pause, downgrade, or cancel — your retention surface for at-risk customers. |
cancelFlowToken | string | The raw token (same one embedded in cancelFlowUrl) so you can build your own page if you’d rather host it yourself. |
reason | string | Plain-language explanation of why this was triggered — safe to surface in internal dashboards. |
cancelFlowUrl to the customer via your existing channel. Customers who reach this link voluntarily are dramatically more likely to retain (via pause / downgrade) than customers who are forced through the dunning gauntlet first. Requires the cancel-flow feature to be enabled on your account.
recovery.proactive_retention is only fired for clients with the cancel-flow feature enabled. If you don’t use cancel flow today, this event never fires — you can safely ignore it.card.expiring_soon
Sent ahead of a renewal when the customer’s card on file is close to its expiry date, so you can prompt the customer before the payment fails. Fired during a pre-renewal cascade for clients on DIRECT_GATEWAY mode (Stripe / Checkout.com).
Field names are
expiryMonth and expiryYear (not expMonth/expYear). Treat any additional fields as optional and forward-compatible.Pre-emptive variant (card health sweep)
Accounts with the card health sweep enabled also receivecard.expiring_soon from Revtain’s own monitoring — a routine background check of active cards, independent of any billing platform. This variant is marked preemptive: true and includes a ready-made card-update link:
cardUpdateUrl to the customer through your own channel. The link stays valid for 14 days. Updating the card before the renewal means the decline never happens — the cheapest recovery there is.
recovery.3ds_recommended
Sent after a failed recovery when the decline pattern suggests the charge would clear with 3-D Secure authentication — typically a soft decline from a European or UK issuer.
churn.flow.{outcome}
Sent when a customer completes the hosted cancel flow. The event name carries the outcome: churn.flow.retained, churn.flow.paused, churn.flow.downgraded, or churn.flow.cancelled.