Cases
A case is one account in recovery. All case endpoints only see cases in your workspace.
| Method | Path | What it does |
|---|---|---|
POST |
/cases |
Create a case |
GET |
/cases |
List cases |
GET |
/cases/{id} |
Get a case |
PATCH |
/cases/{id} |
Update a case |
POST |
/cases/{id}/advance |
Run the pipeline |
POST |
/cases/import-csv |
Import cases in bulk |
Create a case
Section titled “Create a case”/casesChanges dataAPI keyCreates a case. RecovAI checks for possible duplicates (same business, filing number or identifiers); if it finds any the case starts in duplicate_review and the matches are returned.
| Field | Type | Required | Description |
|---|---|---|---|
business_name |
string | yes | Legal name of the business on the account. Up to 300 characters. |
balance |
number | Amount owed, in USD. Must be positive. | |
dba |
string | Trading name (“doing business as”). | |
legal_entity |
string | For example LLC or Corporation. |
|
industry |
string | Industry of the business. | |
product |
string | Product the account came from. | |
collateral_type |
string | Collateral described in the agreement. | |
ucc_filing_number |
string | UCC filing number, if known. | |
priority |
string | low, normal, high or urgent. |
|
business_address |
string or object | A single line, or { line1, line2, city, state, zip, country }. |
|
business_identifiers |
object | Free-form identifiers, for example { "ein": "00-0000000" }. Values are strings, numbers, booleans or null. |
|
external_reference |
string | Your own ID for the account. | |
source_system |
string | Name of the system that created the case. | |
source_record_id |
string | Record ID in that system. | |
source |
string | manual, external_sync, import, bulk_upload or folder_upload. |
|
client_id |
UUID | Client portfolio the case belongs to (agency workspaces). | |
assignee_id |
UUID | Owner of the case. | |
assignee_ids |
UUID[] | Everyone on the case, owner included. Send [] to create it unassigned. |
If you send neither assignee_id nor assignee_ids, the workspace’s default owner is used.
Example
Section titled “Example”curl https://api.recovai.ai/cases \ -H "X-API-Key: $RECOVAI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "business_name": "Example Trading LLC", "dba": "Example Goods", "balance": 48250, "priority": "high", "business_address": { "line1": "100 Sample Street", "city": "Springfield", "state": "IL", "zip": "62701" }, "external_reference": "ACCT-000123", "source": "external_sync", "source_system": "servicing" }'<?php$ch = curl_init('https://api.recovai.ai/cases');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => json_encode([ 'business_name' => 'Example Trading LLC', 'dba' => 'Example Goods', 'balance' => 48250, 'priority' => 'high', 'business_address' => [ 'line1' => '100 Sample Street', 'city' => 'Springfield', 'state' => 'IL', 'zip' => '62701', ], 'external_reference' => 'ACCT-000123', 'source' => 'external_sync', 'source_system' => 'servicing', ]), CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('RECOVAI_API_KEY'), 'Content-Type: application/json'],]);$body = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);curl_close($ch);
$result = json_decode($body, true);if ($status >= 400) { throw new RuntimeException($result['error'] . ': ' . ($result['details'] ?? ''));}const res = await fetch('https://api.recovai.ai/cases', { method: 'POST', headers: { 'X-API-Key': process.env.RECOVAI_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "business_name": "Example Trading LLC", "dba": "Example Goods", "balance": 48250, "priority": "high", "business_address": { "line1": "100 Sample Street", "city": "Springfield", "state": "IL", "zip": "62701" }, "external_reference": "ACCT-000123", "source": "external_sync", "source_system": "servicing" }),});const data = await res.json();if (!res.ok) throw new Error(`${data.error}: ${data.details ?? ''}`);import osimport requests
r = requests.post( "https://api.recovai.ai/cases", headers={"X-API-Key": os.environ["RECOVAI_API_KEY"]}, json={ "business_name": "Example Trading LLC", "dba": "Example Goods", "balance": 48250, "priority": "high", "business_address": { "line1": "100 Sample Street", "city": "Springfield", "state": "IL", "zip": "62701", }, "external_reference": "ACCT-000123", "source": "external_sync", "source_system": "servicing", }, timeout=60,)data = r.json()if not r.ok: raise RuntimeError(f"{data['error']}: {data.get('details', '')}")Response 201 Created
Section titled “Response 201 Created”{ "case": { "id": "5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a", "case_number": "RA-2026-K7QX2", "business_name": "Example Trading LLC", "dba": "Example Goods", "balance": "48250", "priority": "high", "readiness": "draft", "main_blocker": "No documents uploaded", "external_reference": "ACCT-000123", "source": "external_sync", "assignee_id": null, "row_version": 1, "closed_at": null, "created_at": "2026-10-14T15:42:07.000Z" }, "duplicates": []}When duplicates are found, readiness is duplicate_review and duplicates lists the matching cases with case_number, match_reason and confidence.
Errors
Section titled “Errors”400 invalid_client, 400 invalid_assignee, 422 validation_error.
List cases
Section titled “List cases”/casesRead onlyAPI keyQuery parameters
Section titled “Query parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
closure |
string | open |
open, closed or all. |
readiness |
string | Only cases with this readiness. | |
q |
string | Search business name, case number, filing number, client, assignee or target names. | |
take |
integer | 50 |
Page size, 1–100. |
skip |
integer | 0 |
Number of cases to skip. |
Newest cases come first.
Example
Section titled “Example”curl "https://api.recovai.ai/cases?readiness=targets_ready&take=20" \ -H "X-API-Key: $RECOVAI_API_KEY"<?php$ch = curl_init('https://api.recovai.ai/cases?readiness=targets_ready&take=20');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('RECOVAI_API_KEY')],]);$body = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);curl_close($ch);
$result = json_decode($body, true);if ($status >= 400) { throw new RuntimeException($result['error'] . ': ' . ($result['details'] ?? ''));}const res = await fetch('https://api.recovai.ai/cases?readiness=targets_ready&take=20', { method: 'GET', headers: { 'X-API-Key': process.env.RECOVAI_API_KEY },});const data = await res.json();if (!res.ok) throw new Error(`${data.error}: ${data.details ?? ''}`);import osimport requests
r = requests.get( "https://api.recovai.ai/cases?readiness=targets_ready&take=20", headers={"X-API-Key": os.environ["RECOVAI_API_KEY"]}, timeout=60,)data = r.json()if not r.ok: raise RuntimeError(f"{data['error']}: {data.get('details', '')}")Response 200 OK
Section titled “Response 200 OK”{ "cases": [ { "id": "5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a", "case_number": "RA-2026-K7QX2", "business_name": "Example Trading LLC", "balance": "48250", "readiness": "targets_ready", "priority": "high", "assignee": { "id": "9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f", "name": "Sam Example", "email": "[email protected]" }, "co_assignees": [], "primary_target": { "name": "Sample Payments Inc.", "confidence": 0.86, "status": "ready" }, "lien_expiry": null, "_count": { "case_documents": 4, "document_extractions": 12 } } ], "total": 1, "skip": 0, "take": 20, "has_more": false}Get a case
Section titled “Get a case”/cases/{id}Read onlyAPI keyExample
Section titled “Example”curl https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a \ -H "X-API-Key: $RECOVAI_API_KEY"<?php$ch = curl_init('https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('RECOVAI_API_KEY')],]);$body = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);curl_close($ch);
$result = json_decode($body, true);if ($status >= 400) { throw new RuntimeException($result['error'] . ': ' . ($result['details'] ?? ''));}const res = await fetch('https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a', { method: 'GET', headers: { 'X-API-Key': process.env.RECOVAI_API_KEY },});const data = await res.json();if (!res.ok) throw new Error(`${data.error}: ${data.details ?? ''}`);import osimport requests
r = requests.get( "https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a", headers={"X-API-Key": os.environ["RECOVAI_API_KEY"]}, timeout=60,)data = r.json()if not r.ok: raise RuntimeError(f"{data['error']}: {data.get('details', '')}")Returns the full case: its fields, current documents, extracted data, duplicate checks, the 50 most recent timeline events, and everyone assigned.
Response 200 OK (abridged)
Section titled “Response 200 OK (abridged)”{ "case": { "id": "5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a", "case_number": "RA-2026-K7QX2", "business_name": "Example Trading LLC", "balance": "48250", "readiness": "strategy_ready", "main_blocker": null, "row_version": 4, "assignee": { "id": "9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f", "name": "Sam Example", "email": "[email protected]" }, "assignees": [ { "id": "9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f", "name": "Sam Example", "role": "owner" } ], "case_documents": [ { "id": "0d9e8f7a-6b5c-4d3e-a2f1-0e9d8c7b6a50", "type": "contract", "status": "verified", "original_file_name": "agreement.pdf" } ], "document_extractions": [ ], "case_events": [ { "event_type": "case_created", "description": "Case RA-2026-K7QX2 created for Example Trading LLC", "created_at": "2026-10-14T15:42:07.000Z" } ], "packet_review": null }}Errors
Section titled “Errors”404 case_not_found.
Update a case
Section titled “Update a case”/cases/{id}Changes dataAPI keyChanges one or more fields. Send expected_row_version from your last read (see Safe concurrent updates).
| Field | Type | Description |
|---|---|---|
expected_row_version |
integer | Required. The case’s row_version when you read it. |
business_name, dba, industry, product, collateral_type, ucc_filing_number, legal_entity |
string | As in Create a case. |
balance |
number | Amount owed. |
priority |
string | low, normal, high or urgent. |
readiness |
string | Move the case to another readiness. Some moves are only allowed when the case meets their conditions. |
status_note |
string | Why you changed readiness. Shown on the timeline. |
main_blocker |
string or null | What is holding the case up. |
business_address |
As in Create a case. | |
website |
string or null | The business’s website, as a full address. |
assignee_id |
UUID or null | Change the owner, keeping the rest of the team. |
assignee_ids |
UUID[] | Replace the whole team, owner included. |
client_id |
UUID or null | Move the case to another client portfolio. |
Example
Section titled “Example”curl -X PATCH https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a \ -H "X-API-Key: $RECOVAI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "expected_row_version": 4, "balance": 46100, "priority": "urgent" }'<?php$ch = curl_init('https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'PATCH', CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => json_encode([ 'expected_row_version' => 4, 'balance' => 46100, 'priority' => 'urgent', ]), CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('RECOVAI_API_KEY'), 'Content-Type: application/json'],]);$body = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);curl_close($ch);
$result = json_decode($body, true);if ($status >= 400) { throw new RuntimeException($result['error'] . ': ' . ($result['details'] ?? ''));}const res = await fetch('https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a', { method: 'PATCH', headers: { 'X-API-Key': process.env.RECOVAI_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "expected_row_version": 4, "balance": 46100, "priority": "urgent" }),});const data = await res.json();if (!res.ok) throw new Error(`${data.error}: ${data.details ?? ''}`);import osimport requests
r = requests.patch( "https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a", headers={"X-API-Key": os.environ["RECOVAI_API_KEY"]}, json={ "expected_row_version": 4, "balance": 46100, "priority": "urgent", }, timeout=60,)data = r.json()if not r.ok: raise RuntimeError(f"{data['error']}: {data.get('details', '')}")Response 200 OK
Section titled “Response 200 OK”{ "case": { "id": "5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a", "balance": "46100", "priority": "urgent", "row_version": 5 }, "tasks_moved": 0}When the owner changes, open tasks move to the new owner; tasks_moved says how many.
Errors
Section titled “Errors”400 no_changes, 400 invalid_website, 400 invalid_assignee, 404 case_not_found, 409 case_closed, 409 when expected_row_version is out of date, 422 validation_error.
Run the pipeline
Section titled “Run the pipeline”/cases/{id}/advanceChanges dataAPI keyRuns every automatic step the case is ready for: readiness checks, lien filing lookup, priority suggestion, target discovery and strategy generation. It is safe to call repeatedly and never sends anything outside RecovAI.
No body. The work runs in the background.
Example
Section titled “Example”curl -X POST https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a/advance \ -H "X-API-Key: $RECOVAI_API_KEY"<?php$ch = curl_init('https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a/advance');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('RECOVAI_API_KEY')],]);$body = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);curl_close($ch);
$result = json_decode($body, true);if ($status >= 400) { throw new RuntimeException($result['error'] . ': ' . ($result['details'] ?? ''));}const res = await fetch('https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a/advance', { method: 'POST', headers: { 'X-API-Key': process.env.RECOVAI_API_KEY },});const data = await res.json();if (!res.ok) throw new Error(`${data.error}: ${data.details ?? ''}`);import osimport requests
r = requests.post( "https://api.recovai.ai/cases/5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a/advance", headers={"X-API-Key": os.environ["RECOVAI_API_KEY"]}, timeout=60,)data = r.json()if not r.ok: raise RuntimeError(f"{data['error']}: {data.get('details', '')}")Response 202 Accepted
Section titled “Response 202 Accepted”{ "job_id": "7e6d5c4b-3a29-4180-9f7e-6d5c4b3a2918", "status": "queued" }If the pipeline is already running for this case you get the existing job, with "already_running": true. Follow it with GET /jobs/{jobId}.
Errors
Section titled “Errors”404 case_not_found, 409 case_closed.
Import cases in bulk
Section titled “Import cases in bulk”/cases/import-csvChanges dataAPI keyCreates up to 500 cases in one request, for example from a CRM export. Despite the name, the body is JSON: one object per row. Imported cases start as draft (or duplicate_review) with no documents, owned by the workspace’s default owner.
| Field | Type | Required | Description |
|---|---|---|---|
rows |
object[] | yes | 1–500 rows. |
Each row can have:
| Field | Description |
|---|---|
business_name |
Required. Rows without it are skipped. |
dba |
Trading name. |
ucc_filing_number |
UCC filing number. A row whose filing number matches an existing case is skipped. |
balance |
Amount owed. Accepts plain numbers or text like "$48,250.00". |
industry, product |
As in Create a case. |
client_name |
Name of an existing client portfolio (matched case-insensitively). |
ein |
Employer identification number. |
state |
State the filing was made in. |
Example
Section titled “Example”curl https://api.recovai.ai/cases/import-csv \ -H "X-API-Key: $RECOVAI_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "rows": [ { "business_name": "Example Trading LLC", "balance": "$48,250.00", "state": "IL" }, { "business_name": "Sample Logistics Inc.", "ucc_filing_number": "2026-0000123" } ] }'<?php$ch = curl_init('https://api.recovai.ai/cases/import-csv');curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => json_encode([ 'rows' => [ [ 'business_name' => 'Example Trading LLC', 'balance' => '$48,250.00', 'state' => 'IL', ], [ 'business_name' => 'Sample Logistics Inc.', 'ucc_filing_number' => '2026-0000123', ], ], ]), CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('RECOVAI_API_KEY'), 'Content-Type: application/json'],]);$body = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);curl_close($ch);
$result = json_decode($body, true);if ($status >= 400) { throw new RuntimeException($result['error'] . ': ' . ($result['details'] ?? ''));}const res = await fetch('https://api.recovai.ai/cases/import-csv', { method: 'POST', headers: { 'X-API-Key': process.env.RECOVAI_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ "rows": [ { "business_name": "Example Trading LLC", "balance": "$48,250.00", "state": "IL" }, { "business_name": "Sample Logistics Inc.", "ucc_filing_number": "2026-0000123" } ] }),});const data = await res.json();if (!res.ok) throw new Error(`${data.error}: ${data.details ?? ''}`);import osimport requests
r = requests.post( "https://api.recovai.ai/cases/import-csv", headers={"X-API-Key": os.environ["RECOVAI_API_KEY"]}, json={ "rows": [ { "business_name": "Example Trading LLC", "balance": "$48,250.00", "state": "IL", }, { "business_name": "Sample Logistics Inc.", "ucc_filing_number": "2026-0000123", }, ], }, timeout=60,)data = r.json()if not r.ok: raise RuntimeError(f"{data['error']}: {data.get('details', '')}")Response 201 Created
Section titled “Response 201 Created”{ "results": [ { "id": "5b0e2c1a-7d4f-4c2b-9a61-2f8e3d4c5b6a", "case_number": "RA-2026-K7QX2", "business_name": "Example Trading LLC", "action": "created" }, { "business_name": "Sample Logistics Inc.", "action": "skipped", "reason": "already exists as RA-2026-M4PZ8 (same UCC number)" } ], "created": 1, "skipped": 1}action is created, created_duplicate_review (created, but held as a possible duplicate) or skipped (with a reason).
Errors
Section titled “Errors”400 no_rows, 400 too_many_rows.
Readiness values
Section titled “Readiness values”| Value | Meaning |
|---|---|
draft |
Just created. |
needs_core_documents |
Waiting for required documents. |
ready_for_extraction |
Documents are in; ready to be read. |
extraction_in_review |
Extracted data needs a person to check it. |
ready_for_compliance_priority_review |
Ready for compliance and priority review. |
ready_for_target_discovery |
Ready to look for targets. |
strategy_ready |
A recovery strategy is prepared. |
targets_ready |
Targets are identified and confirmed. |
ready_to_submit |
Ready for a person to send notices. |
submitted |
Notices have been sent. |
response_received |
A response came back. |
no_response |
No response within the expected time. |
resolution_pending |
Money is moving; resolution in progress. |
blocked |
Something is blocking progress; see main_blocker. |
duplicate_review |
Possible duplicate of another case. |
failed_processing |
Automatic processing failed; see main_blocker. |