Marking payments as recovered

When a customer completes payment outside Declined's hosted flow — direct bank transfer, in-app purchase, manual reconciliation, or your own checkout — you must notify Declined so recovery sequences stop, invoices are marked paid, and analytics stay accurate.

What happens on recovery

  • Active recovery attempt status changes to recovered
  • Linked invoice status changes to paid
  • A payment event is recorded for analytics
  • Outbound webhooks fire (e.g. recovery.completed)
  • Sequence enrollment stats are updated

Option 1 — Event API (recommended)

Send a payment_recovered event with your customer and invoice identifiers. Declined resolves the active recovery attempt automatically.

typescript
await client.events.markPaymentRecovered({
  event_id: "evt_" + Date.now(),
  customer_id: "cus_123",
  invoice_id: "inv_456",
  amount: 24900,
  currency: "usd",
  provider: "stripe",
});

Equivalent raw request:

curl
curl -X POST https://dev.declined.io/api/v1/events \
  -H "Authorization: Bearer decl_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "event_id": "evt_rec_001",
    "type": "payment_recovered",
    "customer_id": "cus_123",
    "invoice_id": "inv_456"
  }'

Option 2 — Recovery attempt API

If you have a Declined recovery attempt ID (from GET /v1/recoveries, dashboard, or an outbound webhook), mark it directly:

typescript
await client.recoveries.markRecovered("ra_abc123");

Equivalent raw request:

curl
curl -X POST https://dev.declined.io/api/v1/recoveries/ra_abc123/mark-recovered \
  -H "Authorization: Bearer decl_live_sk_..."

Response

json
{
  "recovery_attempt_id": "ra_abc123",
  "status": "recovered"
}

If the attempt was already recovered, the API returns already_recovered: true without side effects.

When to use each approach

  • Event API — You know the customer/invoice IDs from your billing system (Stripe, Paddle, etc.) but not the Declined recovery attempt ID.
  • Recovery attempt API — You received a recovery attempt ID from Declined webhooks or listed recoveries via the API.

SDK examples

Language-specific snippets are in each SDK guide under SDKs.