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

# Contacts

> Create, read, update, delete, and bulk upsert contacts

Contacts represent the people in your CRM — leads, customers, prospects, and anyone else captured by your agents or imported from another system. Every contact belongs to a specific agent (`bot_id`) and inherits the workspace from your API key.

## The Contact object

<ResponseField name="id" type="string">
  Unique contact identifier.
</ResponseField>

<ResponseField name="workspace_id" type="string">
  The workspace this contact belongs to.
</ResponseField>

<ResponseField name="bot_id" type="string">
  The agent that owns this contact.
</ResponseField>

<ResponseField name="first_name" type="string | null" />

<ResponseField name="last_name" type="string | null" />

<ResponseField name="emails" type="array">
  Array of `{ email, is_primary }` objects. The first item is treated as primary if no `is_primary` flag is set.
</ResponseField>

<ResponseField name="phone_numbers" type="array">
  Array of `{ phone_number, is_primary }` objects.
</ResponseField>

<ResponseField name="company" type="string | null">
  Free-text company name. Use `company_id` to link to a structured Company record.
</ResponseField>

<ResponseField name="company_id" type="string | null">
  Foreign key to a [Company](/api-reference/companies).
</ResponseField>

<ResponseField name="job_title" type="string | null" />

<ResponseField name="lead_source" type="string | null" />

<ResponseField name="tags" type="string[]" />

<ResponseField name="deal_value" type="number | null">
  Estimated deal value as a number (currency assumed to match workspace settings).
</ResponseField>

<ResponseField name="pipeline_stage_id" type="string | null">
  Current stage in a sales pipeline.
</ResponseField>

<ResponseField name="contact_type" type="string | null">
  Free-form classification (e.g. `"lead"`, `"customer"`).
</ResponseField>

<ResponseField name="address_line1" type="string | null" />

<ResponseField name="address_line2" type="string | null" />

<ResponseField name="city" type="string | null" />

<ResponseField name="state" type="string | null" />

<ResponseField name="postal_code" type="string | null" />

<ResponseField name="country" type="string | null" />

<ResponseField name="linkedin_url" type="string | null" />

<ResponseField name="notes" type="string | null" />

<ResponseField name="custom_fields" type="object">
  Workspace-defined custom fields, stored as a free-form JSON object.
</ResponseField>

<ResponseField name="last_contacted_at" type="string | null">
  ISO 8601 timestamp of the most recent activity.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 timestamp.
</ResponseField>

<ResponseField name="updated_at" type="string">
  ISO 8601 timestamp.
</ResponseField>

***

## List contacts

<ParamField path="method" type="GET">
  `GET /api/v1/contacts`
</ParamField>

Returns a paginated list of contacts in the workspace.

### Query parameters

| Param | Type | Description |
| - | - | - |
| `search` | string | Match against name, email, phone, or company |
| `tags` | string | Comma-separated list of tags to filter by |
| `lead_source` | string | Filter by lead source |
| `pipeline_stage_id` | string | Filter to contacts in a specific pipeline stage |
| `bot_id` | string | Filter to contacts owned by a specific agent |
| `created_after` | string (ISO 8601) | Created at or after this timestamp |
| `created_before` | string (ISO 8601) | Created at or before this timestamp |
| `updated_after` | string (ISO 8601) | Updated at or after this timestamp |
| `page` | integer | See [Pagination](/api-reference/pagination) |
| `page_size` | integer | See [Pagination](/api-reference/pagination) |

### Example

```bash theme={null}
curl "https://clarky.ai/api/v1/contacts?search=ada&page_size=10" \
  -H "Authorization: Bearer ck_live_example"
```

```json theme={null}
{
  "data": [
    {
      "id": "c_01HXYZ...",
      "workspace_id": "w_01HABC...",
      "bot_id": "b_01HDEF...",
      "first_name": "Ada",
      "last_name": "Lovelace",
      "emails": [{ "email": "ada@example.com", "is_primary": true }],
      "phone_numbers": [],
      "company": "Analytical Engines Inc.",
      "company_id": null,
      "job_title": "Founder",
      "lead_source": "website",
      "tags": ["vip"],
      "deal_value": 25000,
      "pipeline_stage_id": "ps_01HGHI...",
      "contact_type": "lead",
      "address_line1": null,
      "address_line2": null,
      "city": "London",
      "state": null,
      "postal_code": null,
      "country": "UK",
      "linkedin_url": null,
      "notes": null,
      "custom_fields": {},
      "last_contacted_at": "2026-04-29T14:00:00.000Z",
      "created_at": "2026-04-12T09:30:00.000Z",
      "updated_at": "2026-04-29T14:00:00.000Z"
    }
  ],
  "pagination": { "page": 1, "page_size": 10, "total": 1, "total_pages": 1 }
}
```

***

## Get contact by email

<ParamField path="method" type="GET">
  `GET /api/v1/contacts/by-email`
</ParamField>

Looks up a single contact by primary email address. Returns `404 not_found` if no contact matches.

### Query parameters

| Param | Type | Required | Description |
| - | - | - | - |
| `email` | string | Yes | Email to match (case-insensitive) |

### Example

```bash theme={null}
curl "https://clarky.ai/api/v1/contacts/by-email?email=ada@example.com" \
  -H "Authorization: Bearer ck_live_example"
```

```json theme={null}
{
  "data": {
    "id": "c_01HXYZ...",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "emails": [{ "email": "ada@example.com", "is_primary": true }],
    "phone_numbers": [],
    "company": "Analytical Engines Inc.",
    "tags": ["vip"],
    "created_at": "2026-04-12T09:30:00.000Z",
    "updated_at": "2026-04-29T14:00:00.000Z"
  }
}
```

***

## Get a contact

<ParamField path="method" type="GET">
  `GET /api/v1/contacts/{id}`
</ParamField>

### Example

```bash theme={null}
curl https://clarky.ai/api/v1/contacts/c_01HXYZ \
  -H "Authorization: Bearer ck_live_example"
```

```json theme={null}
{
  "data": {
    "id": "c_01HXYZ...",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "emails": [{ "email": "ada@example.com", "is_primary": true }],
    "phone_numbers": [],
    "tags": ["vip"],
    "created_at": "2026-04-12T09:30:00.000Z",
    "updated_at": "2026-04-29T14:00:00.000Z"
  }
}
```

***

## Create a contact

<ParamField path="method" type="POST">
  `POST /api/v1/contacts`
</ParamField>

Requires a key with the `write` scope.

### Body fields

| Field | Type | Required | Description |
| - | - | - | - |
| `bot_id` | string | Yes | Agent that should own this contact |
| `first_name` | string | No | |
| `last_name` | string | No | |
| `emails` | array | No | Array of `{ email, is_primary }` |
| `phone_numbers` | array | No | Array of `{ phone_number, is_primary }` |
| `company` | string | No | Free-text company name |
| `company_id` | string | No | Link to an existing Company |
| `job_title` | string | No | |
| `lead_source` | string | No | |
| `tags` | string\[] | No | |
| `deal_value` | number | No | |
| `pipeline_stage_id` | string | No | |
| `contact_type` | string | No | |
| `address_line1` | string | No | |
| `address_line2` | string | No | |
| `city` | string | No | |
| `state` | string | No | |
| `postal_code` | string | No | |
| `country` | string | No | |
| `linkedin_url` | string | No | |
| `notes` | string | No | |
| `custom_fields` | object | No | |

### Example

```bash theme={null}
curl https://clarky.ai/api/v1/contacts \
  -H "Authorization: Bearer ck_live_example" \
  -H "Content-Type: application/json" \
  -d '{
    "bot_id": "b_01HDEF",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "emails": [{ "email": "ada@example.com", "is_primary": true }],
    "company": "Analytical Engines Inc.",
    "tags": ["vip"]
  }'
```

```json theme={null}
{
  "data": {
    "id": "c_01HXYZ...",
    "first_name": "Ada",
    "last_name": "Lovelace",
    "emails": [{ "email": "ada@example.com", "is_primary": true }],
    "company": "Analytical Engines Inc.",
    "tags": ["vip"],
    "created_at": "2026-04-29T18:42:00.000Z",
    "updated_at": "2026-04-29T18:42:00.000Z"
  }
}
```

***

## Update a contact

<ParamField path="method" type="PATCH">
  `PATCH /api/v1/contacts/{id}`
</ParamField>

Updates the listed fields and leaves everything else untouched. Same fields as [Create a contact](#create-a-contact), **except** `bot_id` cannot be changed.

### Example

```bash theme={null}
curl -X PATCH https://clarky.ai/api/v1/contacts/c_01HXYZ \
  -H "Authorization: Bearer ck_live_example" \
  -H "Content-Type: application/json" \
  -d '{
    "tags": ["vip", "newsletter"],
    "deal_value": 30000
  }'
```

```json theme={null}
{
  "data": {
    "id": "c_01HXYZ...",
    "tags": ["vip", "newsletter"],
    "deal_value": 30000,
    "updated_at": "2026-04-29T18:50:00.000Z"
  }
}
```

***

## Delete a contact

<ParamField path="method" type="DELETE">
  `DELETE /api/v1/contacts/{id}`
</ParamField>

Returns `204 No Content` with an empty body on success.

### Example

```bash theme={null}
curl -X DELETE https://clarky.ai/api/v1/contacts/c_01HXYZ \
  -H "Authorization: Bearer ck_live_example"
```

<Warning>
  Deletion is permanent — there's no undo.
</Warning>

***

## Bulk upsert contacts

<ParamField path="method" type="POST">
  `POST /api/v1/contacts/upsert`
</ParamField>

Insert or update up to **200 contacts** in a single request. Each input contact is matched against existing contacts in this order:

1. Primary email (if provided)
2. Primary phone number (if no email match)

If a match is found, the existing contact is updated. If not, a new contact is created.

### Body fields

| Field | Type | Required | Description |
| - | - | - | - |
| `bot_id` | string | Yes | Agent to own newly created contacts |
| `contacts` | array | Yes | Array of contact objects (same fields as [Create](#create-a-contact)). Max 200. |

### Example

```bash theme={null}
curl https://clarky.ai/api/v1/contacts/upsert \
  -H "Authorization: Bearer ck_live_example" \
  -H "Content-Type: application/json" \
  -d '{
    "bot_id": "b_01HDEF",
    "contacts": [
      {
        "first_name": "Ada",
        "emails": [{ "email": "ada@example.com", "is_primary": true }]
      },
      {
        "first_name": "Grace",
        "emails": [{ "email": "grace@example.com", "is_primary": true }]
      }
    ]
  }'
```

### Response

```json theme={null}
{
  "data": {
    "created": [
      {
        "id": "c_01HNEW1...",
        "first_name": "Grace",
        "emails": [{ "email": "grace@example.com", "is_primary": true }]
      }
    ],
    "updated": [
      {
        "id": "c_01HXYZ...",
        "first_name": "Ada",
        "emails": [{ "email": "ada@example.com", "is_primary": true }]
      }
    ],
    "errors": []
  }
}
```

<ResponseField name="created" type="Contact[]">
  Contacts that were newly inserted.
</ResponseField>

<ResponseField name="updated" type="Contact[]">
  Contacts that matched an existing record and were updated.
</ResponseField>

<ResponseField name="errors" type="array">
  Per-row failures as `{ index, message }`. The numeric `index` is the position of the failing item in the input `contacts` array. The rest of the batch still succeeds.
</ResponseField>

<Tip>
  Upsert counts as a single write against your [rate limit](/api-reference/rate-limits) regardless of how many contacts are in the batch — much more efficient than a loop of `POST /contacts` calls.
</Tip>


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