REST API Reference
Complete reference for all 50+ LostChurn REST API endpoints — payments, customers, recoveries, campaigns, analytics, AI scoring, webhooks, and more.
REST API Reference
Complete endpoint reference for the LostChurn REST API. All endpoints are served by the Cloudflare Worker at https://api.lostchurn.com.
For authentication details, see Authentication. For error handling and rate limits, see Errors & Rate Limits.
Status
Check API Connection
GET /api/v1/statusReturns connection status and merchant context. Works with or without authentication — returns additional merchant data when authenticated.
Scopes required: None (public), or any scope for enhanced response.
Example request:
curl https://api.lostchurn.com/api/v1/status \
-H "Authorization: Bearer lc_live_your_api_key"Example response:
{
"data": {
"connected": true,
"merchant_id": "merch_abc123",
"merchant_name": "Acme Inc.",
"plan_tier": "revenue_recovery",
"auth_strategy": "api_key"
},
"meta": {
"timestamp": "2026-03-20T12:00:00Z"
}
}Payments
List Payments
GET /api/v1/paymentsReturns a paginated list of failed payments with optional filters.
Scopes required: read
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter by status: pending, retrying, recovered, terminal, paused |
decline_category | string | — | Filter: soft_retry, hard_customer, terminal, unknown |
psp | string | — | Filter by payment provider (e.g. stripe, braintree) |
created_after | ISO 8601 | — | Filter payments created after this date |
created_before | ISO 8601 | — | Filter payments created before this date |
page | integer | 1 | Page number |
per_page | integer | 25 | Results per page (max 100) |
Example request:
curl "https://api.lostchurn.com/api/v1/payments?status=pending&per_page=10" \
-H "Authorization: Bearer lc_live_your_api_key"Example response:
{
"data": [
{
"id": "pe_abc123",
"provider_payment_id": "pi_3abc123def456",
"amount": 4999,
"currency": "usd",
"status": "pending",
"decline_code": "insufficient_funds",
"decline_category": "soft_retry",
"psp": "stripe",
"customer_email": "jane@example.com",
"retry_count": 0,
"max_retries": 4,
"next_retry_at": "2026-03-22T14:00:00Z",
"created_at": "2026-03-20T10:00:00Z",
"updated_at": "2026-03-20T10:00:00Z"
}
],
"pagination": {
"total": 42,
"page": 1,
"per_page": 10,
"total_pages": 5
},
"meta": { "timestamp": "2026-03-20T12:00:00Z" }
}Get a Payment
GET /api/v1/payments/:idReturns full details for a single payment event.
Scopes required: read
Get Payment Timeline
GET /api/v1/payments/:id/timelineReturns chronological timeline of recovery events for a payment, including retries and status changes.
Scopes required: read
Example response:
{
"data": {
"events": [
{
"type": "payment_failed",
"timestamp": "2026-03-20T10:00:00Z",
"detail": "Payment declined: insufficient_funds"
},
{
"type": "retry",
"timestamp": "2026-03-22T14:00:00Z",
"detail": "Retry scheduled"
}
]
},
"meta": { "timestamp": "2026-03-20T12:00:00Z" }
}Retry a Payment
POST /api/v1/payments/:id/retryManually trigger a payment retry. The system checks retry eligibility before proceeding.
Scopes required: write
Retry rules:
- Cannot retry payments already in
recoveredorterminalstatus - Cannot retry if the maximum retry count has been reached
- Respects quiet hours and campaign-level retry logic
Example response (success):
{
"data": {
"payment_id": "pe_abc123",
"status": "retry_initiated",
"next_retry_at": "2026-03-22T14:00:00Z"
}
}Pause Recovery
POST /api/v1/payments/:id/pausePauses all automated recovery for a specific payment. The payment status changes to paused and no further retries or dunning emails are sent until resumed.
Scopes required: write
Example request:
curl -X POST https://api.lostchurn.com/api/v1/payments/pay_abc123/pause \
-H "Authorization: Bearer lc_live_..."Example response:
{
"ok": true,
"data": {
"id": "pay_abc123",
"status": "paused",
"paused_at": "2026-03-21T10:30:00Z",
"previous_status": "retrying"
}
}Resume Recovery
POST /api/v1/payments/:id/resumeResumes automated recovery for a paused payment. The payment returns to its previous status and scheduled retries or campaign steps are re-activated.
Scopes required: write
Example request:
curl -X POST https://api.lostchurn.com/api/v1/payments/pay_abc123/resume \
-H "Authorization: Bearer lc_live_..."Example response:
{
"ok": true,
"data": {
"id": "pay_abc123",
"status": "retrying",
"resumed_at": "2026-03-21T12:00:00Z",
"next_retry_at": "2026-03-21T18:00:00Z"
}
}Customers
List Customers
GET /api/v1/customersReturns a paginated list of customers.
Scopes required: read
| Parameter | Type | Default | Description |
|---|---|---|---|
has_failed_payment | boolean | — | Filter to customers with active failed payments |
search | string | — | Search by email, name, or provider customer ID |
created_after | ISO 8601 | — | Filter customers created after this date |
created_before | ISO 8601 | — | Filter customers created before this date |
page | integer | 1 | Page number |
per_page | integer | 25 | Results per page (max 100) |
Example request:
curl "https://api.lostchurn.com/api/v1/customers?has_failed_payment=true&search=jane" \
-H "Authorization: Bearer lc_live_your_api_key"Example response:
{
"data": [
{
"id": "cust_abc123",
"provider_customer_id": "cus_stripe_abc123",
"email": "jane@example.com",
"name": "Jane Doe",
"psp": "stripe",
"total_payments": 12,
"failed_payments": 2,
"recovered_payments": 1,
"lifetime_value_cents": 59988,
"created_at": "2025-06-15T08:30:00Z"
}
],
"pagination": { "total": 1, "page": 1, "per_page": 25, "total_pages": 1 },
"meta": { "timestamp": "2026-03-20T12:00:00Z" }
}Get a Customer
GET /api/v1/customers/:idReturns customer profile with recovery statistics.
Scopes required: read
List Customer Payments
GET /api/v1/customers/:id/paymentsReturns a paginated list of a customer's failed payments.
Scopes required: read
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter by payment status |
page | integer | 1 | Page number |
per_page | integer | 25 | Results per page (max 100) |
List Customer Recoveries
GET /api/v1/customers/:id/recoveriesReturns recovery states for all of a customer's failed payments.
Scopes required: read
Delete Customer Data (GDPR Erasure)
DELETE /api/v1/customers/:idPermanently deletes all data for a customer in compliance with GDPR Article 17 (Right to Erasure). This action is irreversible.
Scopes required: admin
Example response:
{
"data": {
"deleted": true,
"customer_id": "cust_abc123",
"records_deleted": 15
}
}Recoveries
List Recoveries
GET /api/v1/recoveriesReturns a paginated list of recovery states with details on retry status, next retry time, and outcome.
Scopes required: read
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter: pending, retrying, recovered, terminal, paused |
page | integer | 1 | Page number |
per_page | integer | 25 | Results per page (max 100) |
Example request:
curl "https://api.lostchurn.com/api/v1/recoveries?status=retrying" \
-H "Authorization: Bearer lc_live_your_api_key"Example response:
{
"data": [
{
"id": "rec_abc123",
"payment_event_id": "pe_abc123",
"status": "retrying",
"retry_count": 2,
"max_retries": 4,
"next_retry_at": "2026-03-22T14:00:00Z",
"recovered_at": null,
"psp_payment_id": "pi_3abc123def456",
"amount": 4999,
"currency": "usd",
"decline_code": "insufficient_funds",
"decline_category": "soft_retry",
"psp": "stripe",
"created_at": "2026-03-20T10:00:00Z",
"updated_at": "2026-03-21T10:00:00Z"
}
],
"pagination": { "total": 8, "page": 1, "per_page": 25, "total_pages": 1 },
"meta": { "timestamp": "2026-03-20T12:00:00Z" }
}Get a Recovery
GET /api/v1/recoveries/:idReturns full details for a single recovery state, including state history and decline subcategory.
Scopes required: read
Example response:
{
"data": {
"id": "rec_abc123",
"payment_event_id": "pe_abc123",
"status": "retrying",
"retry_count": 2,
"max_retries": 4,
"next_retry_at": "2026-03-22T14:00:00Z",
"recovered_at": null,
"customer_id": "cus_stripe_abc123",
"subscription_id": "sub_abc123",
"decline_subcategory": "Balance",
"state_history": [
{ "status": "pending", "at": "2026-03-20T10:00:00Z", "reason": "Payment failed" },
{ "status": "retrying", "at": "2026-03-20T10:05:00Z", "reason": "Retry scheduled" }
],
"psp_payment_id": "pi_3abc123def456",
"amount": 4999,
"currency": "usd",
"decline_code": "insufficient_funds",
"decline_category": "soft_retry",
"psp": "stripe",
"created_at": "2026-03-20T10:00:00Z",
"updated_at": "2026-03-21T10:00:00Z"
},
"meta": { "timestamp": "2026-03-20T12:00:00Z" }
}Trigger Dunning Email
POST /api/v1/recoveries/:id/dunningManually triggers a dunning email for a recovery. Cannot be called for payments that are already recovered or terminal.
Scopes required: write
Example response:
{
"data": {
"recovery_id": "rec_abc123",
"payment_event_id": "pe_abc123",
"status": "dunning_triggered"
},
"meta": { "merchant_id": "merch_abc123", "timestamp": "2026-03-20T12:00:00Z" }
}| Status Code | Meaning |
|---|---|
202 | Dunning email queued for delivery |
404 | Recovery not found |
409 | Payment already recovered or terminal |
Campaigns
List Campaigns
GET /api/v1/campaignsReturns all campaigns for the authenticated merchant.
Scopes required: read
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter: draft, active, paused, archived |
page | integer | 1 | Page number |
per_page | integer | 25 | Results per page (max 100) |
Get a Campaign
GET /api/v1/campaigns/:idReturns full campaign details including steps, trigger configuration, and performance metrics.
Scopes required: read
Create a Campaign
POST /api/v1/campaignsCreates a new recovery campaign in draft status.
Scopes required: write
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Campaign display name |
trigger_type | string | Yes | One of: payment_failed, payment_failed_soft, payment_failed_hard, subscription_past_due, card_expiring |
steps | array | Yes | Array of campaign step objects (see below) |
delay_hours | number | No | Hours to wait before first step (default: 0) |
max_attempts | number | No | Maximum retry attempts (default: 4) |
stop_on_recovery | boolean | No | Stop campaign when payment recovers (default: true) |
stop_on_card_update | boolean | No | Stop when customer updates card (default: true) |
start_hour | number | No | Earliest hour to send (0-23, default: 9) |
end_hour | number | No | Latest hour to send (0-23, default: 21) |
Example request:
curl -X POST https://api.lostchurn.com/api/v1/campaigns \
-H "Authorization: Bearer lc_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Soft Decline Recovery",
"trigger_type": "payment_failed_soft",
"steps": [
{ "action": "retry", "delay_hours": 6 },
{ "action": "email", "delay_hours": 24, "template_id": "tmpl_abc123" },
{ "action": "retry", "delay_hours": 48 }
],
"max_attempts": 4,
"stop_on_recovery": true
}'Update a Campaign
PUT /api/v1/campaigns/:idUpdates a campaign. Only draft and paused campaigns can be updated.
Scopes required: write
Activate a Campaign
POST /api/v1/campaigns/:id/activateActivates a draft or paused campaign. New matching payments will be enrolled.
Scopes required: write
Pause a Campaign
POST /api/v1/campaigns/:id/pausePauses an active campaign. In-progress enrollments continue but no new enrollments are created.
Scopes required: write
Archive a Campaign
POST /api/v1/campaigns/:id/archiveArchives a campaign permanently. Cannot be undone.
Scopes required: write
Subscriptions
Get Subscription Recovery Status
GET /api/v1/subscriptions/:idReturns recovery status for a specific subscription, including failed invoice count, at-risk revenue, and recovery rate.
Scopes required: read
Example request:
curl https://api.lostchurn.com/api/v1/subscriptions/sub_abc123 \
-H "Authorization: Bearer lc_live_your_api_key"Example response:
{
"data": {
"subscription_id": "sub_abc123",
"status": "at_risk",
"failed_invoice_count": 2,
"total_at_risk": 9998,
"recovered_amount": 4999,
"recovery_rate": 0.5
},
"meta": { "timestamp": "2026-03-20T12:00:00Z" }
}| Status | Meaning |
|---|---|
at_risk | Has active failed invoices |
recovered | All failed invoices recovered |
unknown | No failure data available |
List Failed Invoices for a Subscription
GET /api/v1/subscriptions/:id/failed-invoicesReturns a paginated list of failed invoices for a subscription.
Scopes required: read
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
per_page | integer | 25 | Results per page (max 100) |
Example response:
{
"data": [
{
"invoice_id": "in_abc123",
"amount": 4999,
"currency": "usd",
"decline_code": "insufficient_funds",
"decline_category": "soft_retry",
"status": "retrying",
"created_at": "2026-03-20T10:00:00Z"
}
],
"pagination": { "total": 2, "page": 1, "per_page": 25, "total_pages": 1 }
}Invoices
Get Invoice Recovery Status
GET /api/v1/invoices/:idReturns recovery status for a specific invoice, including retry count, decline details, and next retry time.
Scopes required: read
Example request:
curl https://api.lostchurn.com/api/v1/invoices/in_abc123 \
-H "Authorization: Bearer lc_live_your_api_key"Example response:
{
"data": {
"invoice_id": "in_abc123",
"payment_event_id": "pe_abc123",
"amount": 4999,
"currency": "usd",
"status": "retrying",
"decline_code": "insufficient_funds",
"decline_category": "soft_retry",
"decline_subcategory": "Balance",
"psp": "stripe",
"retry_count": 2,
"max_retries": 4,
"next_retry_at": "2026-03-22T14:00:00Z",
"recovered_at": null,
"created_at": "2026-03-20T10:00:00Z",
"updated_at": "2026-03-21T10:00:00Z"
},
"meta": { "timestamp": "2026-03-20T12:00:00Z" }
}Get Invoice Recovery Timeline
GET /api/v1/invoices/:id/timelineReturns a chronological timeline of all recovery events for an invoice, including retries and status transitions.
Scopes required: read
Example response:
{
"data": {
"invoice_id": "in_abc123",
"payment_event_id": "pe_abc123",
"events": [
{
"timestamp": "2026-03-20T10:00:00Z",
"type": "status_change",
"detail": "Status changed to pending: Payment failed"
},
{
"timestamp": "2026-03-21T14:00:00Z",
"type": "retry",
"detail": "Retry succeeded: Payment recovered"
}
]
},
"meta": { "timestamp": "2026-03-20T12:00:00Z" }
}Analytics
Recovery Analytics
GET /api/v1/analytics/recoveryReturns recovery performance metrics for a date range, broken down by decline category.
Scopes required: read or analytics
| Parameter | Type | Default | Description |
|---|---|---|---|
start_date | YYYY-MM-DD | 30 days ago | Start of date range |
end_date | YYYY-MM-DD | Today | End of date range |
group_by | string | day | Group by: hour, day, week, month |
psp | string | — | Filter by payment provider |
decline_category | string | — | Filter by decline category |
Example response:
{
"data": {
"summary": {
"total_failed": 150,
"total_recovered": 95,
"total_terminal": 30,
"total_pending": 25,
"recovery_rate": 0.633,
"total_failed_amount": 750000,
"total_recovered_amount": 475000,
"total_terminal_amount": 150000,
"currency": "usd"
},
"by_category": [
{
"category": "soft_retry",
"failures": 80,
"recovered": 65,
"recovery_rate": 0.813,
"recovered_amount": 325000
},
{
"category": "hard_customer",
"failures": 50,
"recovered": 25,
"recovery_rate": 0.5,
"recovered_amount": 125000
}
],
"period": { "start_date": "2026-02-18", "end_date": "2026-03-20", "group_by": "day" }
}
}Campaign Analytics
GET /api/v1/analytics/campaignsReturns performance metrics for all campaigns.
Scopes required: read or analytics
Example response:
{
"data": [
{
"id": "cmp_abc123",
"name": "Soft Decline Recovery",
"status": "active",
"trigger": "payment_failed_soft",
"sessions_count": 120,
"recovered_count": 78,
"recovery_rate": 0.65
}
]
}Revenue Analytics
GET /api/v1/analytics/revenueReturns revenue recovered and MRR impact for a date range.
Scopes required: read or analytics
| Parameter | Type | Default | Description |
|---|---|---|---|
start_date | YYYY-MM-DD | 30 days ago | Start of date range |
end_date | YYYY-MM-DD | Today | End of date range |
Example response:
{
"data": {
"revenue_recovered": 475000,
"revenue_at_risk": 275000,
"payments_recovered": 95,
"payments_failed": 150,
"currency": "usd",
"period": { "start_date": "2026-02-18", "end_date": "2026-03-20" }
}
}At-Risk Revenue
GET /api/v1/analytics/at-riskReturns the total revenue currently at risk (sum of all pending/retrying failed payments).
Scopes required: read or analytics
Example response:
{
"data": {
"at_risk_cents": 275000,
"at_risk_count": 55
}
}Decline Code Distribution
GET /api/v1/analytics/decline-codesReturns decline code distribution and recovery rates for a date range.
Scopes required: read or analytics
| Parameter | Type | Default | Description |
|---|---|---|---|
start_date | YYYY-MM-DD | 30 days ago | Start of date range |
end_date | YYYY-MM-DD | Today | End of date range |
Example response:
{
"data": {
"decline_codes": [
{
"decline_code": "insufficient_funds",
"category": "soft_retry",
"subcategory": "Balance",
"occurrences": 45,
"recovered": 35,
"recovery_rate": 0.778,
"total_amount": 225000
}
],
"period": { "start_date": "2026-02-18", "end_date": "2026-03-20" }
}
}Analytics Summary (Alias)
GET /api/v1/analytics/summaryRedirects (307) to /api/v1/analytics/recovery. Used by the BigCommerce integration.
Decline Codes
List Decline Codes
GET /api/v1/decline-codesReturns all known decline codes with their category, subcategory, and recommended recovery strategy.
Scopes required: read
Get Decline Code
GET /api/v1/decline-codes/:codeReturns details for a specific decline code.
Scopes required: read
List Decline Code Categories
GET /api/v1/decline-codes/categoriesReturns the decline code category taxonomy.
Scopes required: read
Webhooks (Outbound)
List Webhook Endpoints
GET /api/v1/webhooks/endpointsReturns all registered webhook endpoints for the authenticated merchant.
Scopes required: webhooks or read
Create Webhook Endpoint
POST /api/v1/webhooks/endpointsRegisters a new HTTPS endpoint to receive webhook events.
Scopes required: webhooks or admin
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
url | string (URL) | Yes | HTTPS URL to receive events |
events | string[] | No | Event types to subscribe to (default: all) |
description | string | No | Human-readable label |
Example request:
curl -X POST https://api.lostchurn.com/api/v1/webhooks/endpoints \
-H "Authorization: Bearer lc_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/lostchurn",
"events": ["recovery.succeeded", "recovery.failed"],
"description": "Production recovery events"
}'Update Webhook Endpoint
PUT /api/v1/webhooks/endpoints/:idUpdates URL and/or subscribed events for a webhook endpoint.
Scopes required: webhooks or admin
Delete Webhook Endpoint
DELETE /api/v1/webhooks/endpoints/:idRemoves a webhook endpoint.
Scopes required: webhooks or admin
List Webhook Events
GET /api/v1/webhooks/eventsReturns a paginated log of recent webhook deliveries.
Scopes required: webhooks or read
Test Webhook Endpoint
POST /api/v1/webhooks/endpoints/:id/testSends a test event to the specified webhook endpoint.
Scopes required: webhooks or admin
Settings
Get Quiet Hours
GET /api/v1/settings/quiet-hoursReturns the merchant's quiet hours configuration. During quiet hours, no retry attempts or dunning messages are sent.
Scopes required: read
Example response:
{
"data": {
"start_hour": 22,
"end_hour": 8,
"timezone": "America/New_York",
"enabled": true
}
}Set Quiet Hours
POST /api/v1/settings/quiet-hoursConfigures quiet hours for the merchant.
Scopes required: write
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
start_hour | integer | Yes | Hour to begin quiet period (0-23) |
end_hour | integer | Yes | Hour to end quiet period (0-23) |
timezone | string | Yes | IANA timezone (e.g. America/New_York, Europe/London) |
Example request:
curl -X POST https://api.lostchurn.com/api/v1/settings/quiet-hours \
-H "Authorization: Bearer lc_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"start_hour": 22,
"end_hour": 8,
"timezone": "America/New_York"
}'Templates
Preview Dunning Template
GET /api/v1/templates/preview/:idReturns a rendered preview of a dunning email template with sample data substituted into all placeholders.
Scopes required: read
Example response:
{
"data": {
"id": "tmpl_abc123",
"name": "Soft Decline - First Touch",
"subject": "Your payment of $49.99 could not be processed",
"html_preview": "<html>...(rendered HTML with sample data)...</html>",
"text_preview": "Hi Jane Doe, your payment of $49.99...",
"placeholders": [
"{{customer_name}}",
"{{customer_email}}",
"{{amount}}",
"{{amount_cents}}",
"{{currency}}",
"{{decline_code}}",
"{{decline_reason}}",
"{{retry_date}}",
"{{merchant_name}}",
"{{update_payment_url}}",
"{{support_email}}",
"{{subscription_name}}",
"{{invoice_id}}"
]
}
}Integrations
Register Shopify Store
POST /api/v1/integrations/shopify/registerCreates or updates a merchant's Shopify store association. Used during Shopify app installation.
Scopes required: admin
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
shop_domain | string | Yes | Shopify store domain (e.g. mystore.myshopify.com) |
Example request:
curl -X POST https://api.lostchurn.com/api/v1/integrations/shopify/register \
-H "Authorization: Bearer lc_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "shop_domain": "mystore.myshopify.com" }'Ingest Payment Event
POST /api/v1/ingest/payment-eventGeneric payment event ingestion endpoint. Used by BigCommerce, WooCommerce, and other integrations that do not have native webhook support.
Scopes required: write or admin
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
event_type | string | Yes | One of: payment_failed, payment_succeeded, subscription_cancelled, payment_method_updated, charge_refunded |
amount_cents | integer | Yes | Amount in smallest currency unit (e.g. cents) |
currency | string | Yes | ISO 4217 currency code (lowercase): usd, eur, gbp, etc. |
customer_email | string | Yes | Customer email address |
decline_code | string | No | PSP-specific decline code |
metadata | object | No | Arbitrary key-value metadata |
Supported currencies: usd, eur, gbp, cad, aud, jpy, chf, nzd, sek, nok, dkk, pln, czk, huf, ron, bgn, hrk, brl, mxn, inr
Example request:
curl -X POST https://api.lostchurn.com/api/v1/ingest/payment-event \
-H "Authorization: Bearer lc_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"event_type": "payment_failed",
"amount_cents": 2999,
"currency": "usd",
"customer_email": "jane@example.com",
"decline_code": "insufficient_funds",
"metadata": { "order_id": "ORD-12345" }
}'AI Generation
Generate AI Text
POST /api/v1/ai/generateUnified AI text generation endpoint powered by Cloudflare Workers AI (Qwen3 30B). Used for email personalization, decline explanations, campaign copy, and more.
Scopes required: write
Rate limit: 100 calls/minute per merchant (separate from API rate limit).
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
use_case | string | Yes | See use cases below |
system_prompt | string | Yes | System prompt for the AI model |
user_prompt | string | Yes | User prompt with context |
max_tokens | integer | No | Max output tokens (50-4096, default: 500) |
temperature | number | No | Sampling temperature (0-2, default: 0.7) |
language | string | No | Target language for output |
Valid use cases:
| Use Case | Description | Cacheable |
|---|---|---|
email_personalization | Personalized dunning email content | No |
sms_personalization | SMS message personalization | No |
whatsapp_personalization | WhatsApp message personalization | No |
decline_explanation | Human-readable decline code explanation | Yes (1 hour) |
retry_scoring | AI-assisted retry scoring | Yes (1 hour) |
campaign_copy | Campaign email/SMS copy generation | No |
weekly_digest | Weekly recovery digest summary | No |
multi_language_dunning | Multi-language dunning content | No |
Example request:
curl -X POST https://api.lostchurn.com/api/v1/ai/generate \
-H "Authorization: Bearer lc_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"use_case": "decline_explanation",
"system_prompt": "Explain payment decline codes in plain English for customer support agents.",
"user_prompt": "Explain the decline code: insufficient_funds",
"max_tokens": 200,
"temperature": 0.3
}'Example response:
{
"text": "The \"insufficient_funds\" decline means the customer's card did not have enough available balance...",
"model": "@cf/qwen/qwen3-30b-a3b-fp8",
"tokens_used": 85,
"cached": false,
"latency_ms": 342
}AI Health Check
GET /api/v1/ai/pingTests whether the Workers AI binding is responsive. No authentication required.
Example response:
{
"status": "ok",
"model": "@cf/qwen/qwen3-30b-a3b-fp8",
"response": "OK",
"latency_ms": 150
}AI Retry Scoring
Score Payment Retry
POST /api/v1/scoring/retryScores a payment retry attempt using AI, predicting the probability of success and recommending optimal delay. Supports three model tiers.
Scopes required: write
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
decline_code | string | Yes | PSP decline code (e.g. insufficient_funds) |
amount_cents | integer | Yes | Payment amount in smallest currency unit |
decline_category | string | No | Category: SoftRetry, HardCustomer, Terminal, Unknown |
attempt_number | integer | No | Current attempt number (default: 1) |
hour_of_day | integer | No | Hour of retry (0-23, default: 12) |
day_of_week | integer | No | Day of week (0=Sun, 6=Sat) |
currency | string | No | ISO 4217 code (default: usd) |
card_brand | string | No | Card brand (e.g. visa, mastercard) |
card_last4 | string | No | Last 4 digits of card |
is_debit | boolean | No | Whether card is debit |
customer_lifetime_value_cents | integer | No | Customer LTV in cents |
previous_successful_payments | integer | No | Count of past successful payments |
subscription_months | integer | No | Months customer has been subscribed |
days_since_failure | integer | No | Days since the original failure |
payday_proximity_days | integer | No | Days until customer's likely payday |
success_rate_90d | number | No | 90-day success rate (0-1) |
anomaly_score | number | No | Anomaly score (0-1) |
model | string | No | Model to use: rules, enhanced (default), lgbm |
Model tiers:
| Model | Source | Description |
|---|---|---|
rules | Worker | Simple rule-based scoring using decline code heuristics |
enhanced | Workers AI | AI-powered scoring with Workers AI (falls back to rules on failure) |
lgbm | KV Lookup | Pre-computed LightGBM scores from training pipeline (falls back to enhanced) |
Example request:
curl -X POST https://api.lostchurn.com/api/v1/scoring/retry \
-H "Authorization: Bearer lc_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"decline_code": "insufficient_funds",
"amount_cents": 4999,
"attempt_number": 2,
"hour_of_day": 14,
"card_brand": "visa",
"customer_lifetime_value_cents": 59988,
"model": "enhanced"
}'Example response:
{
"score": 0.62,
"delay_hours": 48,
"model_version": "workers-ai-granite-4.0-h-micro",
"confidence": 0.7,
"source": "workers_ai",
"reasoning": "Insufficient funds with a loyal customer (high LTV). Afternoon retry on attempt 2 has a moderate probability of success.",
"factors": [
{ "name": "decline_code", "impact": "positive", "weight": 0.7 },
{ "name": "customer_ltv", "impact": "positive", "weight": 0.15 },
{ "name": "attempt_number", "impact": "negative", "weight": 0.15 }
]
}Vector Search
Vector search endpoints use Cloudflare Vectorize to find similar past recovery events and recommend optimal strategies.
Embed Recovery Event
POST /api/v1/vectors/embed-eventEmbeds a recovery event into the vector index for future similarity searches.
Scopes required: write
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique event identifier |
decline_code | string | Yes | PSP decline code |
amount_cents | integer | Yes | Payment amount in cents |
currency | string | Yes | 3-letter ISO currency code |
customer_segment | string | Yes | Customer segment (e.g. high_value, new, at_risk) |
recovery_strategy | string | Yes | Strategy used (e.g. retry_only, email_then_retry) |
outcome | string | Yes | Outcome: recovered, failed, pending |
Example request:
curl -X POST https://api.lostchurn.com/api/v1/vectors/embed-event \
-H "Authorization: Bearer lc_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"id": "rec_abc123",
"decline_code": "insufficient_funds",
"amount_cents": 4999,
"currency": "usd",
"customer_segment": "high_value",
"recovery_strategy": "email_then_retry",
"outcome": "recovered"
}'Example response:
{
"data": {
"embedded": true,
"id": "rec_abc123",
"merchant_id": "merch_abc123"
}
}Find Similar Recoveries
POST /api/v1/vectors/find-similarFinds similar past recovery events based on decline context. Returns results ranked by cosine similarity.
Scopes required: read
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
decline_code | string | Yes | PSP decline code |
amount_cents | integer | Yes | Payment amount in cents |
currency | string | Yes | 3-letter currency code |
customer_segment | string | Yes | Customer segment |
top_k | integer | No | Number of results to return (1-50, default: 10) |
Example response:
{
"data": {
"count": 3,
"matches": [
{
"id": "rec_xyz789",
"score": 0.95,
"metadata": {
"outcome": "recovered",
"strategy": "email_then_retry",
"decline_code": "insufficient_funds"
}
}
]
}
}Recommend Recovery Strategy
GET /api/v1/vectors/recommend-strategy/:payment_idGiven a payment event ID, finds the best recovery strategy based on similar past outcomes. Automatically looks up payment context from SpacetimeDB.
Scopes required: read
Example response:
{
"data": {
"payment_id": "pe_abc123",
"recommended_strategy": "email_then_retry",
"confidence": 0.87,
"similar_outcomes": {
"recovered": 12,
"failed": 3,
"total": 15
}
}
}Cancel Sessions
Cancel flow endpoints manage the lifecycle of subscription cancellation interception sessions. These run on the edge worker at https://edge.lostchurn.com.
Start a Session
POST /api/cancel-session/startCreates a new cancel session with a unique token. Returns session state and first step configuration.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
merchant_id | string | Yes | Merchant identifier |
customer_id | string | Yes | Customer identifier |
subscription_id | string | No | Subscription being cancelled |
Get Session State
GET /api/cancel-session/:tokenReturns the current session state and step configuration.
Advance Session
POST /api/cancel-session/:token/advanceAdvances the session to the next step (e.g. from survey to offer).
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
response | object | No | Customer's response to the current step |
selected_reason | string | No | Selected cancellation reason (survey step) |
Complete Session
POST /api/cancel-session/:token/completeCompletes the session with a final outcome.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
outcome | string | Yes | One of: cancelled, saved_discount, saved_pause, saved_other, abandoned |
reason | string | No | Cancellation reason |
feedback | string | No | Free-text feedback |
Widget
Initialize Widget
POST /api/widget/initInitializes the embeddable recovery widget. Validates the HMAC token, looks up the customer's failed payments, and returns payment info with merchant branding.
Authentication: HMAC signature (not API key).
Rate limit: 30 requests/minute per IP.
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
merchant_id | string | Yes | Merchant identifier |
customer_id | string | Yes | Customer identifier |
timestamp | integer | Yes | Unix timestamp (must be within 5 minutes) |
signature | string | Yes | HMAC-SHA256 signature |
Example response:
{
"has_failed_payment": true,
"amount_cents": 4999,
"currency": "usd",
"update_url": "https://billing.example.com/update-payment",
"branding": {
"logo_url": "https://cdn.example.com/logo.png",
"primary_color": "#4F46E5",
"merchant_name": "Acme Inc."
}
}Get Widget Branding
GET /api/widget/branding/:merchant_idReturns merchant branding configuration for the widget. Public endpoint, cached for 5 minutes.
Authentication: None (public endpoint).
Inbound PSP Webhooks
LostChurn accepts webhooks from 13 payment service providers. Each PSP has a dedicated endpoint with provider-specific signature verification.
| PSP | Endpoint | Signature Method |
|---|---|---|
| Stripe | POST /stripe/:merchant_id | HMAC-SHA256 (Stripe-Signature header) |
| Stripe Invoice | POST /stripe/:merchant_id/invoice | HMAC-SHA256 |
| Braintree | POST /braintree/:merchant_id | Braintree signature verification |
| Paddle | POST /paddle/:merchant_id | Paddle webhook signature |
| Recurly | POST /recurly/:merchant_id | Recurly HMAC |
| Chargebee | POST /chargebee/:merchant_id | Basic auth credentials |
| Shopify | POST /shopify/:merchant_id | HMAC-SHA256 (X-Shopify-Hmac-Sha256) |
| Adyen | POST /adyen/:merchant_id | HMAC-SHA256 (HmacSignature) |
| Checkout.com | POST /checkoutcom/:merchant_id | HMAC-SHA256 |
| Authorize.Net | POST /authorizenet/:merchant_id | Signature key verification |
| PayPal | POST /paypal/:merchant_id | PayPal webhook ID verification |
| GoCardless | POST /gocardless/:merchant_id | GoCardless webhook signature |
| Nuvei | POST /nuvei/:merchant_id | Checksum verification |
| Worldpay | POST /worldpay/:merchant_id | MAC verification |
Configure your PSP to send webhooks to https://webhooks.lostchurn.com/{psp}/{your_merchant_id}. Each PSP's signing secret is configured in Settings > Integrations in the dashboard.
Delivery Webhooks
Twilio SMS/WhatsApp Status
POST /twilio/statusReceives delivery status callbacks from Twilio for SMS and WhatsApp messages. Updates communication_log delivery status in SpacetimeDB.
Twilio Email Status
POST /twilio/email/statusReceives delivery status callbacks for Twilio SendGrid emails. Note: Email delivery has been migrated to Resend. This endpoint is retained for backwards compatibility with legacy SendGrid configurations. New integrations should use the Resend Email Webhook below.
Resend Email Webhook
POST /resend/webhookReceives email delivery events from Resend (via Svix). Tracks opens, clicks, bounces, and complaints.
Custom Hostname (White-Label)
Provision Custom Hostname
POST /api/custom-hostname/provisionCreates a Cloudflare Custom Hostname for white-label domain support. The merchant must create a CNAME record pointing to custom.lostchurn.com.
Scopes required: admin
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
hostname | string | Yes | Custom domain (e.g. billing.yourdomain.com) |
Verify Custom Hostname
POST /api/custom-hostname/verifyChecks DNS CNAME configuration and updates verification status.
Scopes required: admin
Get Custom Hostname Status
GET /api/custom-hostname/status/:idReturns current verification status for a custom hostname.
Scopes required: read
Delete Custom Hostname
DELETE /api/custom-hostname/:idRemoves a custom hostname and cleans up KV cache.
Scopes required: admin
Get Merchant Branding
GET /api/custom-hostname/branding/:merchant_idReturns branding configuration for a merchant's white-label setup. Used by customer-facing pages.
Authentication: None (public, cached).
Email Domain
Auto-Provision Subdomain
POST /api/email-domain/auto-provisionAutomatically provisions a LostChurn subdomain for email sending (e.g. acme.mail.lostchurn.com). Handles DNS records and Resend domain setup.
Scopes required: admin
Custom Domain Setup
POST /api/email-domain/custom-setupInitiates guided custom domain setup for email sending. Returns required DNS records for the merchant to configure.
Scopes required: admin
Verify Domain DNS
POST /api/email-domain/verifyTriggers DNS verification for a pending email domain.
Scopes required: admin
Get Domain Status
GET /api/email-domain/status/:idReturns domain verification status and DNS diagnostics.
Scopes required: read
Deactivate Domain
DELETE /api/email-domain/:idDeactivates an email domain and cleans up DNS records.
Scopes required: admin
CRM Integrations
HubSpot OAuth Callback
POST /hubspot/oauth/callbackHandles HubSpot OAuth token exchange during app installation.
HubSpot CRM Sync
POST /hubspot/syncSyncs recovery data to HubSpot as contacts and timeline events.
Salesforce OAuth Callback
POST /salesforce/oauth/callbackHandles Salesforce OAuth token exchange during connected app installation.
Salesforce CRM Sync
POST /salesforce/syncSyncs recovery data to Salesforce as Recovery__c custom objects and platform events.
Zapier Subscribe
POST /zapier/subscribeRegisters a Zapier REST Hook subscription for real-time event delivery.
Zapier Unsubscribe
DELETE /zapier/subscribeRemoves a Zapier REST Hook subscription.
Zapier Polling Fallback
GET /zapier/poll/:eventTypeReturns the last 50 events of a given type. Used by Zapier as a polling fallback when REST Hooks are unavailable.
Zapier Sample Data
GET /zapier/sample/:eventTypeReturns sample event data for Zapier trigger configuration.
Internal Endpoints
These endpoints are used internally by the dashboard and are not part of the public API.
Webhook Payload Retrieval
GET /internal/webhook/:webhook_log_idRetrieves a stored webhook payload from R2 by log ID.
Authentication: Internal (service binding or CRON_SECRET).
GDPR Webhook Erasure
DELETE /internal/webhook-cleanup/:idDeletes webhook payloads from R2 for GDPR compliance.
Data Export Upload
POST /internal/exports/uploadUploads a data export file to R2.
Data Export Download
GET /internal/exports/:merchantId/:jobIdRetrieves a data export file from R2.
Cache Invalidation
POST /api/internal/cache/invalidatePurges a KV cache entry. Requires CRON_SECRET.
LightGBM Lookup Update
POST /api/internal/scoring/update-lookupSeeds or updates the LightGBM KV lookup table with pre-computed retry scores from the training pipeline.
Authentication: CRON_SECRET.
WebSocket Proxy
These endpoints support the HttpOnly WebSocket proxy for secure SpacetimeDB connections.
Create WS Session
POST /ws/auth/sessionCreates a WebSocket session. Validates Clerk JWT and sets an HttpOnly session cookie.
Refresh WS Session
POST /ws/auth/refreshRefreshes an existing WebSocket session cookie.
Revoke WS Session
POST /ws/auth/revokeRevokes a WebSocket session and clears the HttpOnly cookie.
Get WebSocket Token
POST /ws/v1/identity/websocket-tokenProxies a WebSocket token request to SpacetimeDB. Requires valid session cookie.
WebSocket Upgrade
GET /ws/v1/database/:name/subscribeUpgrades the connection to a WebSocket and proxies to SpacetimeDB. Requires valid session cookie.
Health
Health Check
GET /healthReturns a simple health check response. No authentication required.
Example response:
{ "status": "ok" }Cron Triggers
The worker runs scheduled tasks via Cloudflare Cron Triggers:
| Schedule | Tasks |
|---|---|
| Every minute | Flush Slack notification queue, execute pending retries, dispatch queued messages (SMS/WhatsApp/Voice) |
Hourly (0 *) | Advance campaign steps, analytics rollup |
Daily (0 8) | Card expiry checks, pre-dunning notifications, pending domain provisioning, domain warmup, R2 retention cleanup, billing metering |
Endpoint Summary
| # | Method | Path | Auth | Description |
|---|---|---|---|---|
| 1 | GET | /api/v1/status | Optional | API connection check |
| 2 | GET | /api/v1/payments | read | List payments |
| 3 | GET | /api/v1/payments/:id | read | Get payment |
| 4 | GET | /api/v1/payments/:id/timeline | read | Payment timeline |
| 5 | POST | /api/v1/payments/:id/retry | write | Retry payment |
| 6 | POST | /api/v1/payments/:id/pause | write | Pause recovery |
| 7 | POST | /api/v1/payments/:id/resume | write | Resume recovery |
| 8 | GET | /api/v1/customers | read | List customers |
| 9 | GET | /api/v1/customers/:id | read | Get customer |
| 10 | GET | /api/v1/customers/:id/payments | read | Customer payments |
| 11 | GET | /api/v1/customers/:id/recoveries | read | Customer recoveries |
| 12 | DELETE | /api/v1/customers/:id | admin | GDPR erasure |
| 13 | GET | /api/v1/recoveries | read | List recoveries |
| 14 | GET | /api/v1/recoveries/:id | read | Get recovery |
| 15 | POST | /api/v1/recoveries/:id/dunning | write | Trigger dunning |
| 16 | GET | /api/v1/campaigns | read | List campaigns |
| 17 | GET | /api/v1/campaigns/:id | read | Get campaign |
| 18 | POST | /api/v1/campaigns | write | Create campaign |
| 19 | PUT | /api/v1/campaigns/:id | write | Update campaign |
| 20 | POST | /api/v1/campaigns/:id/activate | write | Activate campaign |
| 21 | POST | /api/v1/campaigns/:id/pause | write | Pause campaign |
| 22 | POST | /api/v1/campaigns/:id/archive | write | Archive campaign |
| 23 | GET | /api/v1/subscriptions/:id | read | Subscription status |
| 24 | GET | /api/v1/subscriptions/:id/failed-invoices | read | Failed invoices |
| 25 | GET | /api/v1/invoices/:id | read | Invoice status |
| 26 | GET | /api/v1/invoices/:id/timeline | read | Invoice timeline |
| 27 | GET | /api/v1/analytics/recovery | read/analytics | Recovery metrics |
| 28 | GET | /api/v1/analytics/campaigns | read/analytics | Campaign metrics |
| 29 | GET | /api/v1/analytics/revenue | read/analytics | Revenue metrics |
| 30 | GET | /api/v1/analytics/at-risk | read/analytics | At-risk revenue |
| 31 | GET | /api/v1/analytics/decline-codes | read/analytics | Decline distribution |
| 32 | GET | /api/v1/analytics/summary | — | Alias for recovery |
| 33 | GET | /api/v1/decline-codes | read | List decline codes |
| 34 | GET | /api/v1/decline-codes/:code | read | Get decline code |
| 35 | GET | /api/v1/decline-codes/categories | read | Decline categories |
| 36 | GET | /api/v1/webhooks/endpoints | webhooks | List endpoints |
| 37 | POST | /api/v1/webhooks/endpoints | webhooks | Create endpoint |
| 38 | PUT | /api/v1/webhooks/endpoints/:id | webhooks | Update endpoint |
| 39 | DELETE | /api/v1/webhooks/endpoints/:id | webhooks | Delete endpoint |
| 40 | GET | /api/v1/webhooks/events | webhooks | Delivery log |
| 41 | POST | /api/v1/webhooks/endpoints/:id/test | webhooks | Test endpoint |
| 42 | GET | /api/v1/settings/quiet-hours | read | Get quiet hours |
| 43 | POST | /api/v1/settings/quiet-hours | write | Set quiet hours |
| 44 | GET | /api/v1/templates/preview/:id | read | Preview template |
| 45 | POST | /api/v1/integrations/shopify/register | admin | Register Shopify |
| 46 | POST | /api/v1/ingest/payment-event | write | Ingest event |
| 47 | POST | /api/v1/ai/generate | write | AI text generation |
| 48 | GET | /api/v1/ai/ping | None | AI health check |
| 49 | POST | /api/v1/scoring/retry | write | AI retry scoring |
| 50 | POST | /api/v1/vectors/embed-event | write | Embed event |
| 51 | POST | /api/v1/vectors/find-similar | read | Find similar |
| 52 | GET | /api/v1/vectors/recommend-strategy/:id | read | Recommend strategy |
| 53 | POST | /api/cancel-session/start | Token | Start cancel session |
| 54 | GET | /api/cancel-session/:token | Token | Get session state |
| 55 | POST | /api/cancel-session/:token/advance | Token | Advance session |
| 56 | POST | /api/cancel-session/:token/complete | Token | Complete session |
| 57 | POST | /api/widget/init | HMAC | Widget init |
| 58 | GET | /api/widget/branding/:id | None | Widget branding |
| 59 | GET | /health | None | Health check |