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

# Create Contact List

> Create an empty list with an email side, then add emails with `POST /contact-lists/{id}/emails`. The list is ready at once, so it can be an email campaign audience as soon as it has emails. To import a file of phone numbers, emails, or both, use `POST /contact-lists/import`. Requires the email feature for the organization the list is created in. Names do not have to be unique.



## OpenAPI

````yaml /api-reference/openapi.json post /contact-lists
openapi: 3.1.0
info:
  title: Political Comms API
  summary: >-
    Direct-to-carrier political texting API for campaigns, PACs, advocacy
    organizations, fundraisers, and elected officials.
  description: >-
    Public REST API for the Political Comms platform. Surfaces include Projects
    (compose, test, schedule, send), Conversations (read inbound threads and
    reply inside them), Lists (one import for phone numbers and emails, and
    analysis), Media Files, Organizations and hierarchy, Brands, Campaigns,
    Tracking Domains, Phone Numbers, Analytics, and Billing.


    Authentication is an API key passed in the `X-API-Key` header. Keys are
    generated from the dashboard at Admin → API and are prefixed `pc_live_`. All
    POST and PATCH endpoints that mutate state are designed to be safe to retry,
    with optional `Idempotency-Key` headers for stronger guarantees. Rate limit
    is 600 requests per minute per key (bursts up to 600, refilling at 10 per
    second, the same for every scope; the `X-RateLimit-Limit` header reports
    your key's exact limit, and `Retry-After` on a 429 is the seconds until your
    next request is allowed).


    Webhooks emit `message.sent`, `message.delivered`, `message.failed`,
    `message.replied`, and `link.clicked` events. Payloads are HMAC-signed;
    validate the signature before trusting any payload.


    A Model Context Protocol (MCP) server is available at
    https://docs.politicalcomms.com/mcp for AI agents that need to search the
    documentation programmatically. The developer hub at
    https://politicalcomms.com/developers/ has quickstart examples in cURL, raw
    HTTP, and Python.
  version: 1.6.0
  termsOfService: https://politicalcomms.com/terms/
  contact:
    name: Political Comms Support
    email: support@politicalcomms.com
    url: https://docs.politicalcomms.com
  license:
    name: Proprietary
    url: https://politicalcomms.com/terms/
  x-logo:
    url: https://politicalcomms.com/images/brand/pcomms-logo-left-of-text.png
    altText: Political Comms
    backgroundColor: '#ffffff'
    href: https://politicalcomms.com/
  x-mcp:
    url: https://docs.politicalcomms.com/mcp
    discovery_url: https://docs.politicalcomms.com/.well-known/mcp
    transport: http
    auth: none
    tools:
      - search_political_comms
      - query_docs_filesystem_political_comms
      - submit_feedback
servers:
  - url: https://api.politicalcomms.com/v1
    description: Production
security:
  - ApiKeyAuth: []
tags:
  - name: Projects
    description: Create, edit, test, schedule, and inspect projects.
  - name: Conversations
    description: >-
      Read inbound threads and reply inside them from the same number.
      Conversations are created by project sends; the API never starts one.
  - name: Phone Numbers
    description: List phone numbers across 10DLC campaigns and toll-free verifications.
  - name: Toll-Free Verifications
    description: >-
      List toll-free verifications (carrier registrations) across your
      organization hierarchy.
  - name: Brands
    description: List brands across your organization hierarchy.
  - name: 10DLC Campaigns
    description: >-
      List 10DLC campaigns (carrier registrations) across your organization
      hierarchy.
  - name: Email Campaigns
    description: >-
      Create, schedule, and report on email campaigns. Pause, resume, and test
      sends are deliverability decisions a human makes while watching a send,
      and live in the dashboard.
  - name: Email Templates
    description: Create and read saved email templates.
  - name: Email Senders
    description: >-
      Read the sender identities (From addresses) on your verified domains.
      Identities carry the physical mailing address and paid-for-by disclaimer,
      so they are created and edited in the dashboard.
  - name: Email Suppressions
    description: >-
      Addresses that will not be emailed: unsubscribes, bounces, complaints, and
      addresses you add. Read, add, and lift them in bulk to keep your own
      opt-out record in sync with ours. They apply automatically to every email
      send.
  - name: Contact Lists
    description: >-
      List, import, and analyze lists. One import brings in phone numbers,
      emails, or both, or a do-not-contact file.
  - name: Media Files
    description: List, import, and fetch media files.
  - name: Sending Domains
    description: >-
      Read the sending domains in your organization and the DNS records to
      publish. Domains are added in the dashboard: DNS is published by hand, so
      creation is not part of the API.
  - name: Tracking Domains
    description: >-
      List active link-tracking domains. Use the returned ids as
      `link_tracking_domain_id` on project create/update.
  - name: Organizations
    description: List descendant organizations and hierarchy.
  - name: Billing
    description: Usage and billing data across your organization.
  - name: Analytics
    description: Message statistics and delivery performance.
paths:
  /contact-lists:
    post:
      tags:
        - Contact Lists
      summary: Create Contact List
      description: >-
        Create an empty list with an email side, then add emails with `POST
        /contact-lists/{id}/emails`. The list is ready at once, so it can be an
        email campaign audience as soon as it has emails. To import a file of
        phone numbers, emails, or both, use `POST /contact-lists/import`.
        Requires the email feature for the organization the list is created in.
        Names do not have to be unique.
      operationId: createContactList
      parameters:
        - $ref: '#/components/parameters/IdempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 255
                organization_id:
                  type: string
                  format: uuid
                  description: >-
                    Create the list in this organization (one your key can
                    reach). Defaults to the key's organization.
                description:
                  type: string
                  maxLength: 2000
                email_domain_id:
                  type: string
                  format: uuid
                  description: >-
                    Scopes the list to one sending domain. Omit for an
                    organization-wide list any campaign can use.
                acquired:
                  type: boolean
                  description: >-
                    Set this when the list came from somewhere other than your
                    own sign-up flow. Acquired lists ramp on half the normal
                    warm-up steps.
                consent:
                  type: object
                  description: >-
                    Optional. Where the email addresses came from. Allowed only
                    when `purpose` is `audience` and `email_column` is set; sent
                    with any other combination it returns `400`. Omit it and no
                    consent record is stored.
                  properties:
                    source:
                      type: string
                      enum:
                        - donation_form
                        - petition
                        - signup_form
                        - event
                        - purchased
                        - rented
                        - other
                      description: >-
                        How these people agreed to hear from you. Answer
                        honestly: `purchased` and `rented` change how the list
                        is treated, and an undeclared acquired list is the usual
                        cause of a mid-send deliverability pause.
                    note:
                      type: string
                      maxLength: 1000
                  required:
                    - source
                  additionalProperties: false
              required:
                - name
              additionalProperties: false
            example:
              name: August donors
              consent:
                source: donation_form
                note: Donate page opt-in checkbox
      responses:
        '201':
          description: List created. The same object `GET /contact-lists/{id}` returns.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    $ref: '#/components/schemas/ContactListDetail'
                required:
                  - success
                  - data
                additionalProperties: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: >-
            The API key lacks the required permission scope, the requested
            resource belongs to an organization outside the key's hierarchy, or
            the organization is not entitled to this feature.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/AuthErrorResponse'
                  - $ref: '#/components/schemas/ErrorResponse'
              examples:
                missingScope:
                  summary: Key lacks the required permission scope
                  value:
                    error: Insufficient permissions
                    message: >-
                      This API key does not have the required permissions:
                      public:read
                orgAccessDenied:
                  summary: Resource outside the key's organization hierarchy
                  value:
                    error: >-
                      Access denied: You do not have permission to access this
                      organization's data
                    code: ORG_ACCESS_DENIED
                    correlationId: de038205-7c9c-4d4a-99bc-d7275a52f07e
                entitlementRequired:
                  summary: Organization is not entitled to this feature
                  value:
                    error: This feature requires enablement by Political Comms
                    code: ENTITLEMENT_REQUIRED
                    details:
                      entitlement: email
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  parameters:
    IdempotencyKeyHeader:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        minLength: 16
        maxLength: 200
      description: >-
        Optional idempotency key: a unique string of 16-200 printable ASCII
        characters (a UUID is recommended). Retrying the write with the same key
        within 24 hours returns the stored response of the first call with an
        `X-Idempotent-Replayed: true` response header instead of executing it
        again. Reusing a key with a different request body returns `422`
        (`IDEMPOTENCY_MISMATCH`); a duplicate sent while the first call is still
        running returns `409` with a `Retry-After` header. Keys are scoped per
        endpoint and organization.
  schemas:
    ContactListDetail:
      type: object
      properties:
        list_id:
          type: string
        name:
          type: string
        brand_id:
          type:
            - string
            - 'null'
        organization_id:
          type:
            - string
            - 'null'
        imported_via_api:
          type: boolean
          description: True when the list was created through POST /contact-lists/import.
        created_at:
          type: string
          format: date-time
        channels:
          type: array
          items:
            type: string
            enum:
              - text
              - email
          description: >-
            `text` when the list has phone numbers (or has no emails), `email`
            when it has emails. One list can carry both; its emails are under
            `email` and listed on `GET /contact-lists/{id}/emails`.
        available_merge_tags:
          type: array
          description: >-
            The merge tags this list offers in message content, one per mapped
            column. When the list was imported without `merge_tags`, these are
            the tags assigned automatically.
          items:
            type: object
            properties:
              tag:
                type: string
                description: The merge tag name. Use it in message content as `{tag}`.
              column:
                type: string
                description: The CSV column the tag reads.
              standard:
                type: boolean
                description: >-
                  True for a standard field such as first_name; false for a
                  custom tag.
        import:
          type: object
          properties:
            status:
              type: string
              enum:
                - uploading
                - processing
                - ready
                - failed
            progress:
              type: integer
              description: >-
                Import progress percentage (0-100). Always 100 once status is
                ready.
            total_rows:
              type:
                - integer
                - 'null'
            total_contacts:
              type:
                - integer
                - 'null'
            clean_contacts:
              type:
                - integer
                - 'null'
            duplicate_count:
              type:
                - integer
                - 'null'
            bad_number_count:
              type:
                - integer
                - 'null'
            opted_out_count:
              type:
                - integer
                - 'null'
            processing_started_at:
              type:
                - string
                - 'null'
            processing_completed_at:
              type:
                - string
                - 'null'
          additionalProperties: true
        analysis:
          type: object
          properties:
            status:
              type: string
              enum:
                - not_started
                - processing
                - complete
                - failed
            analyzed_numbers:
              type: integer
            breakdown:
              type: object
              properties:
                mobile:
                  type: integer
                landline:
                  type: integer
                voip:
                  type: integer
                invalid:
                  type: integer
              additionalProperties: true
              description: Present only after at least one number has been analyzed.
          additionalProperties: true
        downloads:
          type: object
          description: >-
            API URLs for `GET /contact-lists/{id}/download`. Authenticate with
            `X-API-Key`; the URLs do not expire.
          properties:
            original_url:
              type: string
            analyzed_url:
              type:
                - string
                - 'null'
              description: Null until `analysis.status` is `complete`.
          additionalProperties: true
        email:
          oneOf:
            - $ref: '#/components/schemas/ContactListEmail'
            - type: 'null'
          description: The email side of the list, or null when it has no emails.
      additionalProperties: true
    AuthErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Short error summary.
        message:
          type: string
          description: Human-readable detail.
      required:
        - error
        - message
      additionalProperties: true
      description: >-
        Error body returned by the API-key authentication layer (401 and
        permission 403s).
    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          description: Human-readable error message.
        code:
          type: string
          description: >-
            Machine-readable error code, e.g. VALIDATION_ERROR,
            ORG_ACCESS_DENIED, PROJECT_NOT_FOUND.
        correlationId:
          type: string
          description: Request correlation ID; include it in support requests.
        details:
          type:
            - array
            - object
          description: >-
            Present on some errors. For VALIDATION_ERROR, an array of { field,
            message, code } with one entry per rejected field (field is the
            dotted path); an object for other codes (e.g. insufficient-balance
            shortfall).
      required:
        - error
        - code
      additionalProperties: true
      description: >-
        Standard error body produced by the API error handler for 4xx/5xx
        responses.
    ContactListEmail:
      type: object
      description: >-
        A list's email side: its import status, counts and settings. The list's
        id and name are the list's own.
      properties:
        description:
          type:
            - string
            - 'null'
        source_type:
          type: string
          enum:
            - uploaded
            - segmented
            - winred
            - anedot
        status:
          type: string
          enum:
            - processing
            - ready
            - failed
            - archived
          description: >-
            The email side's import status. After `POST /contact-lists/import`
            with an `email_column`, poll `GET /contact-lists/{id}` until this is
            `ready` or `failed`.
        email_domain_id:
          type:
            - string
            - 'null'
          format: uuid
          description: >-
            The sending domain this list is scoped to; null for an
            organization-wide list any campaign can use.
        acquired:
          type:
            - boolean
            - 'null'
          description: >-
            True when the list came from somewhere other than your own sign-up
            flow. Acquired lists ramp on half the normal warm-up steps.
        sunset_enabled:
          type:
            - boolean
            - 'null'
          description: Whether the unengaged-contact sunset policy applies to this list.
        quality_grade:
          type:
            - string
            - 'null'
          enum:
            - green
            - yellow
            - red
            - null
          description: >-
            The list quality grade shown on the Lists page; null until the list
            is graded.
        counts:
          $ref: '#/components/schemas/ContactListEmailCounts'
        removed_at_upload:
          type: object
          description: >-
            Addresses removed before they joined the list, by upload reason:
            `yahoo_family`, `high_complaint_domain`, `role`, `typo`,
            `disposable`, `no_mx`, `syntax`, `platform_bounce`, `duplicate`.
            Counts, not addresses.
          additionalProperties:
            type: integer
        last_send_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
      required:
        - source_type
        - status
        - counts
      additionalProperties: true
    RateLimitErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - false
        error:
          type: string
        message:
          type: string
        code:
          type: string
          enum:
            - RATE_LIMIT_EXCEEDED
        statusCode:
          type: integer
          enum:
            - 429
        retryAfter:
          type: integer
          description: >-
            Seconds until the rate-limit window resets. Also sent as the
            Retry-After header.
      required:
        - success
        - error
        - code
        - statusCode
        - retryAfter
      additionalProperties: true
    ContactListEmailCounts:
      type: object
      properties:
        total:
          type: integer
        sendable:
          type: integer
          description: Addresses that would receive the next send from this list.
        bounced:
          type: integer
        complained:
          type: integer
        unsubscribed:
          type: integer
        invalid:
          type: integer
        suppressed_global:
          type: integer
      additionalProperties: true
  responses:
    BadRequest:
      description: >-
        Invalid request (validation failure, malformed parameters, or a request
        the resource state does not allow).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: Validation failed
            code: VALIDATION_ERROR
            correlationId: 030bf857-922f-4b82-8fca-c5b0769c1590
            details:
              - field: organization_id
                message: Invalid UUID
                code: invalid_format
    Unauthorized:
      description: Missing, malformed, revoked, or expired API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AuthErrorResponse'
          example:
            error: API key required
            message: Please provide an API key in the X-API-Key header
    NotFound:
      description: Resource not found or not visible to this API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: Project not found
            code: PROJECT_NOT_FOUND
            correlationId: de038205-7c9c-4d4a-99bc-d7275a52f07e
    RateLimited:
      description: Rate limit exceeded. Check the Retry-After header.
      headers:
        Retry-After:
          description: Seconds until the window resets.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RateLimitErrorResponse'
          example:
            success: false
            error: Rate limit exceeded
            message: Maximum 600 requests per 60 seconds
            code: RATE_LIMIT_EXCEEDED
            statusCode: 429
            retryAfter: 3
    InternalError:
      description: Unexpected server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: An unexpected error occurred. Please try again later.
            code: INTERNAL_ERROR
            correlationId: de038205-7c9c-4d4a-99bc-d7275a52f07e
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        Authenticate every request by passing your API key in the X-API-Key
        header. Keys are scoped to your organization hierarchy.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.