---
title: "Create Default Waterfall Config"
method: POST
path: "/enrichment/waterfall/configs"
tags: ["Enrichment"]
---

# Create Default Waterfall Config

`POST /enrichment/waterfall/configs`

## Request

- Base URL: `https://be.graph8.com/api/v1`
- URL: `https://be.graph8.com/api/v1/enrichment/waterfall/configs`
- Auth: HTTP bearer

Create (or reuse) a waterfall pipeline on a list.

Three call-styles (mutually exclusive between `providers[]` and `steps[]`):

* **Bare** — pass only `list_id`. Builds a single-step graph8 lookup.
* **By providers** — pass `providers=["hunter","prospeo"]`. Builds default
  email-finder steps in that order.
* **By steps** — pass `steps=[...]` with full step shapes. Persisted
  verbatim (matches the canonical `WaterfallStepDto` shape the UI saves).

`email_verification` may be combined with any call-style.

Idempotent by `(list_id, name)`: if a pipeline with the same name already
exists on this list, returns its column_id with `created=False` instead
of creating a duplicate. Mirrors the dedup behaviour of the UI's
`POST /enrichments/waterfall/save` endpoint.

## Headers

- `Authorization` string, nullable
- `X-Target-Org-Id` string, nullable

## Request body

- CreateDefaultWaterfallConfigRequest: Create (or reuse) a waterfall pipeline for a list. Three call-styles, in increasing order of control: 1. **Bare** — pass only `list_id`. Server builds a single-step graph8 lookup pipeline that fills name/email/company for 0 credits. Backward-compatible with the pre-2026-06 contract. 2. **By providers** — pass `providers=["hunter","prospeo"]`. Server maps each provider to its default email-finder action (see provider matrix in `docs/developer-api-reference.md`) and ordered steps. Use this when you want a 1-line "try these providers, in this order" pipeline. 3. **By steps** — pass `steps=[...]` with full step shapes. Server persists them verbatim. Use this when you need provider-specific config (e.g. apollo `reveal_phone_numbers=true`). `providers[]` and `steps[]` are mutually exclusive — pass one or neither, not both. `email_verification` may be combined with any call-style; when set it runs after the main waterfall and follows the same credit + valid-status rules the UI uses. `list_id` is required; everything else has a sensible default so callers pass the minimum. `skip_existing_values` defaults True so we never overwrite populated fields. Idempotent by `(list_id, name)`.
  - `list_id` integer, required: List ID to attach the pipeline to
  - `name` string: Human-readable name for the pipeline
  - `field_to_enrich` string: Target field identifier for this pipeline (legacy; ignored when `field` is set)
  - `field` string, nullable: Canonical enrichment column to fill — CONTACT_DIRECT_PHONE, CONTACT_MOBILE_PHONE, CONTACT_WORK_EMAIL, CONTACT_PERSONAL_EMAILS, CONTACT_LINKEDIN_URL, COMPANY_PHONE, COMPANY_LINKEDIN_URL — or a friendly synonym ('phone', 'mobile', 'email', 'linkedin', 'company_phone'). When set, the pipeline is built from the server's per-field waterfall TEMPLATE, so each provider runs its CORRECT action for that column (e.g. leadmagic `mobile_finder` for a phone, not `email_finder`), and the enriched value is written into the real contact/company column — exactly like the web UI's 'enrich existing column' flow. Combine with `providers[]` to keep only those providers from the template (their correct per-field action is preserved). Mutually exclusive with `steps[]`. When omitted, the legacy bare/`providers[]` email-finder behavior is unchanged.
  - `skip_existing_values` boolean: If true, the pipeline skips contacts where the target field is already populated
  - `providers` string[], nullable: Ordered list of provider names — server builds default email-finder steps in this order. Mutually exclusive with `steps`. Allowed providers: graph8, hunter, prospeo, dropcontact, icypeas, leadmagic, emaillistverify, apollo, lusha, rocketreach. Use `steps[]` for full control.
  - `steps` WaterfallStepInput[], nullable: Full caller-supplied step list (advanced). Persisted verbatim into the pipeline. Mutually exclusive with `providers`.
    - `id` string, nullable: Step identifier. Auto-generated (`step_<provider>_<uuid8>`) if omitted.
    - `provider` string, required: Provider name: graph8, hunter, prospeo, dropcontact, apollo, ...
    - `action` string, required: Provider action: lookup_person, find_email, enrich_person, ...
    - `action_display_name` string, nullable: Human-readable label shown in UI. Auto-titled from `action` if omitted.
    - `output_field` string, nullable: Dotted path the worker writes into (e.g. 'contact.work_email').
    - `input_mapping` object: Maps provider input names to graph8 column identifiers (CONTACT_*).
    - `output_mapping` object: Optional output field remap (deprecated — prefer `output_field`).
    - `config` object: Provider-specific options (e.g. apollo `reveal_phone_numbers=true`).
    - `order` integer: 0-indexed execution order.
    - `enabled` boolean
    - `is_system_provider` boolean: True = use graph8-managed credentials + charge credits. False = use org BYOK.
    - `stop_on_success` boolean
    - `confidence_threshold` number
    - `max_retries` integer
  - `email_verification` EmailVerificationInput: Email verification config attached to a waterfall pipeline. Mirrors the canonical `EmailVerificationConfigDto` in `enrichment/interfaces/fastapi/waterfall_dtos.py` so the dev-API and the UI persist an identical settings shape (the CF worker reads `settings. email_verification.*` regardless of which surface saved the pipeline).
    - `enabled` boolean: Enable post-enrichment email verification
    - `provider` 'zerobounce' | 'hunter' | 'icypeas' | 'leadmagic': Verification provider. Charges credits when use_system_credentials=True.
    - `use_system_credentials` boolean: Use graph8-managed API keys (charges credits). False = use org's BYOK keys.
    - `accept_catchall` boolean: Treat catch-all responses as valid (domains that accept any address).
    - `valid_statuses` string[]: Verification statuses to accept as valid. Typical: ['valid'] or ['valid','catch-all'].
  - `credential_mode` 'auto' | 'system' | 'byok': How to resolve each provider step's credential source. Applies to the `providers[]` shortcut and the bare default; `steps[]` payloads always honor their own per-step `is_system_provider`. 'auto' (default, matches the UI): for each provider use the org's BYOK key when one is configured (free), otherwise graph8's system key for funded providers (charges credits). 'system': force graph8 system keys for funded providers (charges credits). 'byok': force the org's own keys for every non-graph8 provider (legacy behavior — steps silently skip when no key exists). graph8 is always system-funded regardless of mode.

## Response `201`

Successful Response

- ApiResponseCreateDefaultWaterfallConfigResponse
  - `data` CreateDefaultWaterfallConfigResponse, required: Result of creating (or reusing) a default pipeline.
    - `column_id` string, required
    - `config_id` string, required
    - `name` string, required
    - `list_id` integer, nullable
    - `created` boolean: True if a new config was created; False if an existing one with the same name was reused
    - `providers` string[]: Ordered, de-duplicated provider list of the persisted (or reused) pipeline.
    - `email_verification` object, nullable: Persisted email_verification block, if any. Mirrors EmailVerificationInput shape.
    - `providers_detail` ProviderStepDetail[]: Per-step credential breakdown (system vs BYOK + readiness) for the persisted pipeline.
      - `provider` string, required
      - `is_system_provider` boolean, required: True = uses graph8-managed system key (charges credits). False = uses org BYOK key.
      - `credential_source` 'system' | 'byok', required: 'system' = graph8-funded; 'byok' = org's own key.
      - `credential_ready` boolean, required: True if the step can actually run: system steps are always ready; BYOK steps are ready only when the org has an active key for that provider.
    - `missing_credentials` string[]: Providers whose step is BYOK but the org has no active key (these steps will be skipped at run time). Empty when every step is runnable.
  - `pagination` PaginationMeta
    - `page` integer, required: Current page number (1-indexed)
    - `limit` integer, required: Items per page
    - `total` integer, required: Total number of items
    - `has_next` boolean, required: Whether there are more pages
    - `next_cursor` string, nullable: Opaque cursor for the next page; null when there are no more items. Pass it back as the `cursor` query param to fetch the following page without computing page numbers.
    - `total_available` integer, nullable: Search endpoints only: how many records really match, when known. Can be larger than `total`, which is capped at the 10,000 results that can be paged. For company search this is a distinct-company estimate. Null when unknown; absent outside search.
    - `total_is_lower_bound` boolean, nullable: Search endpoints only: true when more records match than `total` shows (the result set is larger than the 10,000 results that can be paged). Absent outside search.
    - `notes` string[], nullable: Search endpoints only: plain-language notes about the result, e.g. that more records match than can be paged, or that a reversed `between` range was put in order.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/graph8/apis/graph8-developer-api.md) · [All operations](https://skmtc.dev/graph8/apis/graph8-developer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/graph8/apis/graph8-developer-api/revisions/2e7cfa0ca921?raw)
