Skip to documentation

Accounts

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

27 endpoints

GET

List people

GET /external/v1/people

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

Requirements

API scopes required:
accounts:read

Request

Parameters

NameTypeDescription
q stringSearch name, email, role, Account name, or Account domain.
limit numberMaximum results, from 1 to 100.
offset numberNumber of people to skip.

Responses

200
Success

Workspace people were retrieved.

Schema
PeoplePage

Example Request

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

Example Response

200 OK
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

Create a person

POST /external/v1/people

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

Requirements

API scopes required:
accounts:write

Request

Request body (application/json)

namestring
Required

Person display name.

emailstring

Email used for deterministic matching on later calls.

rolestring

Role or title.

accountIduuid | null

Account assignment, or null for unassigned.

Responses

201
Created

The person was created.

Schema
Person
409
Conflict

A person with the same workspace email already exists.

Example Request

POST
/external/v1/people
json
{
  "name": "Primary product contact",
  "email": "buyer@example.com",
  "role": "VP Product",
  "accountId": "account-uuid"
}
PATCH

Update or assign a person

PATCH /external/v1/people/:id

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

Requirements

API scopes required:
accounts:write

Request

Parameters

NameTypeDescription
id *uuidThe person UUID.

Request body (application/json)

namestring

Updated display name.

emailstring

Updated email.

rolestring

Updated role or title.

accountIduuid | null

New Account assignment, or null to unassign.

Responses

200
Updated

The person was updated.

Schema
Person

Example Request

PATCH
/external/v1/people/:id
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"}'
DELETE

Delete a person

DELETE /external/v1/people/:id

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

Requirements

API scopes required:
accounts:write

Request

Parameters

NameTypeDescription
id *uuidThe person UUID.

Responses

200
Deleted

The person was deleted.

Example Request

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

List all accounts

GET /external/v1/accounts

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

Requirements

API scopes required:
accounts:read

Request

Parameters

NameTypeDescription
limit numberMaximum number of accounts to return.
offset numberNumber of accounts to skip.
q stringCase-insensitive search across name, domain, website, and externalId.
name stringExact account name match (case-insensitive).
domain stringExact domain or website match (case-insensitive).
externalId stringStable client-side identifier such as crm-example-account.
updatedSince iso-dateOnly accounts whose own fields changed at or after this ISO-8601 timestamp, oldest change first. Pass the latest updatedAt you have received to sync incrementally, and dedupe by id. Changes made only to contacts or source links may not change an account’s updatedAt.

Responses

200
Success

Accounts were successfully retrieved.

Schema
Array<Account>

Example Request

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

Example Response

200 OK
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

Find duplicate accounts

GET /external/v1/accounts/duplicate-candidates

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.

Requirements

API scopes required:
accounts:read

Responses

200
Success

Likely duplicate Account groups were retrieved.

Schema
Array<AccountDuplicateCandidate>

Example Request

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

Example Response

200 OK
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

Get one account

GET /external/v1/accounts/:id

Retrieve one account from the current workspace.

Requirements

API scopes required:
accounts:read

Request

Parameters

NameTypeDescription
id *uuidThe backend account UUID.

Responses

200
Success

The account was successfully retrieved.

Schema
404
Not Found

No account found with the provided id.

Example Request

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

Example Response

200 OK
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

Create an account

POST /external/v1/accounts

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.

Requirements

API scopes required:
accounts:write

Request

Request body (application/json)

namestring
Required

Account display name.

externalIdstring

Stable client-side identifier such as crm-example-account.

domainstring

Primary company domain.

websitestring

Canonical website URL.

industrystring

Industry or vertical label.

companySizestring

Company size bucket.

lifecycleStagestring

Canonical Zentrik lifecycle stage: LEAD, TRIAL, ACTIVE, CHURNED, or PARTNER.

dataClassificationstring

Account type tag: EXTERNAL (default), INTERNAL, or TEST. Tags organize and filter accounts; they do not change revenue totals.

notesstring

Supplemental operator or CRM notes.

Responses

201
Created

The account was successfully created.

Schema

Example Request

POST
/external/v1/accounts
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."
}

Example Response

201 Created
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

Update an account

PATCH /external/v1/accounts/:id

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

Requirements

API scopes required:
accounts:write

Request

Parameters

NameTypeDescription
id *uuidThe backend account UUID.

Request body (application/json)

namestring

Updated display name.

externalIdstring

Updated stable client-side identifier.

domainstring

Updated primary company domain.

websitestring

Updated canonical website URL.

industrystring

Updated industry or vertical label.

companySizestring

Updated company size bucket.

lifecycleStagestring

Updated canonical Zentrik lifecycle stage.

dataClassificationstring

Updated account type tag: EXTERNAL, INTERNAL, or TEST.

notesstring

Updated operator or CRM notes.

Responses

200
Updated

The account was successfully updated.

Schema

Example Request

PATCH
/external/v1/accounts/:id
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"}'
DELETE

Delete an account

DELETE /external/v1/accounts/:id

Delete an account from the current workspace.

Requirements

API scopes required:
accounts:delete

Request

Parameters

NameTypeDescription
id *uuidThe backend account UUID.

Responses

200
Deleted

The account was successfully deleted.

Example Request

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

Preview or merge duplicate accounts

POST /external/v1/accounts/:id/merge

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 with dryRun set to false. Executed merges retain a bounded recovery journal (10,000 rows, 4 MiB). Undo is available for seven days only when the supported graph is unchanged; unsupported graphs block automatic undo. Some unsupported references or journal limits block the merge itself. Raw recovery snapshots are never returned by the public API.

Requirements

API scopes required:
accounts:delete

Request

Parameters

NameTypeDescription
id *uuidThe Account UUID that must survive the merge.

Request body (application/json)

sourceAccountIdsuuid[]
Required

One or more duplicate Account UUIDs to fold into the survivor and delete.

dryRunboolean

Set true to return the merge plan without writing. Defaults to false.

fieldOverridesobject

Explicit survivor field values to use when duplicate records disagree.

Responses

200
Success

A dry run returns the merge plan. An executed merge returns the surviving Account and mergeOperationId for recovery review.

Schema
AccountMergePreview | (Account & { mergeOperationId: uuid })
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 Request

POST
/external/v1/accounts/:id/merge
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}'

Example Response

200 OK
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

Get account company context

GET /external/v1/accounts/:id/company-context

Read the Account, ancestors, immediate children, source parent proposals, and company measures. Children remain distinct entities, not a physical-location count. Revenue and Evidence remain Account-scoped unless explicitly requested through another supported workflow. This uses the same company-context service as the application.

Requirements

API scopes required:
accounts:read

Request

Parameters

NameTypeDescription
id *uuidThe Account UUID in the API-key workspace.
offset integerChild offset from 0 to 100000; defaults to 0. Pages contain at most 50 children. Follow nextOffset until null.

Responses

200
Success

Returns account, ancestors, children, childCount, offset, nextOffset, sources, sourceParent, measures, and warnings.

Schema
AccountCompanyContext
400
Invalid offset

Offset is outside the supported integer range.

404
Not found

The Account is not in this workspace.

PATCH

Set account parent

PATCH /external/v1/accounts/:id/parent

Set a reviewed immediate parent in this workspace, detach with parentAccountId null, or apply the resolved source parent with useSource true. Source family membership alone does not prove a parent relationship. Loops and invalid relationships are rejected. Uses the application hierarchy service.

Requirements

API scopes required:
accounts:write

Request

Parameters

NameTypeDescription
id *uuidThe Account UUID in the API-key workspace.

Request body (application/json)

parentAccountIduuid | null

Explicit parent Account, or null to detach. Do not combine with useSource true.

useSourceboolean

Set true to use the uniquely resolved source parent instead of a manual parent.

Responses

200
Updated

Returns refreshed company context.

Schema
AccountCompanyContext
409
Conflict

The source parent is unresolved, conflicting, or invalid, or the requested hierarchy would be unsafe.

Example Request

PATCH
/external/v1/accounts/:id/parent
json
{
  "useSource": true
}
GET

Get account measures

GET /external/v1/accounts/:id/measures

Read Account and company operational measures through the same service as the application. Null means unknown, not zero. Self readings describe one Account; company_total readings belong only to the current company root and replace member sums for that measure. Partial or conflicting totals are identified rather than presented as complete.

Requirements

API scopes required:
accounts:read

Request

Parameters

NameTypeDescription
id *uuidThe Account UUID in the API-key workspace.

Responses

200
Success

Returns account and company measure summaries, knownValue, coverage, status, basis, and source readings.

Schema
AccountMeasuresContext
409
Conflict

The hierarchy or measure scope exceeds safe limits; no partial total is substituted.

PATCH

Record an account measure

PATCH /external/v1/accounts/:id/measures

Record a dated, source-attributed operational measure. Repeating the same Account, key, unit, scope, source, and date updates that reading. Use null for an explicitly unknown value. Company totals can be recorded only on the current company root; this does not change ARR or copy a family total to each child.

Requirements

API scopes required:
accounts:write

Request

Parameters

NameTypeDescription
id *uuidThe Account UUID in the API-key workspace.

Request body (application/json)

keystring
Required

Stable lowercase measure key, up to 80 characters.

labelstring
Required

Display label, up to 120 characters.

unitstring
Required

Measure unit, up to 40 characters; for example locations.

valuenumber | null
Required

Finite numeric value, or null for unknown.

scopestring
Required

self or company_total. The latter is root-only.

observedAtdate
Required

Observation date in YYYY-MM-DD format.

sourceKeystring
Required

Source identity, up to 255 characters.

sourceRecordIdstring
Required

Exact source record ID, up to 255 characters.

sourceUrlurl | null

Optional HTTP or HTTPS evidence URL.

Responses

200
Recorded

Returns the source-attributed measure reading.

Schema
AccountMeasureReading
409
Conflict

A company_total reading was requested on a child Account.

Example Request

PATCH
/external/v1/accounts/:id/measures
json
{
  "key": "locations",
  "label": "Locations",
  "unit": "locations",
  "value": null,
  "scope": "self",
  "observedAt": "2026-10-03",
  "sourceKey": "example:inventory",
  "sourceRecordId": "example-record"
}
POST

Inspect a Salesforce account

POST /external/v1/accounts/import/salesforce/inspect

Inspect one exact Salesforce source record through the application import service. The record contains only these seven fields when present: Id, Name, ParentId, Website, LastModifiedDate, PGID__c, and PGID_OID__c. Account-read scope does not expose arbitrary raw CRM properties.

Requirements

API scopes required:
accounts:read

Request

Request body (application/json)

integrationIdstring
Required

Salesforce connection in this workspace.

sourceRecordIdstring
Required

Exact Salesforce Account source record ID.

Responses

201
Success

Returns integrationId, sourceRecordId, and the seven-field record projection. No Account is changed.

Schema
SalesforceAccountInspection

Example Request

POST
/external/v1/accounts/import/salesforce/inspect
json
{
  "integrationId": "00000000-0000-4000-8000-000000000001",
  "sourceRecordId": "001AbCdEfGhIjKl"
}
POST

Apply reviewed Salesforce account imports

POST /external/v1/accounts/import/salesforce/apply

Apply reviewed Salesforce link, create, or skip actions through the same source-backed import service as the application. Every link or create action must explicitly provide selectedFields. An empty list links identity only; omitted selection is rejected. Returned created and updated Accounts use the public Account response shape, not raw CRM records.

Requirements

API scopes required:
accounts:write

Request

Request body (application/json)

integrationIdstring
Required

Salesforce connection in this workspace.

actionsobject[]
Required

1–500 {sourceRecordId, action, targetAccountId?, selectedFields} actions. action is link, create, or skip; link requires a target Account. selectedFields accepts up to 20 supported field keys and is required for link/create, including [] for identity only.

Responses

201
Applied

Returns created and updated public Accounts with skipped and failed action results.

Schema
CrmAccountImportResult
400
Invalid selection

A link/create action omitted selectedFields or the request shape is invalid.

Example Request

POST
/external/v1/accounts/import/salesforce/apply
json
{
  "integrationId": "00000000-0000-4000-8000-000000000001",
  "actions": [
    {
      "sourceRecordId": "001AbCdEfGhIjKl",
      "action": "link",
      "targetAccountId": "00000000-0000-4000-8000-000000000002",
      "selectedFields": []
    }
  ]
}
POST

Preview a Pendo account correction

POST /external/v1/accounts/import/pendo-account-links/correction/preview

Preview one exact app-scoped identity, target Salesforce proof, binding, all response attributions, and managed/local Signal links. Caps are 1,000 responses and 250 Signals per identity; exceeding either cap rejects the request instead of returning a partial correction. The API key creator must currently be an owner or admin of this workspace; a missing creator fails closed. Workspace and actor cannot be supplied in the body. The preview returns only receipt IDs, Account/Signal IDs, status and update time, plus Signal public IDs and control summaries. No raw provider response IDs, response content, personal response identities, Account mappings, or processing payloads are returned. Preview checks Signal processing status, not provider queue or Redis availability.

Requirements

API scopes required:
accounts:read

Request

Request body (application/json)

integrationIduuid
Required

A Pendo connection in this workspace.

appIdstring
Required

Exact Pendo app ID, not a product display name.

externalIdstring
Required

Exact imported app-scoped Account value, including its prefix.

targetAccountIduuid
Required

The reviewed target Account. A fuzzy candidate match is not required.

targetSalesforceSourceKeystring
Required

Exact salesforce:<org-id>:account source key that proves the target.

targetSalesforceExternalIdstring
Required

Salesforce Account ID, 15 or 18 characters. The first 15 characters retain case and must bind uniquely to the target.

reviewReferencestring
Required

Non-empty review reference, up to 512 characters. Do not include personal or confidential response data.

reviewedLegacyLinksobject[]

Up to 250 explicit {signalId, accountId, action} choices, with action adopt or remove. Unproven human links remain unless individually reviewed; a receipt alone does not prove ownership.

Responses

201
Preview

Returns identity, target, binding, responseHistory, signals, summary, caps, driftFingerprint, canApply, and blockers. No correction is applied.

Schema
PendoAccountCorrectionPreview
400
Invalid or too large

An exact identity/review is missing or a history cap is exceeded.

403
Forbidden

The key creator is not a current workspace owner/admin.

409
Conflict

The Salesforce target proof or Pendo source scope is not uniquely verified.

Example Request

POST
/external/v1/accounts/import/pendo-account-links/correction/preview
json
{
  "integrationId": "00000000-0000-4000-8000-000000000001",
  "appId": "example-app",
  "externalId": "cloud9_subdomain:example",
  "targetAccountId": "00000000-0000-4000-8000-000000000002",
  "targetSalesforceSourceKey": "salesforce:example-org:account",
  "targetSalesforceExternalId": "001AbCdEfGhIjKl",
  "reviewReference": "Example workbook/Main/42",
  "reviewedLegacyLinks": []
}
POST

Apply a reviewed Pendo account correction

POST /external/v1/accounts/import/pendo-account-links/correction/apply

Apply the exact reviewed identity and choices using the current-data driftFingerprint from preview. The service locks and rereads affected records, checks the processing queue, and rejects drift before writing. It journals before/after attribution, reconciles response and managed Signal contributions, preserves unreviewed human links, local exclusions, and other providers, and retains the correction on repeat imports. Generic unrestricted Pendo correction remains blocked. Caps remain 1,000 responses and 250 Signals. The API key creator must currently be an owner or admin of this workspace; a missing creator fails closed. Workspace and actor cannot be supplied in the body. Changed Signals use the existing processing service after commit; queue failure does not undo a committed correction and is reported as pending work. Raw journal snapshots are not returned.

Requirements

API scopes required:
accounts:write

Request

Request body (application/json)

integrationIduuid
Required

A Pendo connection in this workspace.

appIdstring
Required

Exact Pendo app ID, not a product display name.

externalIdstring
Required

Exact imported app-scoped Account value, including its prefix.

targetAccountIduuid
Required

The reviewed target Account. A fuzzy candidate match is not required.

targetSalesforceSourceKeystring
Required

Exact salesforce:<org-id>:account source key that proves the target.

targetSalesforceExternalIdstring
Required

Salesforce Account ID, 15 or 18 characters. The first 15 characters retain case and must bind uniquely to the target.

reviewReferencestring
Required

Non-empty review reference, up to 512 characters. Do not include personal or confidential response data.

reviewedLegacyLinksobject[]

Up to 250 explicit {signalId, accountId, action} choices, with action adopt or remove. Unproven human links remain unless individually reviewed; a receipt alone does not prove ownership.

driftFingerprintstring
Required

The 64-character lowercase hexadecimal fingerprint from a fresh preview of these exact review choices.

Responses

201
Applied

Returns correctionId, bindingId, responseCount, signalCount, processingSignalIds, queuedSignalIds, pendingSignalIds, and journalUpdatePending.

Schema
PendoAccountCorrectionResult
403
Forbidden

The key creator is not a current workspace owner/admin.

409
Conflict

History changed, processing is active, or source/control proof blocks correction. Preview again; no correction was saved.

Example Request

POST
/external/v1/accounts/import/pendo-account-links/correction/apply
json
{
  "integrationId": "00000000-0000-4000-8000-000000000001",
  "appId": "example-app",
  "externalId": "cloud9_subdomain:example",
  "targetAccountId": "00000000-0000-4000-8000-000000000002",
  "targetSalesforceSourceKey": "salesforce:example-org:account",
  "targetSalesforceExternalId": "001AbCdEfGhIjKl",
  "reviewReference": "Example workbook/Main/42",
  "reviewedLegacyLinks": [],
  "driftFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
POST

Retry Pendo correction processing

POST /external/v1/accounts/import/pendo-account-links/correction/retry-processing

Retry failed queue submissions or failed Signals from this correction's bounded processing journal. Successful Signals and Signals outside the journal are not reprocessed. This does not reapply Account attribution. The API key creator must currently be an owner or admin of this workspace; a missing creator fails closed. Workspace and actor cannot be supplied in the body.

Requirements

API scopes required:
accounts:write

Request

Request body (application/json)

correctionIduuid
Required

The committed Pendo correction UUID returned by apply.

Responses

201
Retried

Returns correctionId, queuedSignalIds, pendingSignalIds, and journalUpdatePending.

Schema
PendoAccountCorrectionProcessingResult
403
Forbidden

The key creator is no longer a workspace owner/admin.

404
Not found

The correction is not in this workspace.

Example Request

POST
/external/v1/accounts/import/pendo-account-links/correction/retry-processing
json
{
  "correctionId": "00000000-0000-4000-8000-000000000003"
}
GET

List account merge operations

GET /external/v1/accounts/:id/merge-operations

List the latest 50 merge operations for the surviving Account, newest first. Returns operation IDs, survivor/loser IDs, actor, source, status, and recovery timestamps. Raw before/after snapshots are never exposed. Uses the same recovery journal service as the application.

Requirements

API scopes required:
accounts:read

Request

Parameters

NameTypeDescription
id *uuidThe Account UUID in the API-key workspace.

Responses

200
Success

Returns public operation metadata.

Schema
Array<AccountMergeOperation>
POST

Preview account merge undo

POST /external/v1/accounts/merge-operations/:operationId/undo-preview

Review unchanged-only recovery of a journaled merge within its seven-day window. Returns canUndo and blockers, never raw snapshots. Changed data, expired windows, unsupported reference graphs, or unsupported schema changes block automatic undo; there is no force mode. A successful preview does not guarantee a later undo if data changes.

Requirements

API scopes required:
accounts:read

Request

Parameters

NameTypeDescription
operationId *uuidThe merge operation UUID in the API-key workspace.

Responses

201
Preview

Returns operationId, survivorId, loserIds, status, undoExpiresAt, mode, canUndo, and blockers.

Schema
AccountMergeUndoPreview
404
Not found

The merge operation is not in this workspace.

POST

Undo an unchanged account merge

POST /external/v1/accounts/merge-operations/:operationId/undo

Restore a supported journaled merge atomically, only within seven days and only if affected data and references remain unchanged. Both accounts:write AND accounts:delete are required. The service locks and checks current data again; unsupported graphs and drift block automatic undo. No force mode, partial recovery, or raw snapshot response is available.

Requirements

API scopes required:
accounts:write
accounts:delete

Request

Parameters

NameTypeDescription
operationId *uuidThe merge operation UUID in the API-key workspace.

Request body (application/json)

modestring
Required

Must be unchanged-only.

Responses

201
Restored

Returns operationId, status undone, survivorId, and restoredAccountIds.

Schema
AccountMergeUndoResult
409
Blocked

Recovery is expired, already used, changed, unsupported, or busy. No recovery changes were saved.

Example Request

POST
/external/v1/accounts/merge-operations/:operationId/undo
json
{
  "mode": "unchanged-only"
}
GET

List account contacts

GET /external/v1/accounts/:id/contacts

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

Requirements

API scopes required:
accounts:read

Request

Parameters

NameTypeDescription
id *uuidThe backend account UUID.

Responses

200
Success

Contacts were successfully retrieved.

Schema
Array<Contact>
404
Not Found

No account found with the provided id in the current workspace.

Example Request

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

Example Response

200 OK
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

Create an account contact

POST /external/v1/accounts/:id/contacts

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

Requirements

API scopes required:
accounts:write

Request

Parameters

NameTypeDescription
id *uuidThe backend account UUID.

Request body (application/json)

namestring
Required

Contact display name.

rolestring

Role, title, or account-specific responsibility.

emailstring

Email address.

phonestring

Phone number.

notesstring

Operator notes such as influence, interests, and outreach guidance.

isPrimaryboolean

Whether this is the primary contact for the account.

Responses

201
Created

The contact was successfully created.

Schema
Contact
404
Not Found

No account found with the provided id in the current workspace.

Example Request

POST
/external/v1/accounts/:id/contacts
json
{
  "name": "Primary product contact",
  "role": "VP Product",
  "email": "product-contact@example.com",
  "notes": "Primary product evaluator and champion.",
  "isPrimary": true
}

Example Response

201 Created
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

Update an account contact

PATCH /external/v1/accounts/:id/contacts/:contactId

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

Requirements

API scopes required:
accounts:write

Request

Parameters

NameTypeDescription
id *uuidThe backend account UUID.
contactId *uuidThe backend contact UUID.

Request body (application/json)

namestring

Updated contact display name.

rolestring

Updated role, title, or account-specific responsibility.

emailstring

Updated email address.

phonestring

Updated phone number.

notesstring

Updated operator notes.

isPrimaryboolean

Updated primary-contact flag.

Responses

200
Updated

The contact was successfully updated.

Schema
Contact
404
Not Found

No account/contact pair found in the current workspace.

Example Request

PATCH
/external/v1/accounts/:id/contacts/:contactId
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."}'
DELETE

Delete an account contact

DELETE /external/v1/accounts/:id/contacts/:contactId

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

Requirements

API scopes required:
accounts:write

Request

Parameters

NameTypeDescription
id *uuidThe backend account UUID.
contactId *uuidThe backend contact UUID.

Responses

200
Unassigned

The person is now unassigned and remains available in People.

404
Not Found

No account/contact pair found in the current workspace.

Example Request

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