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

# Add contacts to a campaign

> Adds contacts by phone number. Numbers are canonicalised to E.164 before storage, so the same number written two ways resolves to one contact.

Contacts already in your library are reused rather than duplicated, and contacts already on this campaign are counted as `duplicates` and skipped. Adding contacts to a `completed` campaign re-opens it to `paused`, **not** `running` — the response says so via `campaign_reopened`. Dialling does not resume on its own: call `POST /campaigns/{id}/resume`, which re-validates caller IDs and provider access first.

Maximum 10,000 contacts per request.



## OpenAPI

````yaml /openapi.json post /campaigns/{id}/contacts
openapi: 3.1.0
info:
  title: Vocobase API
  version: '2.0'
  description: >-
    API for managing voice AI agents, documents, and calls on the Vocobase
    platform.
servers:
  - url: https://api.vocobase.com/api/v2
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Config
    description: >-
      Read and update account configuration, webhook settings, and telephony
      credentials.
  - name: Agents
    description: Create, read, update, and delete voice AI agents.
  - name: Voices
    description: >-
      List voice tiers, available voices, and stream preview audio for agent
      configuration.
  - name: Documents
    description: Upload, manage, and delete knowledge base documents.
  - name: Agent Documents
    description: Link and unlink documents to agents for knowledge base integration.
  - name: Calls
    description: Initiate outbound calls and view call history.
  - name: Phone Numbers
    description: >-
      Import DIDs, assign agents for inbound routing, and re-sync carrier
      Application bindings.
  - name: Inbound Routing Policies
    description: >-
      Define pre-answer routing decisions for inbound calls before AI sessions
      are created.
  - name: Telephony Connections
    description: Create, list, rename, and disconnect named V2 telephony connections.
  - name: VoiceLink Management
    description: Manage VoiceLink reseller clients, DID mapping, and readiness sync.
  - name: Projects
    description: Organize agents into projects. Every agent belongs to exactly one project.
  - name: Dictionaries
    description: Speech-recognition dictionary CRUD and agent attachment.
  - name: Sessions
    description: Browser-initiated WebRTC voice sessions for in-app voice agents.
  - name: Campaigns
    description: Batch outbound calling.
  - name: Billing
    description: Balance, transactions, and usage summaries.
  - name: Custom Functions
    description: HTTP endpoints the agent can call mid-conversation.
  - name: Extraction Sets
    description: Reusable sets of post-call extraction fields.
  - name: Lifecycle Hooks
    description: Automatic pre-call and post-call integration steps.
  - name: Messaging
    description: Bring-your-own WhatsApp sending.
  - name: Integration Logs
    description: Audit trail for integration calls.
  - name: Connectors
    description: Catalog of tools an agent can be connected to.
  - name: Customers
    description: B2B2B sub-tenants and their tool connections.
  - name: Agent Tools
    description: Binding connectors and connections to an agent.
paths:
  /campaigns/{id}/contacts:
    post:
      tags:
        - Campaigns
      summary: Add contacts to a campaign
      description: >-
        Adds contacts by phone number. Numbers are canonicalised to E.164 before
        storage, so the same number written two ways resolves to one contact.


        Contacts already in your library are reused rather than duplicated, and
        contacts already on this campaign are counted as `duplicates` and
        skipped. Adding contacts to a `completed` campaign re-opens it to
        `paused`, **not** `running` — the response says so via
        `campaign_reopened`. Dialling does not resume on its own: call `POST
        /campaigns/{id}/resume`, which re-validates caller IDs and provider
        access first.


        Maximum 10,000 contacts per request.
      operationId: addCampaignContacts
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Campaign ID.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - contacts
              properties:
                contacts:
                  type: array
                  minItems: 1
                  maxItems: 10000
                  items:
                    type: object
                    required:
                      - phone
                    properties:
                      phone:
                        type: string
                        description: E.164, e.g. `+919876543210`.
                      name:
                        type: string
                        nullable: true
                      metadata:
                        type: object
                        nullable: true
                        additionalProperties: true
                        description: >-
                          Arbitrary JSON carried through to the call and
                          returned on webhooks.
            example:
              contacts:
                - phone: '+919876543210'
                  name: Asha Menon
                  metadata:
                    plan: pro
                    renews_on: '2026-09-01'
                - phone: '+919812345678'
                  name: Ravi Kumar
      responses:
        '201':
          description: Contacts added.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      added:
                        type: integer
                        description: Contacts newly attached to this campaign.
                      duplicates:
                        type: integer
                        description: Already on this campaign; skipped.
                      contacts_created:
                        type: integer
                        description: New rows created in your contact library.
                      contacts_existing:
                        type: integer
                        description: Reused from your contact library.
                      campaign_reopened:
                        type: boolean
                        description: >-
                          `true` when this request put a finished campaign back
                          to `paused`. It is now an ordinary paused campaign —
                          `POST /campaigns/{id}/resume` is the only way on from
                          here.
              example:
                success: true
                data:
                  added: 2
                  duplicates: 0
                  contacts_created: 1
                  contacts_existing: 1
                  campaign_reopened: false
        '400':
          description: >-
            `VALIDATION_ERROR` for an empty array, more than 10,000 contacts, or
            invalid E.164 numbers. `INVALID_STATE` when the campaign is an
            inbound campaign, or has been stopped — a stopped campaign refuses
            contacts forever, including after it is relabelled `completed`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: VALIDATION_ERROR
                  message: 'Invalid E.164 phone numbers: 9876543210, 044-1234'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  schemas:
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          const: false
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
          required:
            - code
            - message
      required:
        - success
        - error
  responses:
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: UNAUTHORIZED
              message: Invalid or missing API key
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: NOT_FOUND
              message: Resource not found
    RateLimitExceeded:
      description: Per-account rate limit exceeded. Retry after a short backoff.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: RATE_LIMITED
              message: Rate limit exceeded. Retry after 60 seconds.
    InternalError:
      description: Unexpected server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error:
              code: INTERNAL_ERROR
              message: An unexpected error occurred
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        API key in format: `rg_live_xxxx`. Pass as a Bearer token in the
        Authorization header.

````