---
title: Contacts
description: Create, read, update and delete the people at your Accounts.
product: signal
section: api
order: 11
---

# Contacts

A Contact is a person at one of your Accounts. Every Contact belongs to an Account. For people you're still prospecting, use [prospect contacts](prospects.md) instead.

## The Contact object

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string | UUID. |
| `first_name` | string | Can be empty. |
| `last_name` | string | Can be empty. |
| `email` | string or null | |
| `phone` | string or null | |
| `mobile` | string or null | |
| `job_title` | string or null | |
| `department` | string or null | |
| `linkedin_url` | string or null | |
| `account_id` | string | The Account the Contact belongs to. |
| `is_primary` | boolean | The Account's primary Contact. Read-only through the API. |
| `is_champion` | boolean | Marked as a champion. Read-only through the API. |
| `created_at` | string | ISO 8601 timestamp. |
| `updated_at` | string | ISO 8601 timestamp. |

```json
{
  "id": "a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54",
  "first_name": "Maria",
  "last_name": "Garcia",
  "email": "maria.garcia@contoso.example",
  "phone": "+1 312 555 0187",
  "mobile": null,
  "job_title": "Head of Procurement",
  "department": "Operations",
  "linkedin_url": null,
  "account_id": "3f6c2a1e-8b4d-4c2a-9f1e-2b7d5a9c4e10",
  "is_primary": false,
  "is_champion": false,
  "created_at": "2026-11-12T09:30:00.000Z",
  "updated_at": "2026-11-12T09:30:00.000Z"
}
```

---

## List Contacts

`GET /contacts` · Scope: `contacts:read`

Returns Contacts newest first. Archived Contacts and Contacts in **Recently deleted** aren't listed.

### Query parameters

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `limit` | integer | No | 1 to 100. Default 20. |
| `cursor` | string | No | `meta.cursor` from the previous page. |

### Example

```bash
curl "https://crm.empiraa.app/api/v1/contacts?limit=100" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```js
const res = await fetch("https://crm.empiraa.app/api/v1/contacts?limit=100", {
  headers: { Authorization: "Bearer YOUR_API_KEY" },
});
const { data, meta } = await res.json();
```

### Response `200`

```json
{
  "data": [
    {
      "id": "a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54",
      "first_name": "Maria",
      "last_name": "Garcia",
      "email": "maria.garcia@contoso.example",
      "phone": null,
      "mobile": null,
      "job_title": "Head of Procurement",
      "department": null,
      "linkedin_url": null,
      "account_id": "3f6c2a1e-8b4d-4c2a-9f1e-2b7d5a9c4e10",
      "is_primary": false,
      "is_champion": false,
      "created_at": "2026-11-12T09:30:00.000Z",
      "updated_at": "2026-11-12T09:30:00.000Z"
    }
  ],
  "meta": { "cursor": null, "total": 1, "hasMore": false },
  "error": null
}
```

`meta.total` is the number of Contacts in the workspace.

---

## Create a Contact

`POST /contacts` · Scope: `contacts:write`

### Request body

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `account_id` | string | Yes | The Account the Contact belongs to. |
| `first_name` | string | No | Defaults to an empty string. |
| `last_name` | string | No | Defaults to an empty string. |
| `email` | string or null | No | |
| `phone` | string or null | No | |
| `mobile` | string or null | No | |
| `job_title` | string or null | No | |
| `department` | string or null | No | |
| `linkedin_url` | string or null | No | |

### Example

```bash
curl -X POST https://crm.empiraa.app/api/v1/contacts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "account_id": "3f6c2a1e-8b4d-4c2a-9f1e-2b7d5a9c4e10",
    "first_name": "Maria",
    "last_name": "Garcia",
    "email": "maria.garcia@contoso.example",
    "job_title": "Head of Procurement"
  }'
```

```js
const res = await fetch("https://crm.empiraa.app/api/v1/contacts", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({
    account_id: "3f6c2a1e-8b4d-4c2a-9f1e-2b7d5a9c4e10",
    first_name: "Maria",
    last_name: "Garcia",
    email: "maria.garcia@contoso.example",
    job_title: "Head of Procurement",
  }),
});
```

### Response `201`

The new [Contact object](#the-contact-object) in `data`.

### Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_REQUEST` | `account_id` is missing or not a string. |
| 404 | `NOT_FOUND` | `account_id` isn't an Account in your workspace ("Account not found"). |

**Webhook events:** [`contact.created`](../webhooks.md#event-catalog).

---

## Get a Contact

`GET /contacts/{id}` · Scope: `contacts:read`

### Path parameters

| Parameter | Type | Notes |
| --- | --- | --- |
| `id` | string | Contact ID. |

### Example

```bash
curl https://crm.empiraa.app/api/v1/contacts/a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```js
await fetch("https://crm.empiraa.app/api/v1/contacts/a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54", {
  headers: { Authorization: "Bearer YOUR_API_KEY" },
});
```

### Response `200`

The [Contact object](#the-contact-object) in `data`.

### Errors

| Status | Code | When |
| --- | --- | --- |
| 404 | `NOT_FOUND` | No Contact with that ID in this workspace. |

---

## Update a Contact

`PATCH /contacts/{id}` · Scope: `contacts:write`

Send only the fields you want to change.

### Request body

| Field | Type | Notes |
| --- | --- | --- |
| `first_name` | string | |
| `last_name` | string | |
| `email` | string or null | |
| `phone` | string or null | |
| `job_title` | string or null | |
| `department` | string or null | |

Other fields, including `mobile`, `linkedin_url` and `account_id`, can't be changed through this endpoint and are ignored.

### Example

```bash
curl -X PATCH https://crm.empiraa.app/api/v1/contacts/a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"job_title": "VP Procurement"}'
```

```js
await fetch("https://crm.empiraa.app/api/v1/contacts/a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54", {
  method: "PATCH",
  headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ job_title: "VP Procurement" }),
});
```

### Response `200`

The updated [Contact object](#the-contact-object) in `data`.

### Errors

| Status | Code | When |
| --- | --- | --- |
| 404 | `NOT_FOUND` | No Contact with that ID in this workspace. |

**Webhook events:** none.

---

## Delete a Contact

`DELETE /contacts/{id}` · Scope: `contacts:write`

Moves the Contact to **Recently deleted** in Signal. It stays there for 30 days, during which it can be restored from **Settings → Recently deleted**. After 30 days it's removed for good.

### Example

```bash
curl -X DELETE https://crm.empiraa.app/api/v1/contacts/a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```js
await fetch("https://crm.empiraa.app/api/v1/contacts/a1d4e7b2-5c3f-4e8a-b9d6-0f2c7e1a3b54", {
  method: "DELETE",
  headers: { Authorization: "Bearer YOUR_API_KEY" },
});
```

### Response `204`

No body.

### Errors

| Status | Code | When |
| --- | --- | --- |
| 404 | `NOT_FOUND` | No Contact with that ID in this workspace. |

**Webhook events:** [`contact.deleted`](../webhooks.md#event-catalog).
