lemlist Contacts API

CRM-side contacts, contact lists, list membership and exports.

OpenAPI Specification

lemlist-contacts-api-openapi.yml Raw ↑
openapi: 3.0.0
info:
  title: lemlist Contacts API
  version: 1.0.0
  description: The Contacts operations of the lemlist API, split by tag from the OpenAPI lemlist publishes
    at https://developer.lemlist.com/api-reference/openapi/v2.json. Operation content is carried verbatim
    from the provider spec.
servers:
- url: https://api.lemlist.com/api
security:
- basicAuth: []
tags:
- name: Contacts
paths:
  /contacts:
    get:
      summary: Get Many Contacts
      description: 'Retrieves contacts by IDs/emails, or searches/lists contacts by name, email, contact
        list, campaign membership, or company link.


        When using `idsOrEmails`, returns an array of matching contacts directly.


        When using `search`, `email`, `listId`, `notInAnyCampaign`, any of the `company*` filters, or
        no filter at all, returns a paginated response with `data`, `total`, `limit`, and `offset` fields.
        You can combine filters together to narrow results (e.g. `listId` with `search`, or `notInAnyCampaign`
        with `companyId`). Calling the endpoint without any filter returns all contacts of the team, paginated.


        The `company*` filters (`companyId`, `companyDomain`, `companyLinkedinUrl`, `companySalesnavUrl`)
        are mutually exclusive — use only one at a time. `companyDomain` / `companyLinkedinUrl` / `companySalesnavUrl`
        are resolved to a `companyId` through the Companies collection; if no matching company exists,
        the endpoint returns an empty list with `total: 0` (not an error), which keeps automation flows
        simple.'
      tags:
      - Contacts
      parameters:
      - name: idsOrEmails
        in: query
        required: false
        description: A comma separated string of either valid contact IDs (MongoDB ObjectId) or valid
          email addresses. Optional — when omitted, returns the paginated list of all contacts of the
          team. Maximum 100 values.
        example: ctc_xW8Ou6C03Csv8vatp,riley@example.com
        schema:
          type: string
        style: form
        explode: false
      - name: search
        in: query
        required: false
        description: Search contacts by name or other text fields. Must be at least 2 characters.
        schema:
          type: string
      - name: email
        in: query
        required: false
        description: Search contacts by exact email address.
        schema:
          type: string
          format: email
      - name: listId
        in: query
        required: false
        description: Filter contacts by contact list ID (`clt_xxx` format). Can be combined with `search`
          or `email`, or used alone to list all contacts in a list. Get valid IDs from `GET /contacts/lists`.
        example: clt_abc123def456ghi78
        schema:
          type: string
          pattern: ^clt_[a-zA-Z0-9]+$
      - name: notInAnyCampaign
        in: query
        required: false
        description: When set to `true`, only returns contacts that are not part of any campaign (orphan
          contacts). Can be used alone or combined with other filters such as `search`, `email`, or `listId`.
        schema:
          type: boolean
      - name: companyId
        in: query
        required: false
        description: Filter contacts by attached company ID (`cpn_xxx` format). Use this when you already
          know the lemlist company id (for example after fetching `GET /companies?crmSyncStatus=unique_index_error_company`).
          Mutually exclusive with `companyDomain`, `companyLinkedinUrl`, and `companySalesnavUrl`.
        example: cpn_A1B2C3D4E5F6G7H8I
        schema:
          type: string
          pattern: ^cpn_[a-zA-Z0-9]+$
      - name: companyDomain
        in: query
        required: false
        description: 'Filter contacts by their company''s website domain. Resolved to a `companyId` against
          the Companies collection. If no company matches, the endpoint returns an empty list (`total:
          0`). Mutually exclusive with the other `company*` filters.'
        example: acme.com
        schema:
          type: string
      - name: companyLinkedinUrl
        in: query
        required: false
        description: 'Filter contacts by their company''s LinkedIn URL. Resolved to a `companyId` against
          the Companies collection. If no company matches, the endpoint returns an empty list (`total:
          0`). Mutually exclusive with the other `company*` filters.'
        example: https://www.linkedin.com/company/acme
        schema:
          type: string
      - name: companySalesnavUrl
        in: query
        required: false
        description: 'Filter contacts by their company''s LinkedIn Sales Navigator URL. Resolved to a
          `companyId` against the Companies collection. If no company matches, the endpoint returns an
          empty list (`total: 0`). Mutually exclusive with the other `company*` filters.'
        example: https://www.linkedin.com/sales/company/12345678
        schema:
          type: string
      - name: fieldRejectionReason
        in: query
        required: false
        description: 'Filter contacts to those carrying a field rejection with this reason — a value lemlist
          refused to write, prefixed by its origin (`enrichment_*` while enriching, `crm_sync_*` during
          CRM sync). Returns an empty list (`total: 0`) when no contact matches. Each returned contact
          exposes the full detail under `fieldRejections[]` (which field, why, and `conflictingRecordId`
          for duplicates). Only applies to the paginated list — ignored when `idsOrEmails` is provided
          (that path returns the exact contacts requested, unfiltered).'
        example: enrichment_duplicate_linkedin_url
        schema:
          type: string
          enum:
          - enrichment_duplicate_linkedin_url
          - enrichment_duplicate_email
          - crm_sync_duplicate_linkedin_url
          - crm_sync_invalid_linkedin_url
          - crm_sync_invalid_phone
          - crm_sync_duplicate_contact_blocked
          - crm_sync_duplicate_company_blocked
          - crm_sync_company_not_in_lemlist
          - crm_sync_custom_field_creation_failed
          - crm_sync_value_oscillating
          - crm_sync_owner_sync_loop
          - crm_sync_unmapped_user
          - crm_sync_value_incompatible
          - crm_sync_unknown_error
      - name: limit
        in: query
        required: false
        description: Maximum number of contacts to return (1–500). Defaults to 100.
        schema:
          type: integer
          minimum: 1
          maximum: 500
          default: 100
      - name: offset
        in: query
        required: false
        description: Number of contacts to skip for pagination. Defaults to 0.
        schema:
          type: integer
          minimum: 0
          default: 0
      responses:
        '200':
          description: Success. When using `idsOrEmails`, returns an array of contacts. Otherwise, returns
            a paginated object with `data`, `total`, `limit`, and `offset`.
          content:
            application/json:
              schema:
                oneOf:
                - type: array
                  items:
                    $ref: '#/components/schemas/Contact'
                - type: object
                  properties:
                    data:
                      type: array
                      items:
                        $ref: '#/components/schemas/Contact'
                    total:
                      type: integer
                    limit:
                      type: integer
                    offset:
                      type: integer
                  required:
                  - data
                  - total
                  - limit
                  - offset
              example:
              - _id: ctc_xW8Ou6C03Csv8vatp
                teamId: tea_8QvkOiBfPdb2ZRhHi
                fullName: John Doe
                email: support@lemlist.com
                fields:
                  firstName: John
                  jobTitle: Growth Engineer
                  lastName: Doe
                  industry: Technology
                  isActiveInCampaigns: false
                  lastCampaign: NEW TO DELETE
                  lastLeadMarkedAsInterestedDate: '2025-10-28T02:12:31.971Z'
                  leadStatus: Marked as not Interested by api
                campaigns:
                - campaignId: cam_bSn8EORHQxbWPjHvu
                  campaignState: running
                  leadState: review
                  leadId: lea_fiDpiGV585wy3Oii2
                ownerId: usr_ahfFktBBHUIxbVG5P
                createdAt: '2025-10-28T00:40:37.917Z'
                createdBy: usr_ahfFktBBHUIxbVG5P
                unsubscribed: false
              - _id: ctc_a9RxJNa7pmMd85H9b
                teamId: tea_8QvkOiBfPdb2ZRhHi
                fullName: Casey
                email: riley@example.com
                fields:
                  firstName: Casey
                  isActiveInCampaigns: false
                campaigns:
                - campaignId: cam_jwm7THjgGFE3ylR85
                  campaignState: running
                  leadState: done
                  leadId: lea_XKjAytuJhBKZhxhWh
                - campaignId: cam_eF4DlNERV0CW1TwRd
                  campaignState: running
                  leadState: done
                  leadId: lea_fJcS9D3UtEqZcDcAG
                - campaignId: cam_UBbMt30jHq0vNJKJr
                  campaignState: running
                  leadState: done
                  leadId: lea_GlaMfjxlUYuwEDL0w
                - campaignId: cam_pijDVnytN5S7frriD
                  campaignState: running
                  leadState: review
                  leadId: lea_Bs9aMGCcjdzTDvixY
                ownerId: usr_Emu1g29BMtBixhMSP
                createdAt: '2024-10-01T09:00:13.831Z'
                createdBy: usr_Emu1g29BMtBixhMSP
                unsubscribed: true
        '400':
          description: 'Possible errors: Bad team / search query too short / invalid listId format'
          content:
            text/plain:
              example: Search query must be at least 2 characters
        '401':
          description: The authentication you supplied is incorrect
          content:
            text/plain:
              example: The authentication you supplied is incorrect
        '405':
          description: Method not allowed
    post:
      summary: Add and update contact
      description: Creates a new contact or updates an existing one (upsert). If a contact with the same
        email, LinkedIn URL, or Sales Navigator URL already exists, it will be updated with the provided
        non-empty fields. Null or empty values are ignored during updates to preserve existing data. You
        can target an existing contact directly by providing `contactId`, bypassing email/LinkedIn matching.
        You can optionally link the contact to a company by providing `companyId`, `companyDomain`, or
        `companyLinkedinUrl`.
      tags:
      - Contacts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                contactId:
                  type: string
                  description: Existing contact ID. Updates a specific contact by ID, bypassing email/LinkedIn
                    matching. Can only be used to update an existing contact, not to create a new one.
                    When provided, `email` and `linkedinUrl` are not required. At least one of `contactId`,
                    `email`, `linkedinUrl`, or `linkedinUrlSalesNav` is required.
                email:
                  type: string
                  description: Contact email address. Used as a unique key for upsert matching. At least
                    one of `contactId`, `email`, `linkedinUrl`, or `linkedinUrlSalesNav` is required.
                linkedinUrl:
                  type: string
                  description: LinkedIn profile URL. Used as an alternative unique key for upsert matching.
                    At least one of `contactId`, `email`, `linkedinUrl`, or `linkedinUrlSalesNav` is required.
                linkedinUrlSalesNav:
                  type: string
                  description: LinkedIn Sales Navigator profile URL. Used as an alternative unique key
                    for upsert matching.
                additionalEmails:
                  type: array
                  items:
                    type: string
                  description: Additional email addresses for the contact. Each must be a valid email
                    address.
                firstName:
                  type: string
                  description: Contact first name.
                lastName:
                  type: string
                  description: Contact last name.
                phone:
                  type: string
                  description: Contact phone number.
                jobTitle:
                  type: string
                  description: Contact job title. If a company is linked, this is saved as part of the
                    job data.
                jobDescription:
                  type: string
                  description: Contact job description. If a company is linked, this is saved as part
                    of the job data.
                picture:
                  type: string
                  description: URL of the contact's profile picture.
                timezone:
                  type: string
                  description: Contact timezone.
                industry:
                  type: string
                  description: Contact industry.
                languages:
                  type: string
                  description: Contact languages.
                location:
                  type: string
                  description: Contact location.
                skills:
                  type: string
                  description: Contact skills.
                summary:
                  type: string
                  description: Contact summary or bio.
                tagline:
                  type: string
                  description: Contact tagline.
                contactOwner:
                  type: string
                  description: Owner of the contact. Can be a user ID (e.g. `usr_...`) or a team member's
                    email address. If the provided value does not match a team member, the owner defaults
                    to the API key owner.
                source:
                  type: string
                  description: Origin of the contact record. Set on creation only and cannot be updated
                    afterwards. Defaults to `api`.
                  default: api
                companyId:
                  type: string
                  description: ID of a company already existing in lemlist to link to this contact. Takes
                    priority over `companyDomain` and `companyLinkedinUrl`.
                companyDomain:
                  type: string
                  description: Domain of a company already existing in lemlist to link to this contact
                    (e.g. `lemlist.com`). Used if `companyId` is not provided.
                companyLinkedinUrl:
                  type: string
                  description: LinkedIn URL of a company already existing in lemlist to link to this contact.
                    Used if `companyId` and `companyDomain` are not provided.
              additionalProperties:
                description: Any additional key is treated as a custom field. Custom fields must be registered
                  in the team's CRM field registry beforehand.
            example:
              email: jane.smith@example.com
              firstName: Jane
              lastName: Smith
              linkedinUrl: https://linkedin.com/in/janesmith
              jobTitle: Product Manager
              companyDomain: acme.com
      responses:
        '201':
          description: Contact created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    properties:
                      _id:
                        type: string
                        description: Unique contact identifier
                      teamId:
                        type: string
                        description: Team ID
                      emails:
                        type: array
                        description: Contact email addresses
                        items:
                          type: object
                          properties:
                            value:
                              type: string
                      linkedinUrl:
                        type: string
                        description: LinkedIn profile URL
                      linkedinUrlSalesNav:
                        type: string
                        description: LinkedIn Sales Navigator URL
                      fields:
                        type: object
                        description: Key/value fields (firstName, lastName, phone, jobTitle, custom fields,
                          etc.)
                        additionalProperties: true
                      ownerId:
                        type: string
                        description: Owner user ID
                      companyId:
                        type: string
                        description: Linked company ID (present only when a company was linked)
                      source:
                        type: string
                        description: Record origin
                      createdAt:
                        type: string
                        format: date-time
                        description: Creation timestamp
                      createdBy:
                        type: string
                        description: User ID who created the record
                      unsubscribed:
                        type: boolean
                        description: Whether the contact is unsubscribed
                      created:
                        type: boolean
                        description: '`true` when a new contact was created, `false` when an existing
                          contact was updated.'
                      updated:
                        type: boolean
                        description: Present and set to `true` only when an existing contact was updated
                          (i.e. `created` is `false`).
                      companyLinked:
                        type: boolean
                        description: Whether a company was successfully linked to the contact.
                  warnings:
                    type: array
                    description: Optional warnings (e.g. company resolution issues).
                    items:
                      type: object
                      properties:
                        code:
                          type: string
                        message:
                          type: string
              example:
                success: true
                data:
                  _id: ctc_xW8Ou6C03Csv8vatp
                  teamId: tea_8QvkOiBfPdb2ZRhHi
                  emails:
                  - value: jane.smith@example.com
                  linkedinUrl: https://www.linkedin.com/in/janesmith
                  fields:
                    firstName: Jane
                    lastName: Smith
                    jobTitle: Product Manager
                  ownerId: usr_ahfFktBBHUIxbVG5P
                  source: api
                  createdAt: '2025-10-28T00:40:37.917Z'
                  createdBy: usr_ahfFktBBHUIxbVG5P
                  unsubscribed: false
                  created: true
                  companyLinked: true
                  companyId: cpn_gG7PsmZFpEAnpMCHO
        '200':
          description: Existing contact updated (upsert matched by email or LinkedIn URL)
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    type: object
                    description: 'Same shape as the 201 response, with `created: false` and `updated:
                      true`.'
              example:
                success: true
                data:
                  _id: ctc_xW8Ou6C03Csv8vatp
                  teamId: tea_8QvkOiBfPdb2ZRhHi
                  emails:
                  - value: jane.smith@example.com
                  fields:
                    firstName: Jane
                    lastName: Smith
                    jobTitle: Product Manager
                  ownerId: usr_ahfFktBBHUIxbVG5P
                  source: api
                  createdAt: '2025-10-28T00:40:37.917Z'
                  createdBy: usr_ahfFktBBHUIxbVG5P
                  unsubscribed: false
                  created: false
                  updated: true
                  companyLinked: false
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        description: 'Error code: `MISSING_IDENTIFIER`, `INVALID_CONTACT_ID`, `CONTACT_NOT_FOUND_BY_ID`,
                          `INVALID_EMAIL`, `INVALID_ADDITIONAL_EMAILS`, `INVALID_LINKEDIN_URL`, `INVALID_FIELD`,
                          or `CONTACT_VALIDATION_FAILED`.'
                      message:
                        type: string
              example:
                success: false
                error:
                  code: MISSING_IDENTIFIER
                  message: 'At least one identifier is required: email, linkedinUrl or contactId'
        '401':
          description: The authentication you supplied is incorrect
          content:
            text/plain:
              example: The authentication you supplied is incorrect
      parameters: []
  /contacts/{idOrEmail}:
    delete:
      summary: Delete Contact
      description: Deletes a lemlist contact resolved by its id (`ctc_xxx`) or email. Cascades to the
        contact's leads, opportunities, list associations, inbox conversations and activities. **No CRM-side
        propagation** — only the lemlist record is removed. Fails with `409 CONTACT_DELETE_BLOCKED` when
        the contact cannot be deleted right now — for example while an enrichment is still running (retry
        once it finishes); the response `error.message` states the specific reason.
      tags:
      - Contacts
      responses:
        '200':
          description: Contact deleted.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  deletedContactId:
                    type: string
                    description: ID of the deleted lemlist contact.
                required:
                - success
                - deletedContactId
              example:
                success: true
                deletedContactId: ctc_xW8Ou6C03Csv8vatp
        '401':
          description: The authentication you supplied is incorrect.
          content:
            text/plain:
              example: The authentication you supplied is incorrect
        '404':
          description: Contact not found (`CONTACT_NOT_FOUND`).
          content:
            application/json:
              example:
                success: false
                error:
                  code: CONTACT_NOT_FOUND
                  message: No contact found with the provided id or email
        '409':
          description: The contact cannot be deleted right now (`CONTACT_DELETE_BLOCKED`) — for example
            while an enrichment is in progress. The `error.message` states the specific reason; retry
            once it clears.
          content:
            application/json:
              example:
                success: false
                error:
                  code: CONTACT_DELETE_BLOCKED
                  message: Contact cannot be removed if an enrichment is in progress
        '405':
          description: Method not allowed.
    parameters:
    - name: idOrEmail
      in: path
      required: true
      description: The unique identifier or email of the contact
      example: ctc_XXBXpAxmxwg24IzBI
      schema:
        type: string
    get:
      summary: Get Contact
      tags:
      - Contacts
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
              example:
                _id: ctc_xW8Ou6C03Csv8vatp
                teamId: tea_8QvkOiBfPdb2ZRhHi
                fullName: John Doe
                email: support@lemlist.com
                fields:
                  firstName: John
                  jobTitle: Growth Engineer
                  lastName: Doe
                  industry: Technology
                  isActiveInCampaigns: false
                  lastCampaign: NEW TO DELETE
                  lastLeadMarkedAsInterestedDate: '2025-10-28T02:12:31.971Z'
                  leadStatus: Marked as not Interested by api
                campaigns:
                - campaignId: cam_bSn8EORHQxbWPjHvu
                  campaignState: running
                  leadState: review
                  leadId: lea_fiDpiGV585wy3Oii2
                ownerId: usr_ahfFktBBHUIxbVG5P
                createdAt: '2025-10-28T00:40:37.917Z'
                createdBy: usr_ahfFktBBHUIxbVG5P
                unsubscribed: false
        '400':
          description: Bad team
          content:
            text/plain:
              example: Bad team
        '401':
          description: The authentication you supplied is incorrect
          content:
            text/plain:
              example: The authentication you supplied is incorrect
        '405':
          description: Method not allowed
      parameters: []
  /contacts/lists:
    get:
      summary: Get Contact Lists
      description: Retrieves all static and dynamic contact lists for the team. Optionally filter by name.
      tags:
      - Contacts
      parameters:
      - name: search
        in: query
        required: false
        description: Filter lists by name (case-insensitive partial match).
        schema:
          type: string
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    _id:
                      type: string
                      description: Unique list ID (`clt_xxx` format)
                    name:
                      type: string
                      description: Name of the contact list
                    dynamic:
                      type: boolean
                      description: Whether the list is dynamic (auto-populated by filters) or static (manually
                        managed)
              example:
              - _id: clt_abc123def456ghi78
                name: Nurturing
                dynamic: false
              - _id: clt_xyz789uvw012rst34
                name: Enterprise Prospects
                dynamic: true
        '400':
          description: Bad team
          content:
            text/plain:
              example: Bad team
        '401':
          description: The authentication you supplied is incorrect
          content:
            text/plain:
              example: The authentication you supplied is incorrect
        '405':
          description: Method not allowed
    post:
      summary: Create Contact List
      description: Creates a new static contact list. After creating the list, use `POST /contacts/lists/{listId}/entities`
        to add contacts to it.
      tags:
      - Contacts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              properties:
                name:
                  type: string
                  maxLength: 200
                  description: Name of the contact list to create (max 200 characters).
            example:
              name: Nurturing
      responses:
        '201':
          description: Contact list created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  list:
                    type: object
                    properties:
                      id:
                        type: string
                        description: Unique list ID (`clt_xxx` format)
                      name:
                        type: string
                        description: Name of the created list
              example:
                message: Contact list created successfully
                list:
                  id: clt_abc123def456ghi78
                  name: Nurturing
        '400':
          description: 'Possible errors: Bad team / name is required / name too long'
          content:
            text/plain:
              example: name is required and must be a non-empty string
        '401':
          description: The authentication you supplied is incorrect
          content:
            text/plain:
              example: The authentication you supplied is incorrect
        '405':
          description: Method not allowed
  /contacts/lists/{listId}/entities:
    post:
      summary: Add or Remove Contacts in a List
      description: 'Adds existing CRM contacts to a static contact list, or removes them when `?action=remove`
        is set.


        - **Add** (default): send a `POST` with a `contactIds` body. Contacts already in the list are
        silently skipped (no duplicates) and reported in `alreadyInList`.

        - **Remove**: send a `POST` with `?action=remove` and a `contactIds` body. Contacts that are not
        in the list are silently skipped; `removedCount` reflects the associations actually removed. This
        only affects list membership, the contacts are not deleted from your CRM.


        The list must be a **static** contact list (`clt_xxx`). Dynamic lists (auto-populated by filter
        rules) and company lists are rejected.


        **Removal uses `POST ?action=remove` on purpose:** a `DELETE` request body is dropped by our stack,
        so `DELETE` on this path is **not supported** and returns `405 Method Not Allowed`.'
      tags:
      - Contacts
      parameters:
      - name: action
        in: query
        required: false
        description: Omit to **add** the contacts to the list. Set to `remove` to **remove** them from
          the list.
        example: remove
        schema:
          type: string
          enum:
          - remove
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - contactIds
              properties:
                contactIds:
                  type: array
                  items:
                    type: string
                    pattern: ^ctc_[a-zA-Z0-9]+$
                  minItems: 1
                  maxItems: 1000
                  description: Array of contact IDs (`ctc_xxx` format) to add to or remove from the list.
                    Required, non-empty, maximum 1,000 per request.
            example:
              contactIds:
              - ctc_xxxxxxxxxxxxxxxxx
              - ctc_yyyyyyyyyyyyyyyyy
      responses:
        '200':
          description: Contacts added to or removed from the list successfully. The response body depends
            on `action`.
          content:
            application/json:
              schema:
                type: object
                proper

# --- truncated at 32 KB (39 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/lemlist/refs/heads/main/openapi/lemlist-contacts-api-openapi.yml