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

# Batch ingest clients into a pipeline

> Create or update up to 500 clients in one request and place them on the
input node's target stage (or on `stage_id`, if given, within the same
pipeline). The batch form of *Ingest a client into a pipeline*.

Each row is matched by `client_id`, then phone number (country-code
tolerant), then email. What happens on a match is set by `on_duplicate`:
`update` (the default) merges the row into the existing client — blank
fields never clear stored values, and the phone number is never changed;
`skip` leaves the client untouched but still places it; `fail` reports the
row. Note that the single-row endpoint defaults to *not* updating.

Rows succeed or fail individually — read `results[]` — unless
`all_or_none` is set, in which case any failure returns
`400 INGEST_ROLLED_BACK` with the same per-row detail and nothing is
written. `dry_run` returns the same shape without writing anything.



## OpenAPI

````yaml POST /api/v2/public/salesflow/ingest/{input_node_uuid}/batch
openapi: 3.1.0
info:
  title: Dialtu Public API
  version: '2.0'
  description: >-
    Programmatic access to your Dialtu account: schedule AI voice calls, create
    contacts, push leads into Salesflow pipelines, book calendar appointments
    and read WhatsApp conversation history.


    Every request is authenticated with an API key created in the dashboard
    under **Settings → API Keys**, sent as `Authorization: Bearer pk_...`.
servers:
  - url: https://api.dialtu.com
    description: Production
  - url: https://api.stg.dialtu.com
    description: Staging
security:
  - ApiKeyBearer: []
tags:
  - name: Calls
    description: Schedule outbound AI voice calls.
  - name: Clients
    description: Create contacts in your account.
  - name: Salesflow
    description: Push leads into a pipeline through an API input node.
  - name: Salesflow pipelines
    description: Read a pipeline's stages and add, move or remove clients.
  - name: Calendar
    description: Check bookable capacity, book appointments and manage waiting lists.
  - name: WhatsApp
    description: Read-only conversation history and transcripts.
paths:
  /api/v2/public/salesflow/ingest/{input_node_uuid}/batch:
    post:
      tags:
        - Salesflow
      summary: Batch ingest clients into a pipeline
      description: >-
        Create or update up to 500 clients in one request and place them on the

        input node's target stage (or on `stage_id`, if given, within the same

        pipeline). The batch form of *Ingest a client into a pipeline*.


        Each row is matched by `client_id`, then phone number (country-code

        tolerant), then email. What happens on a match is set by `on_duplicate`:

        `update` (the default) merges the row into the existing client — blank

        fields never clear stored values, and the phone number is never changed;

        `skip` leaves the client untouched but still places it; `fail` reports
        the

        row. Note that the single-row endpoint defaults to *not* updating.


        Rows succeed or fail individually — read `results[]` — unless

        `all_or_none` is set, in which case any failure returns

        `400 INGEST_ROLLED_BACK` with the same per-row detail and nothing is

        written. `dry_run` returns the same shape without writing anything.
      operationId: ingestSalesflowClientsBatch
      parameters:
        - in: path
          name: input_node_uuid
          schema:
            description: >-
              UUID of the pipeline's API input node. Copy it from the input
              node's settings in the Salesflow editor, or read it from *List API
              input nodes of a pipeline*.
            title: Input Node Uuid
            type: string
          required: true
          description: >-
            UUID of the pipeline's API input node. Copy it from the input node's
            settings in the Salesflow editor, or read it from *List API input
            nodes of a pipeline*.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SalesflowIngestBatchSchema'
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SalesflowIngestBatchResponse'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SalesflowIngestErrorResponse'
        '401':
          description: Missing, malformed or revoked API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DetailErrorResponse'
              examples:
                missing_header:
                  summary: No Authorization header
                  value:
                    detail: Unauthorized
                invalid_key:
                  summary: Unknown or revoked key
                  value:
                    detail: Invalid or expired API key. Please check your credentials.
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SalesflowIngestErrorResponse'
        '422':
          description: The request did not match the documented schema.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RequestValidationError'
              example:
                detail:
                  - type: missing
                    loc:
                      - body
                      - payload
                      - phone_number
                    msg: Field required
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SalesflowIngestErrorResponse'
      security:
        - ApiKeyBearer: []
components:
  schemas:
    SalesflowIngestBatchSchema:
      description: Request body for the batch ingest.
      examples:
        - all_or_none: false
          clients:
            - company: ACME
              data:
                plan: gold
                source: landing-page
              email: ana@example.com
              external_id: crm-1001
              first_name: Ana
              last_name: García
              phone_number: '+525512345678'
              tags:
                - name: spring-campaign
            - client_id: 8874
              data:
                plan: silver
              external_id: crm-1002
          defaults:
            area_code: '52'
            data:
              source: crm-sync
            tags:
              - name: imported
            timezone: America/Mexico_City
          dry_run: false
          on_duplicate: update
          trigger_automations: true
      properties:
        clients:
          description: 1 to 500 client rows
          items:
            $ref: '#/components/schemas/SalesflowIngestBatchRowSchema'
          maxItems: 500
          minItems: 1
          title: Clients
          type: array
        on_duplicate:
          default: update
          description: >-
            What to do when a row matches an existing client: update = merge the
            row into it (default; blank fields never clear values), skip = leave
            it untouched but still place it on the pipeline, fail = report the
            row as failed
          enum:
            - update
            - skip
            - fail
          title: On Duplicate
          type: string
        all_or_none:
          default: false
          description: >-
            If true, any failed row rolls back the whole batch (400
            INGEST_ROLLED_BACK)
          title: All Or None
          type: boolean
        dry_run:
          default: false
          description: Validate and match without writing anything
          title: Dry Run
          type: boolean
        trigger_automations:
          default: true
          description: >-
            Fire the target stage's on-arrival automations for each placed
            client
          title: Trigger Automations
          type: boolean
        stage_id:
          anyOf:
            - type: integer
            - type: 'null'
          description: >-
            Override the input node's target stage; must belong to the same
            pipeline
          title: Stage Id
        defaults:
          allOf:
            - $ref: '#/components/schemas/SalesflowIngestDefaultsSchema'
          description: Batch-wide values applied wherever a row is silent.
      required:
        - clients
      title: SalesflowIngestBatchSchema
      type: object
    SalesflowIngestBatchResponse:
      description: |-
        Success response for the batch ingest (rows may still have failed
        individually unless all_or_none was set).
      properties:
        status:
          const: success
          default: success
          description: Response status
          title: Status
          type: string
        dry_run:
          description: True when nothing was written
          title: Dry Run
          type: boolean
        input_node_uuid:
          description: The input node the batch was ingested through
          title: Input Node Uuid
          type: string
        pipeline_id:
          description: Pipeline the clients were placed on
          title: Pipeline Id
          type: integer
        stage_id:
          description: Stage the clients were placed on
          title: Stage Id
          type: integer
        summary:
          $ref: '#/components/schemas/SalesflowIngestBatchSummarySchema'
          description: Counts over all rows
        results:
          description: One result per request row, in request order
          items:
            $ref: '#/components/schemas/SalesflowIngestBatchRowResultSchema'
          title: Results
          type: array
      required:
        - dry_run
        - input_node_uuid
        - pipeline_id
        - stage_id
        - summary
        - results
      title: SalesflowIngestBatchResponse
      type: object
    SalesflowIngestErrorResponse:
      description: Error response for Salesflow client ingestion.
      properties:
        status:
          const: error
          description: Error status
          title: Status
          type: string
        error_code:
          description: Machine-readable error code
          title: Error Code
          type: string
        message:
          description: Human-readable error message
          title: Message
          type: string
        details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          description: >-
            Additional error details. For INGEST_ROLLED_BACK: the batch summary
            and per-row results, so every failing row can be fixed at once.
          title: Details
      required:
        - status
        - error_code
        - message
      title: SalesflowIngestErrorResponse
      type: object
    DetailErrorResponse:
      description: 'Plain error body: `{"detail": "..."}`. No `error_code` is included.'
      properties:
        detail:
          description: Human-readable reason for the failure.
          title: Detail
          type: string
      required:
        - detail
      title: DetailErrorResponse
      type: object
    RequestValidationError:
      title: RequestValidationError
      type: object
      description: >-
        Returned when the request body, query string or path could not be parsed
        against the documented schema. Fix the listed fields and retry.
      properties:
        detail:
          type: array
          title: Detail
          description: One entry per invalid field.
          items:
            $ref: '#/components/schemas/RequestValidationErrorItem'
      required:
        - detail
    SalesflowIngestBatchRowSchema:
      description: >-
        One client row. Needs at least one identity: client_id, phone_number or
        email.


        Matching order: client_id → phone (country-code tolerant) → email

        (case-insensitive). What happens on a match is set by the batch-level

        `on_duplicate`.
      properties:
        external_id:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            Your own identifier for this row; echoed back in results, never
            stored
          title: External Id
        client_id:
          anyOf:
            - type: integer
            - type: 'null'
          description: Match an existing client by its Dialtu id
          title: Client Id
        first_name:
          anyOf:
            - type: string
            - type: 'null'
          description: Client first name
          title: First Name
        last_name:
          anyOf:
            - type: string
            - type: 'null'
          description: Client last name
          title: Last Name
        phone_number:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            Phone number. E.164 preferred (+56912345678); national digits are
            accepted together with area_code or defaults.area_code
          title: Phone Number
        area_code:
          anyOf:
            - type: string
            - type: 'null'
          description: Country calling code (e.g. '56'); inferred from an E.164 phone
          title: Area Code
        email:
          anyOf:
            - type: string
            - type: 'null'
          description: Client email
          title: Email
        company:
          anyOf:
            - type: string
            - type: 'null'
          description: Client company
          title: Company
        timezone:
          anyOf:
            - type: string
            - type: 'null'
          description: IANA timezone; inferred from the phone number when omitted
          title: Timezone
        data:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          description: >-
            Custom fields (the client's Data tab). Merged key by key on update;
            a null value removes that key
          title: Data
        tags:
          anyOf:
            - items:
                $ref: '#/components/schemas/SalesflowIngestTagSchema'
              type: array
            - type: 'null'
          description: Tags to add to this client (appended on update)
          title: Tags
      title: SalesflowIngestBatchRowSchema
      type: object
    SalesflowIngestDefaultsSchema:
      description: Batch-wide values applied wherever a row is silent.
      properties:
        area_code:
          anyOf:
            - type: string
            - type: 'null'
          description: Country calling code for rows whose phone has none
          title: Area Code
        timezone:
          anyOf:
            - type: string
            - type: 'null'
          description: Timezone for rows where none is given or inferable
          title: Timezone
        tags:
          description: Tags added to every row
          items:
            $ref: '#/components/schemas/SalesflowIngestTagSchema'
          title: Tags
          type: array
        data:
          additionalProperties: true
          description: Custom fields applied to every row; the row's own keys win
          title: Data
          type: object
      title: SalesflowIngestDefaultsSchema
      type: object
    SalesflowIngestBatchSummarySchema:
      properties:
        total:
          default: 0
          description: Rows in the request
          title: Total
          type: integer
        created:
          default: 0
          description: Rows that created a new client
          title: Created
          type: integer
        updated:
          default: 0
          description: Rows merged into an existing client
          title: Updated
          type: integer
        skipped:
          default: 0
          description: 'Rows that matched a client left untouched (`on_duplicate: skip`)'
          title: Skipped
          type: integer
        failed:
          default: 0
          description: Rows rejected — see each row's `errors`
          title: Failed
          type: integer
        pipeline_added:
          default: 0
          description: Rows placed on the stage (new or reactivated cards)
          title: Pipeline Added
          type: integer
        pipeline_already_active:
          default: 0
          description: Rows whose client was already on the pipeline
          title: Pipeline Already Active
          type: integer
        pipeline_failed:
          default: 0
          description: Rows whose client was written but could not be placed
          title: Pipeline Failed
          type: integer
      title: SalesflowIngestBatchSummarySchema
      type: object
    SalesflowIngestBatchRowResultSchema:
      properties:
        index:
          description: Position of the row in the request
          title: Index
          type: integer
        external_id:
          anyOf:
            - type: string
            - type: 'null'
          description: Echo of the row's external_id
          title: External Id
        status:
          description: What happened to the client record
          enum:
            - created
            - updated
            - skipped
            - failed
          title: Status
          type: string
        client_id:
          anyOf:
            - type: integer
            - type: 'null'
          description: Dialtu client id (created or matched)
          title: Client Id
        matched_by:
          anyOf:
            - enum:
                - id
                - phone
                - email
              type: string
            - type: 'null'
          description: How an existing client was matched
          title: Matched By
        errors:
          description: Why the row failed (empty unless status is `failed`)
          items:
            $ref: '#/components/schemas/SalesflowIngestBatchRowErrorSchema'
          title: Errors
          type: array
        pipeline:
          anyOf:
            - $ref: '#/components/schemas/SalesflowIngestBatchRowPipelineSchema'
            - type: 'null'
          description: Pipeline placement outcome for this row
        existing_client:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          description: 'dry_run only: snapshot of the matched client before any merge'
          title: Existing Client
      required:
        - index
        - status
      title: SalesflowIngestBatchRowResultSchema
      type: object
    RequestValidationErrorItem:
      title: RequestValidationErrorItem
      type: object
      properties:
        loc:
          type: array
          title: Loc
          description: >-
            Where the problem is: the first element is `body`, `query` or
            `path`, followed by the field path.
          items:
            anyOf:
              - type: string
              - type: integer
        msg:
          type: string
          title: Msg
          description: What is wrong with the value.
        type:
          type: string
          title: Type
          description: >-
            Machine-readable validation error type, e.g. `missing` or
            `int_parsing`.
      required:
        - loc
        - msg
        - type
    SalesflowIngestTagSchema:
      description: A tag to attach to a client. Matched by exact name; created if missing.
      properties:
        name:
          description: Tag name (exact match)
          maxLength: 100
          title: Name
          type: string
        color:
          anyOf:
            - type: string
            - type: 'null'
          description: Hex colour for a newly created tag
          title: Color
        description:
          anyOf:
            - type: string
            - type: 'null'
          description: Description for a newly created tag
          title: Description
      required:
        - name
      title: SalesflowIngestTagSchema
      type: object
    SalesflowIngestBatchRowErrorSchema:
      properties:
        field:
          description: Row field the error refers to
          title: Field
          type: string
        code:
          description: Machine-readable error code
          enum:
            - required
            - invalid_format
            - too_long
            - duplicate_in_batch
            - not_found
            - exists
            - conflict
          title: Code
          type: string
        message:
          description: Human-readable explanation
          title: Message
          type: string
        existing_client_id:
          anyOf:
            - type: integer
            - type: 'null'
          description: The client this row matched (exists / conflict)
          title: Existing Client Id
        conflicting_client_id:
          anyOf:
            - type: integer
            - type: 'null'
          description: The other client that owns the conflicting phone/email
          title: Conflicting Client Id
        duplicate_of_index:
          anyOf:
            - type: integer
            - type: 'null'
          description: Index of the earlier row with the same identity (duplicate_in_batch)
          title: Duplicate Of Index
      required:
        - field
        - code
        - message
      title: SalesflowIngestBatchRowErrorSchema
      type: object
    SalesflowIngestBatchRowPipelineSchema:
      properties:
        status:
          description: Pipeline placement outcome for this row
          enum:
            - added
            - reactivated
            - already_active
            - failed
            - not_attempted
          title: Status
          type: string
        client_state_id:
          anyOf:
            - type: integer
            - type: 'null'
          description: The client's card on the pipeline, when placed or already there
          title: Client State Id
      required:
        - status
      title: SalesflowIngestBatchRowPipelineSchema
      type: object
  securitySchemes:
    ApiKeyBearer:
      type: http
      scheme: bearer
      bearerFormat: pk_...
      description: >-
        API key created in the dashboard under **Settings → API Keys**. Send it
        as `Authorization: Bearer pk_...`.

````