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

# Manage campaign contacts

> Creates, updates, or removes contacts in a campaign.

**Usage scenarios**
- **Upsert contacts (default)**: Contacts will be added if they don't exist, or updated if they do. Contacts with a `disqualified` or `completed` status will not be added. If no `action` is specified, the API defaults to `upsert`.
- **Update existing contacts only**: Set `action` to `"updateOnly"`. Only contacts already in the campaign will be updated. Contacts not in the campaign are ignored. This is useful when your process might include contacts that aren't in the campaign and you don't want them added.
- **Remove contacts**: Set `action` to `"remove"` and provide at least `email` or `phone` for each contact to remove.

**External IDs (CRM routing)**

- **Configure in Manage Assistants** — Define **external IDs** per assistant so each one can reference CRMs outside Replify (for example HubSpot account IDs, storefront codes, or internal site codes).
- **Example** — A key like `hubspotId` paired with values that distinguish Site A from Site B.
- **In this API** — For **organization-wide / multi-assistant** campaigns, include optional **`externalIdKey`** and **`externalIdValue`**. Key names must match what you saved in Manage Assistants.
- **Where to put them** — You can set **`externalIdKey`** / **`externalIdValue`** on individual contacts, at the request root, or both. See the schemas for those properties in this spec.

**Key fields**
- `action`: Controls how contacts are processed. Options are `"upsert"` (default), `"updateOnly"`, and `"remove"`. This applies to all contacts in the request.
- `status`: Use `completed` or `disqualified` to stop further outreach from campaigns. Other status values are allowed but won't affect campaign behavior.
- `contactMetadata`: Add any key:value pairs to inform your AI assistant's behavior. For example, if you set `"tier": "pro"`, you can configure your AI to identify and handle pro-tier contacts appropriately.
- `externalIdKey` / `externalIdValue`: Optional; supported on request root and/or per-contact as described in schema field docs.



## OpenAPI

````yaml /api-reference/external-api.openapi.json post /campaigns/{campaignId}/contacts
openapi: 3.0.1
info:
  title: Replify External API
  version: 1.0.11
  description: >-
    Manage campaign contacts via the Replify API Gateway.


    **Authentication**

    See the [API overview](/api-reference/overview) for API keys (`x-api-key`),
    saving your key after creation, and required headers. This endpoint requires
    `Content-Type: application/json`.
servers:
  - url: https://api.heylibby.com/api/v1
    description: Production
security:
  - api_key: []
paths:
  /campaigns/{campaignId}/contacts:
    post:
      tags:
        - Campaigns
      summary: Manage campaign contacts
      description: >-
        Creates, updates, or removes contacts in a campaign.


        **Usage scenarios**

        - **Upsert contacts (default)**: Contacts will be added if they don't
        exist, or updated if they do. Contacts with a `disqualified` or
        `completed` status will not be added. If no `action` is specified, the
        API defaults to `upsert`.

        - **Update existing contacts only**: Set `action` to `"updateOnly"`.
        Only contacts already in the campaign will be updated. Contacts not in
        the campaign are ignored. This is useful when your process might include
        contacts that aren't in the campaign and you don't want them added.

        - **Remove contacts**: Set `action` to `"remove"` and provide at least
        `email` or `phone` for each contact to remove.


        **External IDs (CRM routing)**


        - **Configure in Manage Assistants** — Define **external IDs** per
        assistant so each one can reference CRMs outside Replify (for example
        HubSpot account IDs, storefront codes, or internal site codes).

        - **Example** — A key like `hubspotId` paired with values that
        distinguish Site A from Site B.

        - **In this API** — For **organization-wide / multi-assistant**
        campaigns, include optional **`externalIdKey`** and
        **`externalIdValue`**. Key names must match what you saved in Manage
        Assistants.

        - **Where to put them** — You can set **`externalIdKey`** /
        **`externalIdValue`** on individual contacts, at the request root, or
        both. See the schemas for those properties in this spec.


        **Key fields**

        - `action`: Controls how contacts are processed. Options are `"upsert"`
        (default), `"updateOnly"`, and `"remove"`. This applies to all contacts
        in the request.

        - `status`: Use `completed` or `disqualified` to stop further outreach
        from campaigns. Other status values are allowed but won't affect
        campaign behavior.

        - `contactMetadata`: Add any key:value pairs to inform your AI
        assistant's behavior. For example, if you set `"tier": "pro"`, you can
        configure your AI to identify and handle pro-tier contacts
        appropriately.

        - `externalIdKey` / `externalIdValue`: Optional; supported on request
        root and/or per-contact as described in schema field docs.
      parameters:
        - name: campaignId
          in: path
          required: true
          description: The unique ID of the campaign
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                externalIdKey:
                  type: string
                  description: >-
                    Optional external ID **name** for this request batch (must
                    match a key configured under Manage Assistants). Pair with
                    `externalIdValue`.
                  example: hubspotId
                externalIdValue:
                  type: string
                  description: >-
                    Optional external CRM **value** paired with root-level
                    `externalIdKey` for routing in multi-assistant org-wide
                    flows.
                  example: '1234'
                contacts:
                  type: array
                  description: >-
                    List of contacts to add or update. When `action` is set to
                    `"remove"`, these contacts are removed from the campaign.
                  items:
                    type: object
                    properties:
                      firstName:
                        type: string
                        description: Contact's first name.
                        example: Jane
                      lastName:
                        type: string
                        description: Contact's last name.
                        example: Doe
                      email:
                        type: string
                        format: email
                        description: >-
                          At least one of `email` or `phone` is required to
                          identify the contact.
                        example: jane.doe@example.com
                      phone:
                        type: string
                        description: >-
                          At least one of `email` or `phone` is required to
                          identify the contact.
                        example: '15551234567'
                      status:
                        type: string
                        description: >-
                          Optional status field. Use `completed` when the
                          contact has achieved the campaign goal (e.g., signed
                          up for trial, made purchase). Use `disqualified` when
                          the contact is not a good fit. Other values are
                          allowed but won't affect campaign behavior.
                        enum:
                          - completed
                          - disqualified
                        example: completed
                      contactMetadata:
                        type: object
                        additionalProperties: true
                        description: >-
                          Key:value pairs that inform your AI assistant's
                          behavior. Use any custom data to help your AI respond
                          differently in different scenarios. For example, set
                          `"tier": "pro"` to help your AI identify and handle
                          pro-tier contacts appropriately.
                        example:
                          source: import
                          tier: pro
                          won: true
                      externalIdKey:
                        type: string
                        description: >-
                          Optional external ID **name** for this contact (must
                          match a key configured under Manage Assistants). Pair
                          with `externalIdValue`.
                        example: hubspotId
                      externalIdValue:
                        type: string
                        description: >-
                          Optional external CRM **value** paired with this
                          contact's `externalIdKey` when routing contacts in
                          Multi-Assistant / organization-wide campaigns.
                        example: '1234'
                action:
                  type: string
                  description: >-
                    Controls how contacts are processed. `upsert` (default) adds
                    new contacts and updates existing ones. `updateOnly` updates
                    only contacts already in the campaign and ignores others.
                    `remove` removes the specified contacts.
                  enum:
                    - upsert
                    - updateOnly
                    - remove
                  example: upsert
              required:
                - contacts
            examples:
              UpsertContacts:
                summary: Upsert contacts (default)
                description: >-
                  Adds contacts that don't exist in the campaign and updates
                  those that do. Contacts with a disqualified or completed
                  status will not be added. This is the default behavior if no
                  action is specified.
                value:
                  contacts:
                    - firstName: Ava
                      lastName: Lopez
                      email: ava@example.com
                      phone: '15551234567'
                      contactMetadata:
                        source: web_form
                        tier: trial
                  action: upsert
              UpdateOnlyContacts:
                summary: Update existing contacts only
                description: >-
                  Updates only contacts that already exist in the campaign.
                  Contacts not in the campaign are ignored. Useful when your
                  process might include contacts that aren't in the campaign and
                  you don't want them added.
                value:
                  contacts:
                    - email: ava@example.com
                      status: completed
                      contactMetadata:
                        won: true
                        conversion_source: appointment_booked
                  action: updateOnly
              RemoveContact:
                summary: Remove a contact
                description: >-
                  Removes specific contacts from the campaign by setting the
                  top-level action to "remove" and providing contact
                  identifiers.
                value:
                  contacts:
                    - email: ava@example.com
                  action: remove
              UpsertWithExternalIds:
                summary: Upsert with per-contact CRM external IDs
                description: >-
                  Each contact can carry different
                  `externalIdKey`/`externalIdValue` pairs for multi-location
                  routing. Roots may omit these fields.
                value:
                  contacts:
                    - firstName: Ava
                      lastName: Lopez
                      email: ava@example.com
                      phone: '15551234567'
                      externalIdKey: hubspotId
                      externalIdValue: '1234'
                    - firstName: Ben
                      lastName: Nguyen
                      email: ben@example.com
                      phone: '15559876543'
                      externalIdKey: hubspotId
                      externalIdValue: '5678'
                  action: upsert
              UpsertWithExternalIdsRoot:
                summary: Upsert batch with external IDs only on root
                description: >-
                  When your payload sets `externalIdKey`/`externalIdValue` at
                  the JSON root instead of nesting them on contacts.
                value:
                  externalIdKey: hubspotId
                  externalIdValue: '1234'
                  contacts:
                    - firstName: Ava
                      lastName: Lopez
                      email: ava@example.com
                      phone: '15551234567'
                  action: upsert
      responses:
        '200':
          description: Request accepted and processed successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    description: Human-readable summary of the operation results.
                    example: Processed 2 contacts successfully, 1 failed
                  success:
                    type: array
                    description: >-
                      Contacts that were successfully created, updated, or
                      deleted.
                    items:
                      type: object
                      properties:
                        phone:
                          type: string
                          description: Normalized phone number (if provided).
                          example: '15551234567'
                        email:
                          type: string
                          format: email
                          description: Email address (if provided).
                          example: jane.doe@example.com
                        action:
                          type: string
                          description: Action taken for this contact.
                          enum:
                            - created
                            - updated
                          example: created
                  errors:
                    type: array
                    description: Contacts that failed to process with error details.
                    items:
                      type: object
                      properties:
                        email:
                          type: string
                          format: email
                          description: Email associated with the error (if available).
                          example: john.smith@example.com
                        phone:
                          type: string
                          description: Phone associated with the error (if available).
                          example: '15559876543'
                        error:
                          type: string
                          description: Reason why the contact could not be processed.
                          example: >-
                            Contact contains email and phone for two different
                            existing contacts
              examples:
                allSuccess:
                  summary: All contacts processed successfully
                  value:
                    message: Processed 3 contacts successfully
                    success:
                      - phone: '15551234567'
                        email: jane.doe@example.com
                        action: created
                      - phone: '15559876543'
                        email: john.smith@example.com
                        action: updated
                    errors: []
                mixedOutcome:
                  summary: Some contacts succeeded, some failed
                  value:
                    message: Processed 1 contacts successfully, 1 failed
                    success:
                      - phone: '15551234567'
                        email: jane.doe@example.com
                        action: created
                    errors:
                      - email: john.smith@example.com
                        error: >-
                          Contact contains email and phone for two different
                          existing contacts
        '400':
          description: >-
            Bad request — the request body contains invalid JSON or missing
            required fields.
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: string
                    example: BAD_REQUEST
                  message:
                    type: string
                    example: Invalid JSON in request body
                  details:
                    type: string
                    example: 'SyntaxError: Unexpected token ''}'' in JSON at position 42'
              examples:
                invalidJson:
                  summary: Invalid JSON syntax
                  value:
                    code: BAD_REQUEST
                    message: Invalid JSON in request body
                    details: 'SyntaxError: Unexpected token ''}'' in JSON at position 42'
                missingContacts:
                  summary: Missing required contacts field
                  value:
                    code: BAD_REQUEST
                    message: 'Missing required field: contacts'
                    details: The request body must contain a 'contacts' array
        '403':
          description: >-
            Forbidden — API key is missing, invalid, or not authorized for this
            campaign.
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: string
                    example: FORBIDDEN
                  message:
                    type: string
                    example: Access to the resource is prohibited
              examples:
                missingKey:
                  summary: Missing API key
                  value:
                    code: FORBIDDEN
                    message: API key is required
                invalidKey:
                  summary: Invalid API key
                  value:
                    code: FORBIDDEN
                    message: Invalid API key provided
                unauthorizedCampaign:
                  summary: Not authorized for this campaign
                  value:
                    code: FORBIDDEN
                    message: You are not authorized to access this campaign
        '404':
          description: Not found — the specified campaign does not exist.
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: string
                    example: NOT_FOUND
                  message:
                    type: string
                    example: Campaign not found
              examples:
                campaignNotFound:
                  summary: Campaign does not exist
                  value:
                    code: NOT_FOUND
                    message: Campaign not found
      security:
        - api_key: []
components:
  securitySchemes:
    api_key:
      type: apiKey
      name: x-api-key
      in: header

````