The Recovery Link API lets your backend mint a link to your hosted recovery page for a customer with a failed payment. You decide where the link goes: a banner in your app, your own email, a support reply. The customer lands on the same page Churnkey recovery emails point to, updates their payment method, and pays the outstanding invoice.
Prerequisites
- A connected Stripe account. Other billing providers are not supported.
- A configured recovery page domain: a Churnkey subdomain or a custom domain, set up under Settings → Domain. Without one the API returns an error.
Links show no campaign offer: the customer always pays the full outstanding amount, exactly like the in-app Failed Payment Wall. Generating a link does not stop a running recovery campaign; the campaign ends when the payment is recovered.
Generate a link
Endpoint
POST https://api.churnkey.co/v1/recovery-sessions
Authentication
All requests require your App ID and Data API Key, which you can find under Settings → Organization in your Churnkey Dashboard. The key never belongs in browser code: your server talks to Churnkey, and only the resulting link reaches the customer.
x-ck-app: YOUR_APP_ID
x-ck-api-key: YOUR_DATA_API_KEY
Content-Type: application/json
Use your live key for live customers and your test key against test billing data. Churnkey reads the mode from the key prefix (live_data_... or test_data_...) and echoes it back as mode in the response.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
customerId | string | Yes | The Stripe customer ID (cus_...). |
invoiceId | string | No | A specific open invoice (in_...) to target. Without it, Churnkey runs the same lookup as the Failed Payment Wall: an open invoice with a balance, whose due date has passed and falls within the last 60 days. |
expiresAt | string | No | ISO 8601 timestamp for when the link stops working. Between 1 hour and 30 days from now. Defaults to 5 days. |
Example request
curl -X POST https://api.churnkey.co/v1/recovery-sessions \
-H "x-ck-api-key: YOUR_DATA_API_KEY" \
-H "x-ck-app: YOUR_APP_ID" \
-H "Content-Type: application/json" \
-d '{ "customerId": "cus_abc123" }'
Example response
{
"id": "665f1c2e8f1b2a0012345678",
"url": "https://billing.yourcompany.com/?auth=eyJhbGciOi...",
"expiresAt": "2026-09-01T12:00:00.000Z",
"invoiceId": "in_1OZDeWAjZMdyHS1H",
"mode": "live",
"attributable": true
}
For most integrations you only need url. Hand it to the customer however fits your product; the link is already authenticated, so there is no verification step in between.
Repeated calls for the same invoice return the existing unexpired link, with its original expiry: a different expiresAt on a repeat call is ignored. To change the expiry, revoke the link and generate a new one. Keep id if you plan to revoke it later.
Error responses
| Status | Meaning |
|---|---|
400 | Missing customerId, or an expiresAt that is not a valid date within the allowed range. |
401 | Missing or invalid x-ck-app / x-ck-api-key. |
404 | No outstanding failed invoice for this customer, or the named invoice was not found. |
422 | No recovery page domain configured, no connected Stripe account, the invoice has no outstanding balance, or the account recovers PaymentIntents rather than invoices, which this endpoint does not support yet. |
503 | Stripe could not be reached to read the invoice. Safe to retry. |
Attribution
A payment recovered through an API-generated link is counted under the Assisted method in your recovery analytics. See Recovery Methods.
Some links cannot be attributed. When Churnkey holds no record of the failed payment, the customer can still pay (the invoice is read from Stripe), but the recovery is missing from analytics. This happens for one-off invoices not tied to a subscription (excluded from Payment Recovery by default) and for customers excluded from recovery campaigns. In that case the response carries "attributable": false and an explanatory attributionNote.
{
"id": "665f1c2e8f1b2a0012345678",
"url": "https://billing.yourcompany.com/?auth=eyJhbGciOi...",
"expiresAt": "2026-09-01T12:00:00.000Z",
"invoiceId": "in_1OZDeWAjZMdyHS1H",
"mode": "live",
"attributable": false,
"attributionNote": "No recovery campaign matches this invoice; a recovery through this link will not be attributed to Churnkey."
}
Revoke a link
DELETE https://api.churnkey.co/v1/recovery-sessions/:id
Uses the same authentication headers, with the key that minted the link: a live key cannot revoke a test link or the other way round. The id comes from the generate response. Revoking expires the link immediately; a later generate call for the same invoice mints a fresh one. Revoking an already expired or revoked link returns 404.
Security notes
- The link authenticates the customer: anyone holding it can see the customer's billing details and pay the invoice. Treat it like a password reset link.
- Choose the shortest expiry that fits your flow. A link behind a button in your logged-in app can live for hours; a link sent by email may need days.
- Links are not single-use. Revoke a link if it may have leaked.