> ## Documentation Index
> Fetch the complete documentation index at: https://docs.revtain.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Integration

> Custom billing system or full API control. Pass us the gateway token on failure and we run the retry.

This guide shows the recommended flow: your checkout stays exactly as it is, and Revtain handles recovery when payments fail.

<Info>
  **Your checkout doesn't change.** Customers continue to pay with whatever you already support. Revtain operates entirely behind the scenes — no UI changes, nothing customer-facing.
</Info>

## How It Works

<Steps>
  <Step title="Customer pays through your existing checkout">
    Stripe, Chargebee, custom — anything.
  </Step>

  <Step title="Payment succeeds → log it (optional)">
    Call `/pulse` so Revtain can calculate Lift accurately.
  </Step>

  <Step title="Payment fails → hand off to Revtain">
    Pass the gateway token, amount, and decline code.
  </Step>

  <Step title="Revtain runs the engine">
    Intelligent retries with prediction-driven timing directly against the gateway that issued the original token.
  </Step>

  <Step title="You receive a webhook with the result">
    `recovery.success`, `recovery.failed`, or `recovery.blocked`.
  </Step>
</Steps>

## Backend: Charge, Then Recover

<CodeGroup>
  ```javascript Node.js theme={null}
  app.post('/your-backend/charge', async (req, res) => {
    const { paymentMethodId, amount, stripeCustomerId } = req.body;

    let declineCode;

    // 1. Try charging through YOUR primary gateway using YOUR token
    //    (Your normal checkout — Revtain not involved yet)
    try {
      const charge = await stripe.paymentIntents.create({
        amount,
        currency: 'usd',
        customer: stripeCustomerId,
        payment_method: 'pm_xxx',
        confirm: true
      });

      // Log the organic success so Revtain can calculate Lift
      await fetch(`${REVTAIN_BASE_URL}/api/recovery/pulse`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-API-KEY': process.env.REVTAIN_API_KEY
        },
        body: JSON.stringify({ amount, currency: 'USD', transactionId: charge.id })
      });

      return res.json({ success: true, chargeId: charge.id });
    } catch (stripeError) {
      declineCode = stripeError.decline_code;
    }

    // 2. Primary failed — hand off to Revtain
    const recovery = await fetch(`${REVTAIN_BASE_URL}/api/recovery/execute`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-KEY': process.env.REVTAIN_API_KEY
      },
      body: JSON.stringify({
        paymentMethodToken: paymentMethodId,
        amount,
        currency: 'USD',
        originalDeclineCode: declineCode,
        cardOrigin: 'US',
        idempotencyKey: `order_${orderId}_recovery`,
        // Set when the failed charge came from Apple Pay / Google Pay / PayPal.
        // Defaults to 'card' if omitted. Wallet payments get a higher predicted
        // recovery score, so passing this correctly meaningfully lifts results.
        paymentMethodType: payment.paymentMethodType || 'card',
        // Optional: pass any alternative payment tokens you hold for this
        // customer (e.g. a backup card-on-file when the primary is a wallet).
        // Revtain tries each in order if the primary payment method can't be
        // recovered. Up to 5 supported. No orchestration code needed.
        fallbackPaymentMethodTokens: payment.fallbackTokens || []
      })
    });

    const result = await recovery.json();

    if (recovery.status === 200 && result.success) {
      return res.json({ success: true, recovered: true, transactionId: result.transactionId });
    } else if (recovery.status === 202) {
      return res.json({ success: false, queued: true, message: result.message });
    } else if (recovery.status === 403) {
      return res.status(403).json({ success: false, blocked: true, error: result.error });
    } else if (recovery.status === 409) {
      return res.json({ success: false, duplicate: true, existing: result.existingTransactionId });
    } else if (recovery.status === 429) {
      // Card is in a protective cooldown or at the card-network retry limit —
      // Revtain is guarding your merchant account health. Retry-After is present
      // on cooldowns; it may be absent when the card-network limit was reached.
      return res.json({ success: false, cooldown: true, retryAfter: recovery.headers.get('Retry-After') });
    }

    return res.json({ success: false, error: result.error || result.message });
  });
  ```

  ```python Python theme={null}
  import requests, os

  @app.route('/your-backend/charge', methods=['POST'])
  def charge():
      data = request.json
      payment_method_id = data['paymentMethodId']
      amount = data['amount']

      # 1. Try your primary gateway
      try:
          intent = stripe.PaymentIntent.create(
              amount=amount, currency='usd',
              customer=data['stripeCustomerId'],
              payment_method='pm_xxx',
              confirm=True
          )

          requests.post(
              f"{REVTAIN_BASE_URL}/api/recovery/pulse",
              headers={'X-API-KEY': os.environ['REVTAIN_API_KEY']},
              json={'amount': amount, 'currency': 'USD'}
          )
          return jsonify(success=True, chargeId=intent.id)
      except stripe.error.CardError as e:
          decline_code = e.error.decline_code

      # 2. Hand off to Revtain
      response = requests.post(
          f"{REVTAIN_BASE_URL}/api/recovery/execute",
          headers={
              'Content-Type': 'application/json',
              'X-API-KEY': os.environ['REVTAIN_API_KEY']
          },
          json={
              'paymentMethodToken': payment_method_id,
              'amount': amount,
              'currency': 'USD',
              'originalDeclineCode': decline_code,
              'cardOrigin': 'US',
              'idempotencyKey': f'order_{order_id}_recovery',
              # Set to 'apple_pay', 'google_pay', 'paypal', or 'venmo' when the
              # failed charge came from a wallet. Defaults to 'card' if omitted.
              'paymentMethodType': data.get('paymentMethodType', 'card'),
              # Optional: alternative tokens for the same customer. Revtain
              # tries each in order if the primary payment method can't be
              # recovered. Up to 5 supported.
              'fallbackPaymentMethodTokens': data.get('fallbackTokens', [])
          }
      )

      result = response.json()
      if response.status_code == 200 and result.get('success'):
          return jsonify(success=True, recovered=True, transactionId=result['transactionId'])
      elif response.status_code == 202:
          return jsonify(success=False, queued=True, message=result['message'])
      elif response.status_code == 403:
          return jsonify(success=False, blocked=True, error=result['error']), 403
      elif response.status_code == 409:
          return jsonify(success=False, duplicate=True)

      return jsonify(error='Recovery failed'), 500
  ```
</CodeGroup>

## Custom / In-House Billing

For in-house billing without a webhook event, call Revtain directly from your payment failure handler:

```javascript theme={null}
async function handlePaymentFailure(payment) {
  const recovery = await fetch('https://api.revtain.com/api/recovery/execute', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-KEY': process.env.REVTAIN_API_KEY
    },
    body: JSON.stringify({
      paymentMethodToken: payment.paymentToken,
      amount: payment.amountCents,
      currency: payment.currency || 'USD',
      originalDeclineCode: payment.declineCode || 'unknown',
      idempotencyKey: `custom_${payment.id}_recovery`
    })
  });
  return recovery.json();
}
```

The only requirement is that you have the customer's payment token from your gateway. Everything else is the same regardless of billing platform.

<Tip>
  **Apple Pay / Google Pay / PayPal customers.** Any gateway-native token works — including the `pm_xxx` tokens Stripe issues for Apple Pay and Google Pay charges. Pass the wallet identifier in `paymentMethodType` (`apple_pay`, `google_pay`, `paypal`, or `venmo`) so the recovery engine applies the right strategy. Recovery rates on wallet payments are materially higher when this field is set correctly — wallet tokens ride network tokens with built-in cryptograms, which the predictor weights accordingly.
</Tip>

<Tip>
  **Customers with more than one stored payment method.** Pass any alternative tokens you hold in `fallbackPaymentMethodTokens`. If the primary payment method can't be recovered, Revtain tries each fallback in order automatically — no orchestration code on your side. Common use: wallet primary + backup card-on-file. Up to 5 fallback tokens supported.
</Tip>

## Optional: Pre-Screening Risky Payments

Before charging a payment, you can ask Revtain to score the risk of failure. This is most useful for high-value or first-time-in-a-billing-cycle charges where a wasted attempt can hurt your merchant standing.

```bash theme={null}
curl -X POST https://api.revtain.com/api/predict/risk \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: rev_YOUR_API_KEY" \
  -d '{
    "paymentMethodToken": "pm_1234567890",
    "amount": 5000,
    "currency": "USD"
  }'
```

**Response:**

```json theme={null}
{
  "success": true,
  "prediction": {
    "riskScore": 72.5,
    "recommendation": "delay",
    "reasoning": "Card has 3 prior declines in last 30 days. Amount matches a historically recoverable pattern."
  }
}
```

| `recommendation`       | What to do                                             |
| ---------------------- | ------------------------------------------------------ |
| `proceed`              | Charge normally                                        |
| `proceed_with_caution` | Charge but monitor for failure                         |
| `delay`                | Hold the charge for a Revtain-recommended retry window |
| `block`                | Do not charge — high chance of decline or chargeback   |

You can also subscribe to the `predict.risk.high` webhook to receive automatic alerts for customers whose stored payment method has crossed a risk threshold. See [Webhooks → predict.risk.high](/guides/webhooks#predict-risk-high).

<Tip>
  **Need help?** Contact the Revtain team at [support@revtain.com](mailto:support@revtain.com) for guidance on integrating with your specific billing platform.
</Tip>
