> ## Documentation Index
> Fetch the complete documentation index at: https://gtmapis.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Catch-all recovery in n8n

> Orchestrate the batch catch-all recovery flow in n8n: upload, recover, branch on the recommended action

# Catch-all recovery in n8n

Catch-all recovery is a **batch** step keyed by CSV job — which is exactly what
n8n's multi-step HTTP orchestration is good at. This flow uploads a list,
triggers recovery on the job, then branches each address by its recommended
action. One API key and the shared credit wallet cover the whole chain.

Recovery starts from standard validation results where `result = risky` and
`is_catchall = true`. It adds confidence, reason, and recommended-action fields;
it does not prove mailbox existence deterministically or convert the canonical
result to `valid`. See [Catch-all detection vs recovery](/concepts/catch-all-detection-vs-recovery)
for the validation semantics.

All requests use an **HTTP Request** node with header auth:
`X-API-Key: {{your_api_key}}`. The key needs the `email:validate:bulk` scope —
mint one from the [dashboard](https://www.gtmapis.com/dashboard/api-keys).

## 1. Upload the list

POST your CSV through the [CSV API](/guides/csv-upload) and capture the returned
`batch_id`. (For large files use the signed-URL upload path; the guide covers
both.)

## 2. Trigger recovery (HTTP Request node)

| Field   | Value                                                                      |
| ------- | -------------------------------------------------------------------------- |
| Method  | `POST`                                                                     |
| URL     | `https://api.gtmapis.com/v1/csv/jobs/{{$json.batch_id}}/recover-catchalls` |
| Headers | `X-API-Key: {{your_api_key}}`, `Content-Type: application/json`            |

The call is **idempotent per batch** — a retried node won't double-charge. It
reserves premium credits, scores the risky catch-all candidates, and finalizes
(consume per recovered lead, refund the rest) in one response:

<CodeGroup>
  ```json Response (excerpt) theme={null}
  {
    "recovery": {
      "eligible_count": 120,
      "recovered_count": 71,
      "credits_reserved": 360,
      "credits_consumed": 213,
      "credits_refunded": 147
    },
    "results": [
      {
        "email": "jane.doe@acme.com",
        "recovery_status": "send_with_caution",
        "recommended_action": "send_with_caution",
        "recovery_confidence": 0.71,
        "reason": "Name-match strong; pattern matches verified domain history; MX is a security gateway."
      }
    ]
  }
  ```
</CodeGroup>

If you'd rather poll instead of reading the trigger response, fetch results later
with `GET https://api.gtmapis.com/v1/csv/jobs/{{batch_id}}/recovery`.

## 3. Branch on the recommended action

Add an **Item Lists → Split Out** node on `results`, then a **Switch** node on
`recommended_action`:

| `recommended_action` | Route to                      |
| -------------------- | ----------------------------- |
| `safe_to_send`       | Send queue / sequencer        |
| `send_with_caution`  | Send queue (reputation-aware) |
| `manual_review`      | Human review list             |
| `suppress`           | Suppression list              |

Carry the `reason` field through so every routing decision is auditable — the
"why" behind each call, not a black-box verdict.

## 4. You only pay for recovered

Recovery charges **3 credits only on a send-worthy result**
(`likely_deliverable` / `send_with_caution`). `manual_review`, `suppress`,
`insufficient_signal`, and `provider_error` cost **0**; reserved credits are
refunded (see `credits_refunded`). The buying frame is **cost per recovered
lead**, not cost per lookup. See [Rate Limits](/guides/rate-limits) for retry and
throttling behavior in long-running flows.
