---
title: "Accounts API reference"
canonical_url: https://zentrik.ai/docs/api/accounts
markdown_url: https://zentrik.ai/docs/api/accounts.md
last_reviewed: 2026-09-03
---

# Accounts API reference

Manage the workspace people and Accounts that connect product engagement across calls, signals, and insights.

- Human reference: https://zentrik.ai/docs/api/accounts
- Complete Markdown index: https://zentrik.ai/docs/api/index.md
- Base URL: `https://zentrik.ai/api`
- Authentication: `Authorization: Bearer YOUR_API_KEY`
- Shared pagination and rate limits: https://zentrik.ai/docs/api/index.md#shared-conventions

## GET /external/v1/people — List people

Operation ID: `list-people`

List the workspace people directory across all Accounts, including unassigned people created from public email addresses. Each result includes its linked signal count.

Required API key scopes: `accounts:read`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `q` | string | No | Search name, email, role, Account name, or Account domain. |
| `limit` | number | No | Maximum results, from 1 to 100. |
| `offset` | number | No | Number of people to skip. |

### Example request

```curl
curl -X GET 'https://zentrik.ai/api/external/v1/people?q=buyer' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Success | Workspace people were retrieved. | PeoplePage |

### Example response

```json
{
  "items": [
    {
      "id": "person-uuid",
      "name": "Primary product contact",
      "email": "buyer@example.com",
      "role": "VP Product",
      "accountId": "account-uuid",
      "account": {
        "id": "account-uuid",
        "name": "Example",
        "domain": "example.com"
      },
      "signalCount": 4
    }
  ],
  "totalRecords": 1,
  "limit": 50,
  "offset": 0,
  "hasMore": false
}
```

---

## POST /external/v1/people — Create a person

Operation ID: `create-person`

Create a workspace person. accountId is optional, so public-email and not-yet-matched people can exist before Account assignment.

Required API key scopes: `accounts:write`

### JSON request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Yes | Person display name. |
| `email` | string | No | Email used for deterministic matching on later calls. |
| `role` | string | No | Role or title. |
| `accountId` | uuid \| null | No | Account assignment, or null for unassigned. |

### Example request

```json
{
  "name": "Primary product contact",
  "email": "buyer@example.com",
  "role": "VP Product",
  "accountId": "account-uuid"
}
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 201 | Created | The person was created. | Person |
| 409 | Conflict | A person with the same workspace email already exists. | — |

---

## PATCH /external/v1/people/:id — Update or assign a person

Operation ID: `update-person`

Update a person or assign an unassigned person to an Account. Existing signal history stays linked to the person.

Required API key scopes: `accounts:write`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | Yes | The person UUID. |

### JSON request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | No | Updated display name. |
| `email` | string | No | Updated email. |
| `role` | string | No | Updated role or title. |
| `accountId` | uuid \| null | No | New Account assignment, or null to unassign. |

### Example request

```curl
curl -X PATCH https://zentrik.ai/api/external/v1/people/person-uuid \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"accountId":"account-uuid"}'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Updated | The person was updated. | Person |

---

## DELETE /external/v1/people/:id — Delete a person

Operation ID: `delete-person`

Delete a workspace person and its signal links. Signals and Accounts are not deleted.

Required API key scopes: `accounts:write`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | Yes | The person UUID. |

### Example request

```curl
curl -X DELETE https://zentrik.ai/api/external/v1/people/person-uuid \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Deleted | The person was deleted. | — |

---

## GET /external/v1/accounts — List all accounts

Operation ID: `list-accounts`

List accounts in the current workspace. This is the main lookup endpoint for CRM automation before transcript ingestion.

Required API key scopes: `accounts:read`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | number | No | Maximum number of accounts to return. |
| `offset` | number | No | Number of accounts to skip. |
| `q` | string | No | Case-insensitive search across name, domain, website, and externalId. |
| `name` | string | No | Exact account name match (case-insensitive). |
| `domain` | string | No | Exact domain or website match (case-insensitive). |
| `externalId` | string | No | Stable client-side identifier such as crm-example-account. |

### Example request

```curl
curl -X GET 'https://zentrik.ai/api/external/v1/accounts?externalId=crm-example-account' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Success | Accounts were successfully retrieved. | Array<Account> |

### Example response

```json
[
  {
    "id": "account-uuid",
    "name": "Example Account",
    "domain": "example.com",
    "website": "https://example.com",
    "lifecycleStage": "TRIAL",
    "externalId": "crm-example-account",
    "workspaceId": "workspace-uuid",
    "contactIds": [
      "contact-1"
    ],
    "insightIds": [
      "insight-1"
    ],
    "opportunityIds": [
      "opportunity-1"
    ],
    "ideaIds": [
      "idea-1"
    ],
    "createdAt": "2026-04-09T10:00:00Z",
    "updatedAt": "2026-04-09T10:00:00Z"
  }
]
```

---

## GET /external/v1/accounts/duplicate-candidates — Find duplicate accounts

Operation ID: `find-duplicate-accounts`

Find likely duplicate Account groups in the workspace before a merge. Candidates are grouped by shared domain, normalized name, or legal name and ordered by confidence. This operation does not change data; a candidate is evidence for review, not an automatic merge decision.

Required API key scopes: `accounts:read`

### Example request

```curl
curl -X GET https://zentrik.ai/api/external/v1/accounts/duplicate-candidates \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Success | Likely duplicate Account groups were retrieved. | Array<AccountDuplicateCandidate> |

### Example response

```json
[
  {
    "score": 100,
    "reason": "domain",
    "accounts": [
      {
        "id": "account-uuid-1",
        "name": "Example",
        "domain": "example.com",
        "lifecycleStage": "ACTIVE"
      },
      {
        "id": "account-uuid-2",
        "name": "Example Inc.",
        "domain": "example.com",
        "lifecycleStage": "TRIAL"
      }
    ]
  }
]
```

---

## GET /external/v1/accounts/:id — Get one account

Operation ID: `get-account`

Retrieve one account from the current workspace.

Required API key scopes: `accounts:read`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | Yes | The backend account UUID. |

### Example request

```curl
curl -X GET https://zentrik.ai/api/external/v1/accounts/account-uuid \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Success | The account was successfully retrieved. | Account |
| 404 | Not Found | No account found with the provided id. | — |

### Example response

```json
{
  "id": "account-uuid",
  "name": "Example Account",
  "domain": "example.com",
  "website": "https://example.com",
  "lifecycleStage": "TRIAL",
  "externalId": "crm-example-account",
  "workspaceId": "workspace-uuid",
  "contactIds": [
    "contact-1"
  ],
  "insightIds": [
    "insight-1"
  ],
  "opportunityIds": [
    "opportunity-1"
  ],
  "ideaIds": [
    "idea-1"
  ],
  "createdAt": "2026-04-09T10:00:00Z",
  "updatedAt": "2026-04-09T10:00:00Z"
}
```

---

## POST /external/v1/accounts — Create an account

Operation ID: `create-account`

Create an account in the current workspace. CRM agents typically use `externalId` as the durable bridge between local records and Zentrik accounts. Manage contacts with the account contacts endpoints after the account exists.

Required API key scopes: `accounts:write`

### JSON request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Yes | Account display name. |
| `externalId` | string | No | Stable client-side identifier such as crm-example-account. |
| `domain` | string | No | Primary company domain. |
| `website` | string | No | Canonical website URL. |
| `industry` | string | No | Industry or vertical label. |
| `companySize` | string | No | Company size bucket. |
| `lifecycleStage` | string | No | Canonical Zentrik lifecycle stage: LEAD, TRIAL, ACTIVE, CHURNED, or PARTNER. |
| `notes` | string | No | Supplemental operator or CRM notes. |

### Example request

```json
{
  "name": "Example Account",
  "externalId": "crm-example-account",
  "domain": "example.com",
  "website": "https://example.com",
  "industry": "B2B software",
  "companySize": "11-50",
  "lifecycleStage": "TRIAL",
  "notes": "Primary contact evaluates roadmap fit."
}
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 201 | Created | The account was successfully created. | Account |

### Example response

```json
{
  "id": "account-uuid",
  "name": "Example Account",
  "externalId": "crm-example-account",
  "domain": "example.com",
  "website": "https://example.com",
  "industry": "B2B software",
  "companySize": "11-50",
  "lifecycleStage": "TRIAL",
  "workspaceId": "workspace-uuid",
  "contactIds": [],
  "insightIds": [],
  "opportunityIds": [],
  "ideaIds": [],
  "createdAt": "2026-04-09T10:00:00Z",
  "updatedAt": "2026-04-09T10:00:00Z"
}
```

---

## PATCH /external/v1/accounts/:id — Update an account

Operation ID: `update-account`

Update an existing account in the current workspace. Contact arrays are not accepted here; use the dedicated contact endpoints for people and roles.

Required API key scopes: `accounts:write`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | Yes | The backend account UUID. |

### JSON request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | No | Updated display name. |
| `externalId` | string | No | Updated stable client-side identifier. |
| `domain` | string | No | Updated primary company domain. |
| `website` | string | No | Updated canonical website URL. |
| `industry` | string | No | Updated industry or vertical label. |
| `companySize` | string | No | Updated company size bucket. |
| `lifecycleStage` | string | No | Updated canonical Zentrik lifecycle stage. |
| `notes` | string | No | Updated operator or CRM notes. |

### Example request

```curl
curl -X PATCH https://zentrik.ai/api/external/v1/accounts/account-uuid \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"lifecycleStage":"ACTIVE"}'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Updated | The account was successfully updated. | Account |

---

## DELETE /external/v1/accounts/:id — Delete an account

Operation ID: `delete-account`

Delete an account from the current workspace.

Required API key scopes: `accounts:delete`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | Yes | The backend account UUID. |

### Example request

```curl
curl -X DELETE https://zentrik.ai/api/external/v1/accounts/account-uuid \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Deleted | The account was successfully deleted. | — |

---

## POST /external/v1/accounts/:id/merge — Preview or merge duplicate accounts

Operation ID: `merge-accounts`

Preview or execute a destructive duplicate Account merge. The path Account survives; source Accounts are folded into it and deleted. Start with dryRun set to true, review moved records and field choices, resolve every blocking conflict, then repeat the same request with dryRun set to false.

Required API key scopes: `accounts:delete`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | Yes | The Account UUID that must survive the merge. |

### JSON request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `sourceAccountIds` | uuid[] | Yes | One or more duplicate Account UUIDs to fold into the survivor and delete. |
| `dryRun` | boolean | No | Set true to return the merge plan without writing. Defaults to false. |
| `fieldOverrides` | object | No | Explicit survivor field values to use when duplicate records disagree. |

### Example request

```curl
curl -X POST https://zentrik.ai/api/external/v1/accounts/account-uuid-1/merge \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"sourceAccountIds":["account-uuid-2"],"dryRun":true}'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Success | A dry run returns the merge plan. An executed merge returns the surviving Account. | AccountMergePreview \| Account |
| 400 | Invalid request | The source list is empty, includes the survivor, or contains an invalid Account reference. | — |
| 404 | Not found | The survivor or a source Account is outside the API-key workspace or does not exist. | — |
| 409 | Blocking conflict | A source identity or integration binding must be resolved before the Accounts can be merged. | — |

### Example response

```json
{
  "survivorId": "account-uuid-1",
  "survivorName": "Example",
  "losers": [
    {
      "id": "account-uuid-2",
      "name": "Example Inc."
    }
  ],
  "moves": {
    "signals": 3,
    "insights": 2,
    "ideas": 0,
    "opportunities": 1,
    "contacts": 2
  },
  "totalMoves": 8,
  "fieldResolutions": [],
  "conflicts": [],
  "blocking": false
}
```

---

## GET /external/v1/accounts/:id/contacts — List account contacts

Operation ID: `list-account-contacts`

List contacts linked to an account in the current workspace. Use this before contact sync automation rather than relying on account `contactIds` alone.

Required API key scopes: `accounts:read`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | Yes | The backend account UUID. |

### Example request

```curl
curl -X GET https://zentrik.ai/api/external/v1/accounts/account-uuid/contacts \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Success | Contacts were successfully retrieved. | Array<Contact> |
| 404 | Not Found | No account found with the provided id in the current workspace. | — |

### Example response

```json
[
  {
    "id": "contact-uuid",
    "accountId": "account-uuid",
    "name": "Primary product contact",
    "role": "VP Product",
    "email": "product-contact@example.com",
    "phone": null,
    "notes": "Primary product evaluator and champion.",
    "isPrimary": true,
    "createdAt": "2026-04-09T10:00:00Z",
    "updatedAt": "2026-04-09T10:00:00Z"
  }
]
```

---

## POST /external/v1/accounts/:id/contacts — Create an account contact

Operation ID: `create-account-contact`

Create one contact on an account. This is the preferred way to add contacts; do not patch the account `contacts` array for incremental updates.

Required API key scopes: `accounts:write`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | Yes | The backend account UUID. |

### JSON request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Yes | Contact display name. |
| `role` | string | No | Role, title, or account-specific responsibility. |
| `email` | string | No | Email address. |
| `phone` | string | No | Phone number. |
| `notes` | string | No | Operator notes such as influence, interests, and outreach guidance. |
| `isPrimary` | boolean | No | Whether this is the primary contact for the account. |

### Example request

```json
{
  "name": "Primary product contact",
  "role": "VP Product",
  "email": "product-contact@example.com",
  "notes": "Primary product evaluator and champion.",
  "isPrimary": true
}
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 201 | Created | The contact was successfully created. | Contact |
| 404 | Not Found | No account found with the provided id in the current workspace. | — |

### Example response

```json
{
  "id": "contact-uuid",
  "accountId": "account-uuid",
  "name": "Primary product contact",
  "role": "VP Product",
  "email": "product-contact@example.com",
  "notes": "Primary product evaluator and champion.",
  "isPrimary": true,
  "createdAt": "2026-04-09T10:00:00Z",
  "updatedAt": "2026-04-09T10:00:00Z"
}
```

---

## PATCH /external/v1/accounts/:id/contacts/:contactId — Update an account contact

Operation ID: `update-account-contact`

Update one contact on an account. The contact must belong to the requested account in the current workspace.

Required API key scopes: `accounts:write`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | Yes | The backend account UUID. |
| `contactId` | uuid | Yes | The backend contact UUID. |

### JSON request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | No | Updated contact display name. |
| `role` | string | No | Updated role, title, or account-specific responsibility. |
| `email` | string | No | Updated email address. |
| `phone` | string | No | Updated phone number. |
| `notes` | string | No | Updated operator notes. |
| `isPrimary` | boolean | No | Updated primary-contact flag. |

### Example request

```curl
curl -X PATCH https://zentrik.ai/api/external/v1/accounts/account-uuid/contacts/contact-uuid \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"notes":"Strategic evaluator. Reach out for roadmap and pricing context."}'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Updated | The contact was successfully updated. | Contact |
| 404 | Not Found | No account/contact pair found in the current workspace. | — |

---

## DELETE /external/v1/accounts/:id/contacts/:contactId — Delete an account contact

Operation ID: `delete-account-contact`

Remove one person from an Account without deleting the person or their engagement history.

Required API key scopes: `accounts:write`

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | uuid | Yes | The backend account UUID. |
| `contactId` | uuid | Yes | The backend contact UUID. |

### Example request

```curl
curl -X DELETE https://zentrik.ai/api/external/v1/accounts/account-uuid/contacts/contact-uuid \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

### Responses

| Status | Meaning | Description | Schema |
| --- | --- | --- | --- |
| 200 | Unassigned | The person is now unassigned and remains available in People. | — |
| 404 | Not Found | No account/contact pair found in the current workspace. | — |
