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

# Lemlist: Post Contacts

> Add and update contact. Lemlist Post Contacts reference includes SDK V2 and CLI requests, input constraints, response fields, and Deepline cost.

Add and update contact.

<Info>
  Tool ID: `lemlist_post_contacts`
</Info>

## Run this action

Use the TypeScript SDK for a single call. Put `ctx.tools.execute(...)` inside a Play when the call should be durable, scheduled, or run across a CSV.

```ts theme={null}
import { Deepline } from 'deepline';

const deepline = await Deepline.connect();
const result = await deepline.tools.execute(
  'lemlist_post_contacts',
  {
    "updateStrategy": "overwriteIgnoreEmpty",
    "contactId": "contact_123",
    "email": "jane.doe@example.com"
  },
);

console.log(result.toolResponse.raw);
```

### CLI

```bash theme={null}
deepline tools execute lemlist_post_contacts --input '{
  "updateStrategy": "overwriteIgnoreEmpty",
  "contactId": "contact_123",
  "email": "jane.doe@example.com"
}' --json
```

## Example response

The SDK exposes this shape at `result.toolResponse.raw`. Values below are representative.

```json theme={null}
{
  "data": {
    "success": true,
    "data": {},
    "warnings": [
      {
        "code": "example",
        "message": "Example message",
        "params": {}
      }
    ]
  }
}
```

Use `deepline tools get lemlist_post_contacts --json` for the latest machine-readable contract.

## Input reference

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 is updated as the `updateStrategy` query parameter says (by default, sent values replace stored ones and empty values are ignored). 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`.

| Name | Type | Required | Default | Details |
| - | - | - | - | - |
| `payload.updateStrategy` | `"overwrite" \| "overwriteIgnoreEmpty" \| "fillEmptyOnly"` | No | `"overwriteIgnoreEmpty"` | What an existing record keeps. `overwriteIgnoreEmpty` (default): sent values replace stored ones, empty values are ignored. `overwrite`: an empty string clears the field. `fillEmptyOnly`: only empty fields are filled, identifiers, links, owner and status kept. A new record receives every value sent. Other values: `400 INVALID_UPDATE_STRATEGY`. Allowed: `overwrite`, `overwriteIgnoreEmpty`, `fillEmptyOnly`. |
| `payload.contactId` | `string` | No | — | 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. |
| `payload.email` | `string` | No | — | Contact email address. Used as a unique key for upsert matching. At least one of `contactId`, `email`, `linkedinUrl`, or `linkedinUrlSalesNav` is required. |
| `payload.linkedinUrl` | `string` | No | — | LinkedIn profile URL. Used as an alternative unique key for upsert matching. At least one of `contactId`, `email`, `linkedinUrl`, or `linkedinUrlSalesNav` is required. |
| `payload.linkedinUrlSalesNav` | `string` | No | — | LinkedIn Sales Navigator profile URL. Used as an alternative unique key for upsert matching. |
| `payload.additionalEmails` | `array` | No | — | Additional email addresses for the contact. Each must be a valid email address. |
| `payload.firstName` | `string` | No | — | Contact first name. |
| `payload.lastName` | `string` | No | — | Contact last name. |
| `payload.phone` | `string` | No | — | Contact phone number. |
| `payload.jobTitle` | `string` | No | — | Contact job title. If a company is linked, this is saved as part of the job data. |
| `payload.jobDescription` | `string` | No | — | Contact job description. If a company is linked, this is saved as part of the job data. |
| `payload.picture` | `string` | No | — | URL of the contact's profile picture. |
| `payload.timezone` | `string` | No | — | Contact timezone. |
| `payload.industry` | `string` | No | — | Contact industry. |
| `payload.languages` | `string` | No | — | Contact languages. |
| `payload.location` | `string` | No | — | Contact location. |
| `payload.skills` | `string` | No | — | Contact skills. |
| `payload.summary` | `string` | No | — | Contact summary or bio. |
| `payload.tagline` | `string` | No | — | Contact tagline. |
| `payload.contactOwner` | `string` | No | — | 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 contact is still written with an `OWNER_NOT_FOUND` or `INVALID_OWNER_FORMAT` entry in `warnings`: the owner defaults to the API key owner on creation and is unchanged on update. On update it replaces the current owner, except under `fillEmptyOnly` where it is only set when the contact has none. |
| `payload.source` | `string` | No | `"api"` | Origin of the contact record. Set on creation only and cannot be updated afterwards. Defaults to `api`. |
| `payload.companyId` | `string` | No | — | ID of a company already existing in lemlist to link to this contact. Takes priority over `companyDomain` and `companyLinkedinUrl`. |
| `payload.companyDomain` | `string` | No | — | Domain of a company already existing in lemlist to link to this contact (e.g. `lemlist.com`). Used if `companyId` is not provided. |
| `payload.companyLinkedinUrl` | `string` | No | — | LinkedIn URL of a company already existing in lemlist to link to this contact. Used if `companyId` and `companyDomain` are not provided. |

<details>
  <summary>Show raw input schema</summary>

  ### Input JSON Schema

  ```json theme={null}
  {
    "type": "object",
    "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 is updated as the `updateStrategy` query parameter says (by default, sent values replace stored ones and empty values are ignored). 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`.",
    "properties": {
      "updateStrategy": {
        "type": "string",
        "description": "What an existing record keeps. `overwriteIgnoreEmpty` (default): sent values replace stored ones, empty values are ignored. `overwrite`: an empty string clears the field. `fillEmptyOnly`: only empty fields are filled, identifiers, links, owner and status kept. A new record receives every value sent. Other values: `400 INVALID_UPDATE_STRATEGY`.",
        "default": "overwriteIgnoreEmpty",
        "enum": [
          "overwrite",
          "overwriteIgnoreEmpty",
          "fillEmptyOnly"
        ]
      },
      "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",
        "description": "Additional email addresses for the contact. Each must be a valid email address.",
        "items": {
          "type": "string"
        }
      },
      "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 contact is still written with an `OWNER_NOT_FOUND` or `INVALID_OWNER_FORMAT` entry in `warnings`: the owner defaults to the API key owner on creation and is unchanged on update. On update it replaces the current owner, except under `fillEmptyOnly` where it is only set when the contact has none."
      },
      "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."
      }
    },
    "required": [],
    "additionalProperties": {
      "description": "Any additional key is treated as a custom field. Custom fields must be registered in the team's CRM field registry beforehand."
    }
  }
  ```
</details>

## Output reference

Standard tool result payload.

| Name | Type | Required | Default | Details |
| - | - | - | - | - |
| `result.data` | `object` | Yes | — | Provider response payload. |
| `result.data.success` | `boolean` | No | — | — |
| `result.data.data` | `record` | No | — | Same shape as the 201 response, with `created: false` and `updated: true`. |
| `result.data.warnings` | `array` | No | — | Non-blocking notices: owner or company not resolved, or `FIELDS_KEPT` when the update strategy kept values you sent (`params.fields` names them). |
| `result.data.warnings[].code` | `string` | No | — | `OWNER_NOT_FOUND` or `INVALID_OWNER_FORMAT` (`contactOwner` matches no team member); `COMPANY_NOT_FOUND_BY_ID`, `COMPANY_NOT_FOUND_BY_DOMAIN` or `COMPANY_NOT_FOUND_BY_LINKEDIN_URL` (company not linked); `FIELDS_KEPT` (values the update strategy kept). |
| `result.data.warnings[].message` | `string` | No | — | — |
| `result.data.warnings[].params` | `record` | No | — | Details: the input echoed back, or `fields` for `FIELDS_KEPT`. |
| `result.meta` | `object` | No | — | Additional response metadata (status, paging). |

<details>
  <summary>Show raw output schema</summary>

  ### Output JSON Schema

  ```json theme={null}
  {
    "type": "object",
    "description": "Standard tool result payload.",
    "properties": {
      "data": {
        "type": "object",
        "description": "Provider response payload.",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "description": "Same shape as the 201 response, with `created: false` and `updated: true`."
          },
          "warnings": {
            "type": "array",
            "description": "Non-blocking notices: owner or company not resolved, or `FIELDS_KEPT` when the update strategy kept values you sent (`params.fields` names them).",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "description": "`OWNER_NOT_FOUND` or `INVALID_OWNER_FORMAT` (`contactOwner` matches no team member); `COMPANY_NOT_FOUND_BY_ID`, `COMPANY_NOT_FOUND_BY_DOMAIN` or `COMPANY_NOT_FOUND_BY_LINKEDIN_URL` (company not linked); `FIELDS_KEPT` (values the update strategy kept)."
                },
                "message": {
                  "type": "string"
                },
                "params": {
                  "type": "object",
                  "description": "Details: the input echoed back, or `fields` for `FIELDS_KEPT`.",
                  "additionalProperties": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    ]
                  }
                }
              },
              "additionalProperties": false
            }
          }
        },
        "additionalProperties": false
      },
      "meta": {
        "type": "object",
        "description": "Additional response metadata (status, paging).",
        "additionalProperties": true
      }
    },
    "required": [
      "data"
    ],
    "additionalProperties": false
  }
  ```
</details>

## Deepline cost

* Pricing model: `fixed` (per call).
* Estimated Deepline credits: `0` per pricing unit.
* Provider-native pricing may still exist outside Deepline credit billing.

## Related documentation

* [Lemlist provider guide](/docs/providers/lemlist/guide)
* [SDK V2 quickstart](/docs/sdk-v2/quickstart)
* [SDK reference](/docs/sdk-v2/sdk-reference)
* [Run tools across a CSV](/docs/sdk-v2/batch-csv)


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