# Subscription Payment Recovery Queue (`afkdigital/subscription-payment-recovery-queue`) Actor

Read-only human review queue from authorized invoice and payment-event CSV/JSON exports; excludes known successful payments and terminal invoice states. No automatic retries, emails or customer-data extraction.

- **URL**: https://apify.com/afkdigital/subscription-payment-recovery-queue.md
- **Developed by:** [AFK Digital](https://apify.com/afkdigital) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 manual review candidates

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Subscription Payment Recovery Queue

**Human-reviewed payment exceptions from authorized CSV/JSON exports. Read-only.**

Find failed subscription invoices that **lack a confirmed later resolution in your supplied exports**. Generate an operational review list—not an instruction to collect, retry a card, contact a customer, or declare an amount recoverable.

### Why this is different

Stripe Billing already provides Smart Retries and configurable payment emails. Churn Buster, Churnkey, and Baremetrics Recover offer automated recovery flows. **This Actor does none of those things.** It reconciles a point-in-time *export* of invoice states and payment events into a compact queue for a finance/operations person to check in their source billing system.

**No Stripe API integration, customer outreach, email, retries, account mutation, scraping, proxies, card input, secrets, or external network calls** in version 1.

### Quick start

1. Export invoices and payment events you are authorized to analyze. Before opening Apify, remove names, email addresses, physical addresses, phone numbers, cards, bank details, identifying customer IDs, authentication keys and free-text notes. Prefer only the columns below.
2. Paste the CSV or JSON text into **Invoice export** and **Payment-event export**, confirm permission, choose ISO currency, evaluation time (or blank for now) and a result limit.
3. Run. Read **Review queue (Dataset)** and **Run summary (Key-value store: SUMMARY)**. An empty queue does **not** establish that all invoices are paid.
4. Before contacting or changing anything externally, confirm invoice status, balance, dunning policy and recent events in the original billing system.

Anonymous examples: examples/input.json; examples/after-success.json demonstrates that a later paid event removes the only candidate.

### Accepted data

You may supply **CSV text** (comma-separated, RFC4180 quotes, UTF-8 BOM and CRLF supported) or **JSON text** (array of records or object with a records, invoices or paymentEvents array). **Version 1 accepts pasted export text, not file-upload URLs.** Convert downloaded CSV/JSON locally, and paste only permitted minimal fields. There is no network-based file fetch.

#### Invoice fields

| Field | Required | Meaning |
|---|---|---|
| invoice_id | yes | Stable non-personal invoice ID such as INV-1001 |
| amount_due | yes | Nonnegative monetary value in **major** units, e.g. USD 49.95; NOT 4995 cents |
| status | recommended | open, unpaid, past_due, draft, pending, overdue, partially_paid; or terminal paid, void, canceled, closed, uncollectible, written_off, forgiven, deleted, settled |
| updated_at | recommended | ISO-8601 with explicit timezone, e.g. 2026-10-10T08:30:00Z; missing is warned |
| currency | optional | Must match chosen currency when provided; no conversion |

#### Payment event fields

| Field | Required | Meaning |
|---|---|---|
| event_id | yes | Stable non-personal ID, duplicates identical only |
| invoice_id | yes | Reference to invoice ID |
| occurred_at | yes | ISO-8601 with timezone |
| event_type | yes | payment_failed, payment_succeeded, invoice_paid, invoice_voided, invoice_canceled, invoice_closed, invoice_uncollectible; common Stripe-style dotted event aliases supported |

Aliases include invoiceid, id (invoice), total_due, amount, invoice_status, event_time, created_at, timestamp, status and type. **This Actor does not automatically convert processor minor currency units into major units.**

Example invoice CSV (first two candidates before evaluating events):

```
invoice_id,amount_due,status,updated_at,currency
INV-1001,79.00,open,2026-10-11T08:00:00Z,USD
INV-1002,125.50,open,2026-10-11T08:00:00Z,USD
```

Example payment events:

```
event_id,invoice_id,occurred_at,event_type
EV-11,INV-1001,2026-10-11T09:00:00Z,payment_failed
EV-12,INV-1002,2026-10-11T09:00:00Z,payment_failed
EV-13,INV-1002,2026-10-11T10:00:00Z,payment_succeeded
```

INV-1001 is sent to a human for verification; INV-1002 is **absent** because a later successful payment appears in the export.

### Resolution rules

- Chronological per-invoice event history is bounded by **evaluationTime** (UTC / offset). Duplicate event IDs are deduplicated when identical; conflicting IDs stop the run.
- No failed event = **not queued**. Zero amount due = **not queued**. Any known terminal invoice snapshot as of the evaluation time = **not queued**.
- Any successful payment, invoice-paid, canceled, voided, closed, or uncollectible event **at/after the last failed attempt** removes that invoice. Ties favor resolution.
- A failure after an earlier success is a new manual review candidate unless a terminal invoice snapshot or later terminal event is known.
- A later unrecognized event is not silently interpreted as a known unpaid state. The review row carries additional uncertainty.
- The queue is capped by maxResults (1–1,000), sorted by oldest failed attempt then invoice ID. SUMMARY retains full counts.
- No output asserts collectible, recoverable, definitely unpaid, or suggests automated payment action.

### Output

Default Dataset: **one row per invoice needing manual verification**, with the following fields:

| Field | Example / interpretation |
|---|---|
| invoiceId | INV-1001 |
| amountDue / currency | "79.00" / USD, major units |
| lastFailedAttempt | eventId EV-11, UTC timestamp |
| knownLaterStatus | no_confirmed_resolution_in_supplied_events, or unrecognized_event |
| queueReason | failed_payment_without_confirmed_resolution_in_export |
| sourceRecordIds | invoice ID and up to 30 payment event IDs |
| uncertaintyWarning | Verify the live billing system; event history may be incomplete |
| evaluationTime / reviewOnly | ISO time / true |

Key-value store **SUMMARY** includes the number of candidates, excluded later successes/cancellations, ignored future events, and whether the output limit truncated results. **Raw exports are not copied to the Dataset or SUMMARY**.

### Privacy and safety

- **Apify retains the original Actor INPUT.** The Actor can discard unsupported fields from its own processing and output, but cannot retroactively remove sensitive material already submitted. **Remove PII, PCI/card fields and credentials BEFORE uploading.** Input-validation errors and runtime logs do not echo raw values.
- Only strictly allowlisted invoice/event IDs, known status categories, timestamps, amounts and counts enter output. IDs are validated to prevent spreadsheet formula injection and free-text identifiers.
- Input limits: 20,000 invoice rows, 100,000 event rows, 5 million characters per export; 1,000 review rows maximum. Use a recent, complete authorized export when possible.
- Human decision required. This is not debt collection advice, a credit-risk score, a compliance determination or a guarantee of successful recovery.
- No connected processor APIs. A future integration would need verified official API scope, explicit authorization and a separate release.

### Pricing and tests

**Initial pay-per-event hypothesis:** USD 0.003 per saved Dataset candidate plus USD 0.0005 per Actor run. The empty queue has no per-review-item fee. No external API, proxy, LLM or currency conversion charge. See PRICING.md for assumptions, competitor comparison and measured costs.

Local QA command: **npm ci && npm test**. The anonymous baseline must yield one Dataset item; the after-success sample must yield zero, with a SUMMARY in both cases. Public release requires local gates, low-budget cloud build/run verification, correct charged event counts and the tested build ID.

This Actor is independently published by AFK Digital. Product names of Stripe and competitors are references to the market, not affiliation.

**Apify spending limit:** The Actor checks the number of additional Dataset-item events still affordable before writing results. A low run budget may reduce the exported queue below maxResults. SUMMARY records exportedReviewCandidates, truncated, budgetLimited and a budgetNotice; only saved Dataset items incur the per-item event charge.

# Actor input Schema

## `invoiceData` (type: `string`):

Required. CSV columns: invoice_id, amount_due (major units), status, updated_at, currency (optional). JSON array with same keys also works. Strip all personal and card data before input.

## `paymentEventData` (type: `string`):

Required. CSV columns: event_id, invoice_id, occurred_at (ISO 8601 with timezone), event_type. Types include payment_failed, payment_succeeded, invoice_paid, invoice_voided, invoice_closed and invoice_canceled. Later terminal events suppress queue items.

## `permissionConfirmed` (type: `boolean`):

Confirm that the invoice and event data belongs to your organization or is otherwise authorized. Remove personal/card fields BEFORE uploading; the original Actor INPUT is retained by Apify.

## `evaluationTime` (type: `string`):

ISO-8601 date-time with Z or explicit offset, e.g. 2026-10-12T12:00:00Z. Events after this time are ignored.

## `currency` (type: `string`):

Three-letter ISO code. All invoice amounts must already be in this single currency in major units (e.g., USD 12.34, NOT 1234 cents); no FX conversion.

## `maxResults` (type: `integer`):

Return up to this number of non-resolved invoice review candidates, ordered by oldest failed attempt. A zero-result run still stores SUMMARY.

## Actor input object example

```json
{
  "invoiceData": "invoice_id,amount_due,status,updated_at,currency\nINV-1001,79.00,open,2026-10-11T08:00:00Z,USD\nINV-1002,125.50,open,2026-10-11T08:00:00Z,USD\nINV-1003,30.00,void,2026-10-11T08:00:00Z,USD",
  "paymentEventData": "event_id,invoice_id,occurred_at,event_type\nEV-11,INV-1001,2026-10-11T09:00:00Z,payment_failed\nEV-12,INV-1002,2026-10-11T09:00:00Z,payment_failed\nEV-13,INV-1002,2026-10-11T10:00:00Z,payment_succeeded\nEV-14,INV-1003,2026-10-11T11:00:00Z,payment_failed",
  "permissionConfirmed": false,
  "evaluationTime": "",
  "currency": "USD",
  "maxResults": 100
}
```

# Actor output Schema

## `reviewQueue` (type: `string`):

Invoice-level review candidates only; a known later successful payment, cancellation or close removes the row.

## `runSummary` (type: `string`):

Counts including resolved and excluded invoices; no raw export or personal information.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "invoiceData": `invoice_id,amount_due,status,updated_at,currency
INV-1001,79.00,open,2026-10-11T08:00:00Z,USD
INV-1002,125.50,open,2026-10-11T08:00:00Z,USD
INV-1003,30.00,void,2026-10-11T08:00:00Z,USD`,
    "paymentEventData": `event_id,invoice_id,occurred_at,event_type
EV-11,INV-1001,2026-10-11T09:00:00Z,payment_failed
EV-12,INV-1002,2026-10-11T09:00:00Z,payment_failed
EV-13,INV-1002,2026-10-11T10:00:00Z,payment_succeeded
EV-14,INV-1003,2026-10-11T11:00:00Z,payment_failed`
};

// Run the Actor and wait for it to finish
const run = await client.actor("afkdigital/subscription-payment-recovery-queue").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "invoiceData": """invoice_id,amount_due,status,updated_at,currency
INV-1001,79.00,open,2026-10-11T08:00:00Z,USD
INV-1002,125.50,open,2026-10-11T08:00:00Z,USD
INV-1003,30.00,void,2026-10-11T08:00:00Z,USD""",
    "paymentEventData": """event_id,invoice_id,occurred_at,event_type
EV-11,INV-1001,2026-10-11T09:00:00Z,payment_failed
EV-12,INV-1002,2026-10-11T09:00:00Z,payment_failed
EV-13,INV-1002,2026-10-11T10:00:00Z,payment_succeeded
EV-14,INV-1003,2026-10-11T11:00:00Z,payment_failed""",
}

# Run the Actor and wait for it to finish
run = client.actor("afkdigital/subscription-payment-recovery-queue").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "invoiceData": "invoice_id,amount_due,status,updated_at,currency\\nINV-1001,79.00,open,2026-10-11T08:00:00Z,USD\\nINV-1002,125.50,open,2026-10-11T08:00:00Z,USD\\nINV-1003,30.00,void,2026-10-11T08:00:00Z,USD",
  "paymentEventData": "event_id,invoice_id,occurred_at,event_type\\nEV-11,INV-1001,2026-10-11T09:00:00Z,payment_failed\\nEV-12,INV-1002,2026-10-11T09:00:00Z,payment_failed\\nEV-13,INV-1002,2026-10-11T10:00:00Z,payment_succeeded\\nEV-14,INV-1003,2026-10-11T11:00:00Z,payment_failed"
}' |
apify call afkdigital/subscription-payment-recovery-queue --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,afkdigital/subscription-payment-recovery-queue"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/bvOQM5qQ52GUGwZVJ/builds/QB2SEfoMeLstMvFrQ/openapi.json
