---
name: gridrouter-waterfalls
description: Builds and runs GridRouter waterfalls, multi-vendor recipes that return one golden record per input, from prebuilt templates (work email, mobile, verification, person, company, job change, full lead enrichment, CRM hygiene) — choosing a template and speed, pinning vendors, estimating and capping cost, sync, async and batch runs, reading provenance and conflicts, and fixing vendor_key_required. Use when enriching records through the GridRouter API or MCP server with more than one vendor.
---

# GridRouter waterfalls

A waterfall tries vendors for one capability until the stop rule holds, merges their answers field by field into a golden record, and records who supplied each field. Templates are prebuilt waterfalls; every step is a routed pool, so GridRouter picks the vendors unless you pin them.

## When to use

- One input needs several vendors (find, then verify; profile, then email), or a list of rows needs the same recipe. For a single vendor call use `/v1/run/{capability}` instead.

## Prerequisites

- API key in `$GRID_API_KEY` (never print, log or paste it) with `pipelines:read`, `pipelines:write`, `pipelines:run` (+ `lists` to poll batches). `grid_test_…` keys run only the `sandbox-*` templates (no vendor key needed; sandbox calls still draw on credit at list price).
- Vendor keys: at launch every vendor call uses your own key (BYOK). GridRouter charges $0 on it and the vendor bills its own rate; template costs are list-price guidance. Add keys in Settings > Vendor keys or `PUT /v1/credentials/{provider}` (scope `credentials:write`).
- REST base `https://api.gridrouter.io`; MCP `https://mcp.gridrouter.io/mcp`. Each MCP tool shares the REST operation's name (shown in brackets below).

## Steps

1. Pick a template (public, no key) [`waterfall_templates`, `waterfall_template_get`]: `curl -sS "https://api.gridrouter.io/v1/waterfalls/templates?runnable=true&q=email" | jq '.items[] | {id, status: .availability.status, inputs: [.inputs[].name]}'`. `GET /v1/waterfalls/templates/{id}` shows inputs, `input_rule`, outputs, `sample_input`, explained steps and `availability` (`status`, `vendor_keys` per stage, `cost` range). Only `ready` and `sandbox` run; `needs_integration` is preview only.

| Goal | Template id |
| --- | --- |
| Work email from name + domain; from a profile URL | `work-email`; `email-from-profile` |
| Mobile number; verify an email; resolve catch-alls | `mobile-number`; `email-verification`; `catch-all-resolution` |
| Who owns an email; enrich a person | `reverse-email-lookup`; `person-enrichment` |
| Job changes; people by role; staff list | `job-change-detection`; `decision-makers`; `employee-list` |
| Firmographics; everything on a lead | `company-enrichment`; `full-lead-enrichment` |
| Form fill (email in); CRM cleanup; role + verified email | `inbound-lead-enrichment`; `crm-hygiene`; `decision-maker-emails` |
| Try it free on a test key (`scenario` input scripts vendors) | `sandbox-work-email`, `sandbox-mobile-number`, `sandbox-company-enrichment`, `sandbox-email-then-mobile` |

2. Choose speed: `fast` races vendors (lowest latency, may pay more than one), `balanced` goes one at a time and hedges a slow vendor, `thorough` goes cheapest first with the longest deadline. Leave it unset to keep the template's.
3. Estimate without calling a vendor [`waterfall_estimate`]: `POST /v1/waterfalls/estimate` `{"template":"work-email","speed":"balanced","pins":{"email":["hunter"]}}` (or `{"definition":{…}}`) returns `worst_case_cost_micro`, `expected_cost_micro`, `expected_ms`, `steps[]` and `issues[]`. Money is integer micro-USD (1 USD = 1,000,000).
4. Create and publish in one call [`waterfall_create_from_template`]:

```bash
curl -sS https://api.gridrouter.io/v1/waterfalls/from-template \
  -H "Authorization: Bearer $GRID_API_KEY" -H "Idempotency-Key: wf-work-email-1" -H 'content-type: application/json' \
  -d '{"template":"work-email","speed":"balanced","max_cost_micro":100000,"pins":{"email":["hunter"]}}' | jq '{id, name, published_version}'
```

   `pins` maps a stage id to vendor slugs tried first; the router still falls back unless `"pins_only": true`. `name` defaults to the template id (`-2`, `-3`… if taken); `publish: false` keeps a draft (run it with `"version":"draft"`).
5. Run one record [`waterfall_run`]: `POST /v1/waterfalls/{id}/run` `{"input":{"first_name":"Ada","last_name":"Lovelace","domain":"example.com"},"max_cost_micro":100000}`. Sync by default (up to 50 s). Multi-stage or slow templates (`run_mode: async`): add `"async": true` (or `"wait": 20` to fall back to `202`), optional `webhook_url` (https), then poll [`waterfall_status`] `GET /v1/waterfalls/{id}/runs/{run_id}` until `status` is `completed`, `failed` or `cancelled`. `fields` trims the output.
6. Lists (`run_mode: batch`) [`waterfall_batch_run`]: `POST /v1/waterfalls/batch-run` `{"waterfall_id":"wf_…","inputs":[{…}],"max_cost_micro_per_run":50000}` (≤ 1,000 rows, or `"csv":"email\n…"`). It returns `202` with `id` and `status_url`; poll `GET /v1/lists/{id}` (`done`/`total`, `hits`, `failed`, `cost_micro`, `status`) and page results with `GET /v1/lists/{id}/rows`.

## Reading a run

- `outcome`: `hit` (every stage's stop rule held), `partial` (some fields filled), `miss`, `failed`; `stop_reason` says why it ended (`first_hit`, `exhausted`, `budget_exceeded`, `deadline_exceeded`, …).
- `data` is the golden record. `_provenance.<field>` names the `provider`, `endpoint_id`, `call_id`, `stage_id`, `confidence`, `verified` and merge `strategy`; `conflicts[]` lists other vendors' disagreeing values: surface them, don't silently drop them.
- `_attempts[]` is every vendor try with `status` (hit, miss, failed, skipped, timeout, cancelled), `reason` (`missing_input`, `over_run_budget`, `low_confidence`, …), `cost_micro`, `latency_ms` and `error`. `cost_micro` and `cost_per_field_micro` total the run.

## Verify

- The template returns `availability.status` `ready` (or `sandbox` on a test key), the estimate has no `issues`, and a run on the template's `sample_input` returns `object: "waterfall_run"` with `outcome` and `_attempts`.

## Errors

- `403 vendor_key_required`: no vendor that could run has your key. `error.message` and `error.details` name the vendors; add a key for any one of them (see Prerequisites), or re-create with `pins` (plus `pins_only`) on vendors you already have keys for. While at least one vendor can run, vendors without a key are just skipped.
- `422 validation_failed`: bad input, pin or template (`details.issues[].path`); `needs_integration` templates always return it on create.
- `stop_reason: budget_exceeded` (partial `data`): the next attempt would pass `max_cost_micro`; re-run the estimate before raising it. `404 not_found`: wrong waterfall, run or template id.
- `429`/`503`: wait `retry_after_ms`, retry with the same `Idempotency-Key`.

## Safety

- Set `max_cost_micro` on every create and run (`max_cost_micro_per_run` on batches); estimate before large batches. Develop on `grid_test_` keys and `sandbox-*` templates.
- Keys stay in env vars or a secret manager; never echo GridRouter or vendor keys. Only enrich people you have a lawful basis to process.
