---
title: "Create Field"
method: POST
path: "/fields"
tags: ["Fields"]
---

# Create Field

`POST /fields`

## Request

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

Create a new custom field (column) for contacts or companies.

Set `list_id` to make the field specific to a single list, or omit it to
create a global field available across every list in the org.

Optionally pass an `enrichment` block to provision a companion waterfall
pipeline in the same request. Without this block the column exists but
has no enrichment plan, so `POST /enrichment/waterfall/enrich` later
returns 422 (`waterfall_config_missing`).

## Headers

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

## Request body

- CreateFieldRequest: Request body for creating a custom field (column).
  - `title` string, required: Column title, e.g. 'Job Level'
  - `data_type` string: Column data type: 'text', 'int' (whole number), 'numeric' (decimal) or 'boolean' (yes/no). Unknown values 400. If the name already exists with another type, the create is rejected with 409.
  - `list_id` integer, nullable: If set, field is list-specific; otherwise global across the org
  - `entity` string: Target entity: 'contacts' or 'companies'
  - `enrichment` CreateFieldEnrichmentInput: Optional enrichment-pipeline block attached at column-create time. When supplied, `POST /fields` creates a companion `enrichment_configs` row in the same request so the new column is immediately runnable via `POST /enrichment/waterfall/enrich`. Without this block the column exists but has no enrichment plan — calling /waterfall/enrich on it returns 422 (`waterfall_config_missing`). Currently only `type='waterfall'` is supported; AI / formula enrichment will follow once the rest of the agent surface lands.
    - `type` string: Pipeline kind. Only 'waterfall' is supported today.
    - `name` string, nullable: Pipeline name. Defaults to `<column title> Enrichment` if omitted.
    - `field_to_enrich` string, nullable: Target field identifier. Defaults to the new column's slug.
    - `target_field` string, nullable: Kind of data this column holds — CONTACT_DIRECT_PHONE, CONTACT_MOBILE_PHONE, CONTACT_WORK_EMAIL, CONTACT_LINKEDIN_URL, COMPANY_PHONE, ... or a friendly synonym ('phone', 'mobile', 'email', 'linkedin'). When set, the pipeline is built from the server's per-column TEMPLATE so each provider runs its CORRECT action for that data kind (e.g. leadmagic `mobile_finder` for a phone, NOT `email_finder`) — the same actions the web UI's 'Add column -> Waterfall' picks. Combine with `providers[]` to keep only those providers from the template. Mutually exclusive with `steps`. When omitted, the legacy `providers[]` email-finder shortcut is unchanged.
    - `skip_existing_values` boolean: If true, skip rows where the target field is already populated.
    - `providers` string[], nullable: Ordered list of provider names. Without `target_field`, server maps each to its default email-finder action (legacy). With `target_field`, filters the column template to these providers (correct per-field action preserved). Mutually exclusive with `steps`.
    - `steps` WaterfallStepInput[], nullable: Full caller-supplied step list. Persisted verbatim. 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'].

## Response `201`

Successful Response

- ApiResponseCreateFieldResponse
  - `data` CreateFieldResponse, required
    - `id` integer, required
    - `title` string, required
    - `data_type` string, required
    - `name` string, required
    - `list_id` integer, nullable
    - `is_global` boolean, required
    - `enrichment` CreateFieldEnrichmentResponse: Echo of the companion enrichment config created by `enrichment` block.
      - `column_id` string, required: column_id slug to pass to /enrichment/waterfall/enrich
      - `config_id` string, required
      - `name` string, required
      - `type` string, required
      - `providers` string[]
      - `email_verification` object, nullable
    - `already_existed` boolean: True when a column with this name already existed in this scope, so no new column was created and `id` is the existing column. A different `data_type` for an existing name is rejected with 409.
    - `existing_data_type` string, nullable: When `already_existed` is true: the existing column's data type.
    - `warnings` string[]: Non-blocking notices, e.g. a column with the same name already exists org-wide, so the list now shows two columns with that name.
  - `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)
