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

# Create a contact list

> Turn rows into a stored list you can call: an upload from [`/contact-lists/uploads`](/api-reference/contacts/upload-a-contact-file), inline CSV or pasted lines, or a Google Sheet. The import validates rows and deduplicates by phone number, keeping only contacts that can be dialed. Pass the returned `listId` as a campaign's `config.contacts.listId`. A list is never edited — to call a different set of people, import again. To see the outcome before creating: [dry-run](/api-reference/contacts/dry-run-a-contact-import) inline rows or a sheet, [re-preview](/api-reference/contacts/re-preview-an-upload-with-a-mapping) an uploaded file. Column naming rules: [Special column names](/voice-agents/campaigns/contacts#special-column-names).



## OpenAPI

````yaml /voice-agents/api-reference/openapi.yaml post /contact-lists
openapi: 3.1.0
info:
  title: UpliftAI Voice Agents API
  version: '1.0'
  description: >-
    Build voice assistants, start conversations on the web or the phone, run
    calling campaigns, and receive outcomes by webhook. All endpoints take a
    bearer API key unless noted otherwise.
servers:
  - url: https://api.upliftai.org/v1
security:
  - apiKey: []
tags:
  - name: Assistants
  - name: Authoring an assistant
  - name: Starting a conversation
  - name: Sessions & call records
  - name: Campaigns
  - name: Campaign outcomes
  - name: Contacts
  - name: Google Sheet sources
  - name: Integrations & delivery health
paths:
  /contact-lists:
    post:
      tags:
        - Contacts
      summary: Create a contact list
      description: >-
        Turn rows into a stored list you can call: an upload from
        [`/contact-lists/uploads`](/api-reference/contacts/upload-a-contact-file),
        inline CSV or pasted lines, or a Google Sheet. The import validates rows
        and deduplicates by phone number, keeping only contacts that can be
        dialed. Pass the returned `listId` as a campaign's
        `config.contacts.listId`. A list is never edited — to call a different
        set of people, import again. To see the outcome before creating:
        [dry-run](/api-reference/contacts/dry-run-a-contact-import) inline rows
        or a sheet,
        [re-preview](/api-reference/contacts/re-preview-an-upload-with-a-mapping)
        an uploaded file. Column naming rules: [Special column
        names](/voice-agents/campaigns/contacts#special-column-names).
      operationId: createContactList
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContactListRequest'
            examples:
              uploadedFile:
                summary: From an uploaded file
                value:
                  name: Al-Falah payment reminders — August
                  kind: csv_upload
                  uploadId: f352c8d3-xxxx-xxxx-xxxx-xxxxxxxxxxxx
              pastedRows:
                summary: From pasted rows
                value:
                  name: Walk-in leads — Tuesday
                  kind: paste
                  data: |-
                    عائشہ صدیقی, +923001234567
                    بلال احمد, 03214567890
              googleSheet:
                summary: One-time pull of a Google Sheet
                value:
                  name: Event invitees — Annual Dinner
                  kind: google_sheets
                  ref: 1kQ7bP2xLd9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx!Contacts
      responses:
        '201':
          description: The stored list.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContactList'
              example:
                listId: 590e4881-xxxx-xxxx-xxxx-xxxxxxxxxxxx
                organizationId: a12b7e74-xxxx-xxxx-xxxx-xxxxxxxxxxxx
                projectId: c782a4ec-xxxx-xxxx-xxxx-xxxxxxxxxxxx
                name: Al-Falah payment reminders — August
                sourceKind: csv_upload
                sourceRef: f352c8d3-xxxx-xxxx-xxxx-xxxxxxxxxxxx
                summary:
                  willBeCalled: 2
                  excluded:
                    invalidPhone: 1
                    missingName: 1
                    duplicate: 1
                    dnc: 0
                    recentlyCalled: 0
                  errors:
                    - row: 4
                      issue: duplicate
                      value: '+923001234567'
                    - row: 5
                      issue: missingName
                      value: '+923451234567'
                    - row: 6
                      issue: invalidPhone
                      value: '0300123'
                contactCount: 2
                createdAt: '2026-08-18T05:20:09.138Z'
                updatedAt: '2026-08-18T05:20:09.138Z'
                version: 1
        '400':
          description: Validation failed, or the request carried no rows.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                validation:
                  summary: The body failed validation
                  value:
                    message:
                      - name must be shorter than or equal to 120 characters
                      - name should not be empty
                      - name must be a string
                    error: Bad Request
                    statusCode: 400
                noRows:
                  summary: Neither uploadId nor data was sent
                  value:
                    message: csv_upload requires `data`
                    error: Bad Request
                    statusCode: 400
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No such upload in the API key's project.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                message: 'Upload not found: 00000000-0000-4000-8000-000000000000'
                error: Not Found
                statusCode: 404
components:
  schemas:
    CreateContactListRequest:
      oneOf:
        - title: Uploaded file
          type: object
          required:
            - name
            - kind
            - uploadId
          properties:
            name:
              type: string
              minLength: 1
              maxLength: 120
            kind:
              type: string
              enum:
                - csv_upload
            uploadId:
              type: string
              format: uuid
              description: >-
                An upload from
                [`/contact-lists/uploads`](/api-reference/contacts/upload-a-contact-file),
                reusable for more than one list.
            mapping:
              allOf:
                - $ref: '#/components/schemas/ContactColumnMapping'
              description: Overrides the columns detected when the file was uploaded.
        - title: Inline CSV or pasted rows
          type: object
          required:
            - name
            - kind
            - data
          properties:
            name:
              type: string
              minLength: 1
              maxLength: 120
            kind:
              type: string
              enum:
                - csv_upload
                - paste
              description: >-
                `csv_upload` for CSV text with a header row; `paste` for
                one-line-per-contact rows.
            data:
              type: string
              description: >-
                The rows themselves: CSV text for `csv_upload`, or one `نام,
                +923001234567` per line for `paste`.
            mapping:
              allOf:
                - $ref: '#/components/schemas/ContactColumnMapping'
              description: Overrides column auto-detection.
        - title: Google Sheet (one-time pull)
          type: object
          required:
            - name
            - kind
            - ref
          properties:
            name:
              type: string
              minLength: 1
              maxLength: 120
            kind:
              type: string
              enum:
                - google_sheets
            ref:
              type: string
              description: >-
                Spreadsheet to pull, as `{spreadsheetId}` or
                `{spreadsheetId}!{tab}`. The rows are copied once, now — to
                re-pull the sheet at every launch, [register a contact
                source](/api-reference/google-sheet-sources/register-a-google-sheet-as-a-source)
                instead.
            connectionId:
              type: string
              format: uuid
              description: >-
                Reads the sheet through a Google connection instead of the
                public path.
            mapping:
              allOf:
                - $ref: '#/components/schemas/ContactColumnMapping'
              description: Overrides column auto-detection.
    ContactList:
      type: object
      properties:
        listId:
          type: string
          format: uuid
        organizationId:
          type: string
          format: uuid
        projectId:
          type: string
          format: uuid
        name:
          type: string
        sourceKind:
          type: string
          enum:
            - csv_upload
            - paste
            - google_sheets
            - customer_api
        sourceRef:
          type: string
          description: >-
            Present only when the import supplied an `uploadId` or a sheet `ref`
            — inline rows have none, even when labelled `csv_upload`.
        summary:
          $ref: '#/components/schemas/ImportSummary'
        contactCount:
          type: integer
          description: Contacts stored — the same number as `summary.willBeCalled`.
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
          description: Equal to `createdAt`, since a list is never edited.
        version:
          type: integer
          description: Always `1`.
    Error:
      type: object
      properties:
        message:
          description: >-
            One message per failed field for validation errors, a single string
            otherwise.
          oneOf:
            - type: string
            - type: array
              items:
                type: string
        error:
          type: string
        statusCode:
          type: integer
    ContactColumnMapping:
      type: object
      description: >-
        Column overrides. Whatever you leave out is auto-detected from the
        headers.
      properties:
        nameColumn:
          type: string
          description: >-
            Header name (case-insensitive) or A1 column letter. An explicit
            value never falls back to auto-detection — if it matches no column,
            names read as empty and the preview reports it in `mappingIssues`.
          example: Name
        phoneColumn:
          type: string
          description: Header name or A1 column letter, resolved like `nameColumn`.
          example: B
        attributeColumns:
          type: array
          items:
            type: string
          description: >-
            Columns to carry as per-contact attributes, usable as
            personalization variables. Exact header names only — no A1 letters.
            Omit to keep every column that is not the name or phone column; send
            `[]` to keep none.
          example:
            - City
            - Appointment
    ImportSummary:
      type: object
      description: What the import kept and dropped.
      properties:
        willBeCalled:
          type: integer
          description: Rows that became contacts.
        excluded:
          type: object
          description: >-
            Dropped rows by reason. `dnc` and `recentlyCalled` are always `0` —
            those checks do not run yet.
          properties:
            invalidPhone:
              type: integer
            missingName:
              type: integer
            duplicate:
              type: integer
              description: >-
                Same number seen twice. Numbers are compared as E.164, so
                `03001234567` and `+923001234567` collapse to one contact,
                keeping the first spelling.
            dnc:
              type: integer
            recentlyCalled:
              type: integer
        errors:
          type: array
          description: The dropped rows, first 20 only.
          items:
            type: object
            properties:
              row:
                type: integer
                description: Line in the source file, counting the header as line 1.
              issue:
                type: string
                enum:
                  - invalidPhone
                  - missingName
                  - duplicate
                  - dnc
                  - recentlyCalled
              value:
                type: string
                description: >-
                  The row's phone number: raw source text for `invalidPhone` and
                  `missingName`, normalized for `duplicate`.
  responses:
    Unauthorized:
      description: A bearer key was sent but is not recognized.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: invalid authorization
            error: Unauthorized
            statusCode: 401
    Forbidden:
      description: No API key sent, or the key lacks permission for this resource.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Forbidden resource
            error: Forbidden
            statusCode: 403
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: Project API key (`sk_api_…`).

````