Merge plans and execution
Prepare and revise merge plans, inspect their effects, and execute an authorized revision.
The APIs on this page require alpha access for your organization. App availability and API access are separate. Check access and permissions.
List merge plans
GET /v1/deduplication/merge-plans
Lists merge plans, one page at a time.
Requires dedupe:read. The connection also needs access to the relevant tables, objects, or workflows.
MCP action: list_dedupe_merge_plans. Its argument names match the fields below; an HTTP header is passed using its argument name in MCP.
Parameters
| Name | Send in | Required | Type or allowed values | Notes |
|---|---|---|---|---|
object_type |
query | No | "companies", "people" |
|
status |
query | No | array of "preparing", "proposed", "authorized", "rejected", "invalidated", "expired", "retired", "executing", "finished", "failed" |
Repeat this query parameter for multiple values. |
created_at_from |
query | No | string (date-time) | |
created_at_before |
query | No | string (date-time) | |
sort |
query | No | "-created_at", "created_at" |
|
limit |
query | No | integer | |
cursor |
query | No | string |
Response
| HTTP status | Result |
|---|---|
200 |
The requested result. Asynchronous work returns an operation to poll. |
Required fields are required within their containing object. Notes identify fields that apply only to a particular response alternative.
| Response field | Type | Required in object | Notes |
|---|---|---|---|
data |
array of object | Yes | |
data[].object |
"deduplication_merge_plan" |
Yes | |
data[].id |
string | Yes | |
data[].version |
string | Yes | |
data[].revision |
string | Yes | |
data[].status |
"preparing", "proposed", "authorized", "rejected", "invalidated", "expired", "retired", "executing", "finished", "failed" |
Yes | |
data[].reason_code |
string or null | Yes | |
data[].connection_id |
string | Yes | |
data[].object_type |
"companies", "people" |
Yes | |
data[].effects_digest |
string or null | Yes | |
data[].preview_digest |
string or null | Yes | |
data[].recovery_coverage_digest |
string or null | Yes | |
data[].plan_expires_at |
string (date-time) or null | Yes | |
data[].limits |
object | Yes | |
data[].limits.max_groups |
integer | Yes | |
data[].limits.max_records_to_merge |
integer | Yes | |
data[].limits.max_paid_units |
0 |
Yes | |
data[].groups |
array of object | Yes | |
data[].groups[].set_id |
string | Yes | |
data[].groups[].set_version |
string | Yes | |
data[].groups[].keeper_record_id |
string | Yes | |
data[].groups[].merge_record_ids |
array of string | Yes | |
data[].safety |
object or null | Yes | |
data[].safety.groups |
integer | Yes | Alternative 1. |
data[].safety.records_to_merge |
integer | Yes | Alternative 1. |
data[].safety.fields_with_dropped_values |
integer | Yes | Alternative 1. |
data[].safety.fields_filled |
integer | Yes | Alternative 1. |
data[].safety.withheld |
boolean | Yes | Alternative 1. |
data[].recovery |
object or null | Yes | |
data[].recovery.categories |
array of object | Yes | Alternative 1. |
data[].recovery.categories[].category |
"list_entries", "comments", "notes", "files", "meetings", "call_recordings", "incoming_references" |
Yes | Alternative 1. |
data[].recovery.categories[].planned |
"capture", "not_granted" |
Yes | Alternative 1. |
data[].authorization_requirement |
object or null | Yes | |
data[].authorization_requirement.scope |
"organization_baseline" |
Yes | When data[].authorization_requirement.mode is "api_key_allowed", "not_allowed". |
data[].authorization_requirement.mode |
"api_key_allowed", "not_allowed" |
Yes | When data[].authorization_requirement.mode is "api_key_allowed", "not_allowed". |
data[].authorization_requirement.policy_version |
string | Yes | When data[].authorization_requirement.mode is "api_key_allowed", "not_allowed". |
data[].authorization_requirement.grant_evaluation |
"at_execution" |
Yes | When data[].authorization_requirement.mode is "api_key_allowed", "not_allowed". |
data[].authorization_requirement.reason_code |
string or null | Yes | When data[].authorization_requirement.mode is "api_key_allowed", "not_allowed". |
data[].authorization |
object | Yes | |
data[].authorization.state |
"unauthorized", "authorized", "rejected", "invalidated", "expired" |
Yes | |
data[].execution_id |
string or null | Yes | |
data[].client_reference |
string or null | Yes | |
data[].created_at |
string (date-time) | Yes | |
data[].prepared_at |
string (date-time) or null | Yes | |
pagination |
object | Yes | |
pagination.next_cursor |
string or null | Yes | |
references |
array of object | Yes | |
references[].provider |
"attio" |
Yes | |
references[].workspace_id |
string or null | Yes | |
references[].object_id |
string or null | Yes | |
references[].object_slug |
string or null | Yes | |
references[].records |
array of object | Yes | |
references[].records[].record_id |
string | Yes | |
references[].records[].web_url |
string (uri) or null | Yes | |
references[].records[].attributes |
array of object | Yes | |
references[].records[].attributes[].attribute_id |
string or null | Yes | |
references[].records[].attributes[].attribute_slug |
string or null | Yes | |
references[].records[].via |
array of object | Yes | |
references[].records[].via[].source_object_id |
string or null | Yes | |
references[].records[].via[].source_object_slug |
string or null | Yes | |
references[].records[].via[].source_record_id |
string | Yes | |
references[].records[].via[].attribute_id |
string or null | Yes | |
references[].records[].via[].attribute_slug |
string or null | Yes | |
references[].records[].via[].target_object_id |
string or null | Yes | |
references[].records[].via[].target_object_slug |
string or null | Yes | |
references[].records[].via[].target_record_id |
string | Yes | |
references[].lookup_hint |
object | Yes | |
references[].lookup_hint.resource |
"records" |
Yes | |
references[].lookup_hint.object |
string | Yes | |
references[].lookup_hint.record_ids |
array of string | Yes |
Errors
| HTTP status | Meaning |
|---|---|
400 |
One of: credential_in_url, invalid_parameter, unknown_parameter, invalid_request, invalid_cursor. |
401 |
One of: missing_credential, invalid_credential, credential_expired, credential_revoked. |
402 |
One of: payment_overdue. |
403 |
The current permissions, plan or policy do not allow this request. |
404 |
One of: not_found. |
409 |
The request conflicts with current resource state, authorization or an earlier request. Read the error code and current resource before retrying. |
410 |
The requested resource or retained recovery data has expired. |
412 |
One of: version_conflict. |
413 |
One of: request_too_large. |
422 |
A field, filter or selection cannot be used for this request. |
423 |
One of: organization_reopening. |
428 |
One of: customer_agreement_required. |
429 |
One of: rate_limited. |
503 |
One of: service_unavailable. |
See errors and recovery and safe retries before repeating a write.
Prepare merge plan
POST /v1/deduplication/merge-plans
Prepares a merge plan: for each duplicate set you choose, the record to keep and the records to merge into it. Preparing changes nothing in your CRM; poll the returned operation, read the plan, then execute it to merge. Backups follow your organization's setting. The limits you send cap the plan's size, and max_paid_units must be 0.
Requires dedupe:propose. The connection also needs access to the relevant tables, objects, or workflows.
MCP action: prepare_dedupe_merge_plan. Its argument names match the fields below; an HTTP header is passed using its argument name in MCP.
Parameters
| Name | Send in | Required | Type or allowed values | Notes |
|---|---|---|---|---|
connection_id |
JSON body | Yes | string | |
object_type |
JSON body | Yes | "companies", "people" |
|
sets |
JSON body | Yes | array of object | |
sets[].set_id |
Within the JSON value | Within this object | string | |
sets[].expected_version |
Within the JSON value | Within this object | string | |
sets[].keeper_record_id |
Within the JSON value | Within this object | string | |
sets[].merge_record_ids |
Within the JSON value | Within this object | array of string | |
limits |
JSON body | Yes | object | |
limits.max_groups |
Within the JSON value | Within this object | integer | |
limits.max_records_to_merge |
Within the JSON value | Within this object | integer | |
limits.max_paid_units |
Within the JSON value | No | 0 |
|
client_reference |
JSON body | No | string | |
request_key |
Header: Idempotency-Key |
Yes | string |
Response
| HTTP status | Result |
|---|---|
202 |
The requested result. Asynchronous work returns an operation to poll. |
Required fields are required within their containing object. Notes identify fields that apply only to a particular response alternative.
| Response field | Type | Required in object | Notes |
|---|---|---|---|
data |
object | Yes | |
data.id |
string | Yes | |
data.object |
"operation" |
Yes | |
data.version |
string | Yes | |
data.resource_id |
string | Yes | |
data.resource_type |
string | Yes | |
data.status |
"pending", "succeeded", "failed", "canceled", "needs_attention" |
Yes | |
data.terminal |
boolean | Yes | |
data.created_at |
string (date-time) | Yes | |
data.ended_at |
string (date-time) or null | Yes | |
data.reason_code |
string or null | Yes | |
data.replayed |
true |
No | |
data.operation_type |
"scan_cancel", "ai_assessment", "ai_assessment_cancel", "review_update", "exclusion_create", "exclusion_update", "scheduled_batch_create", "scheduled_batch_update", "scheduled_batch_cancel", "scheduled_run_cancel", "scheduled_record_cancel", "schedule_create", "schedule_update", "webhook_endpoint_create", "webhook_endpoint_update", "webhook_secret_rotate", "endpoint_verification", "webhook_replay", "merge_rebuild", "scheduled_batch_transfer", "webhook_endpoint_transfer" |
Yes | When data.operation_type is "scan_cancel", "ai_assessment", "ai_assessment_cancel", "review_update", "exclusion_create", "exclusion_update", "scheduled_batch_create", "scheduled_batch_update", "scheduled_batch_cancel", "scheduled_run_cancel", "scheduled_record_cancel", "schedule_create", "schedule_update", "webhook_endpoint_create", "webhook_endpoint_update", "webhook_secret_rotate", "endpoint_verification", "webhook_replay", "merge_rebuild", "scheduled_batch_transfer", "webhook_endpoint_transfer". |
data.action_metadata |
object | Yes | |
data.action_metadata.resource_version |
string | Yes | |
data.action_metadata.allowed_actions |
array of string | Yes | |
data.operation_type |
"merge_execute", "merge_cancel" |
Yes | When data.operation_type is "merge_execute", "merge_cancel". |
data.action_metadata.execute_not_before |
string (date-time) | Yes | When data.operation_type is "merge_execute", "merge_cancel". |
data.action_metadata.authorization_expires_at |
string (date-time) | Yes | When data.operation_type is "merge_execute", "merge_cancel". |
data.operation_type |
"plan_prepare", "plan_revision_prepare", "retry_plan_prepare" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.plan_revision |
string | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.effects_digest |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.preview_digest |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.recovery_coverage_digest |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement |
object | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.scope |
"organization_baseline" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.mode |
"api_key_allowed", "not_allowed" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.policy_version |
string | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.grant_evaluation |
"at_execution" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.reason_code |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_state |
"unauthorized", "authorized", "rejected", "invalidated", "expired" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.operation_type |
"review_bulk_update", "exclusion_bulk_create" |
Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results |
array of object | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].index |
integer | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].outcome |
"succeeded", "failed" |
Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].resource_ref |
object | No | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].resource_ref.id |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].resource_ref.version |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error |
object | No | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error.code |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error.message |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error.param |
string | No | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.operation_type |
"scan", "scan_continue" |
Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child |
object | Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.state |
"not_requested", "pending_admission", "admitted", "skipped", "failed" |
Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.reason_code |
string or null | Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.ai_run_id |
string | No | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.ai_operation_id |
string | No | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.operation_url |
string (uri) | No | When data.operation_type is "scan", "scan_continue". |
Accepted work can continue after the request returns. Follow its returned status or operation until it finishes; accepting work does not mean every item succeeded. See operation status.
Errors
| HTTP status | Meaning |
|---|---|
400 |
One of: credential_in_url, invalid_parameter, unknown_parameter, invalid_request. |
401 |
One of: missing_credential, invalid_credential, credential_expired, credential_revoked. |
402 |
One of: payment_overdue. |
403 |
The current permissions, plan or policy do not allow this request. |
404 |
One of: not_found. |
409 |
The request conflicts with current resource state, authorization or an earlier request. Read the error code and current resource before retrying. |
410 |
The requested resource or retained recovery data has expired. |
412 |
One of: version_conflict. |
413 |
One of: request_too_large. |
422 |
A field, filter or selection cannot be used for this request. |
423 |
One of: organization_reopening. |
428 |
One of: customer_agreement_required. |
429 |
One of: rate_limited. |
503 |
One of: service_unavailable. |
See errors and recovery and safe retries before repeating a write.
Get merge plan
GET /v1/deduplication/merge-plans/{plan_id}
Gets a saved merge plan: the records it would merge and how they will be backed up. Executing it needs merge permission at that time and must fit your organization's merge limits.
Requires dedupe:read. The connection also needs access to the relevant tables, objects, or workflows.
MCP action: get_dedupe_merge_plan. Its argument names match the fields below; an HTTP header is passed using its argument name in MCP.
Parameters
| Name | Send in | Required | Type or allowed values | Notes |
|---|---|---|---|---|
plan_id |
path | Yes | string |
Response
| HTTP status | Result |
|---|---|
200 |
The requested result. Asynchronous work returns an operation to poll. |
Required fields are required within their containing object. Notes identify fields that apply only to a particular response alternative.
| Response field | Type | Required in object | Notes |
|---|---|---|---|
data |
object | Yes | |
data.object |
"deduplication_merge_plan" |
Yes | |
data.id |
string | Yes | |
data.version |
string | Yes | |
data.revision |
string | Yes | |
data.status |
"preparing", "proposed", "authorized", "rejected", "invalidated", "expired", "retired", "executing", "finished", "failed" |
Yes | |
data.reason_code |
string or null | Yes | |
data.connection_id |
string | Yes | |
data.object_type |
"companies", "people" |
Yes | |
data.effects_digest |
string or null | Yes | |
data.preview_digest |
string or null | Yes | |
data.recovery_coverage_digest |
string or null | Yes | |
data.plan_expires_at |
string (date-time) or null | Yes | |
data.limits |
object | Yes | |
data.limits.max_groups |
integer | Yes | |
data.limits.max_records_to_merge |
integer | Yes | |
data.limits.max_paid_units |
0 |
Yes | |
data.groups |
array of object | Yes | |
data.groups[].set_id |
string | Yes | |
data.groups[].set_version |
string | Yes | |
data.groups[].keeper_record_id |
string | Yes | |
data.groups[].merge_record_ids |
array of string | Yes | |
data.safety |
object or null | Yes | |
data.safety.groups |
integer | Yes | Alternative 1. |
data.safety.records_to_merge |
integer | Yes | Alternative 1. |
data.safety.fields_with_dropped_values |
integer | Yes | Alternative 1. |
data.safety.fields_filled |
integer | Yes | Alternative 1. |
data.safety.withheld |
boolean | Yes | Alternative 1. |
data.recovery |
object or null | Yes | |
data.recovery.categories |
array of object | Yes | Alternative 1. |
data.recovery.categories[].category |
"list_entries", "comments", "notes", "files", "meetings", "call_recordings", "incoming_references" |
Yes | Alternative 1. |
data.recovery.categories[].planned |
"capture", "not_granted" |
Yes | Alternative 1. |
data.authorization_requirement |
object or null | Yes | |
data.authorization_requirement.scope |
"organization_baseline" |
Yes | When data.authorization_requirement.mode is "api_key_allowed", "not_allowed". |
data.authorization_requirement.mode |
"api_key_allowed", "not_allowed" |
Yes | When data.authorization_requirement.mode is "api_key_allowed", "not_allowed". |
data.authorization_requirement.policy_version |
string | Yes | When data.authorization_requirement.mode is "api_key_allowed", "not_allowed". |
data.authorization_requirement.grant_evaluation |
"at_execution" |
Yes | When data.authorization_requirement.mode is "api_key_allowed", "not_allowed". |
data.authorization_requirement.reason_code |
string or null | Yes | When data.authorization_requirement.mode is "api_key_allowed", "not_allowed". |
data.authorization |
object | Yes | |
data.authorization.state |
"unauthorized", "authorized", "rejected", "invalidated", "expired" |
Yes | |
data.execution_id |
string or null | Yes | |
data.client_reference |
string or null | Yes | |
data.created_at |
string (date-time) | Yes | |
data.prepared_at |
string (date-time) or null | Yes | |
references |
array of object | Yes | |
references[].provider |
"attio" |
Yes | |
references[].workspace_id |
string or null | Yes | |
references[].object_id |
string or null | Yes | |
references[].object_slug |
string or null | Yes | |
references[].records |
array of object | Yes | |
references[].records[].record_id |
string | Yes | |
references[].records[].web_url |
string (uri) or null | Yes | |
references[].records[].attributes |
array of object | Yes | |
references[].records[].attributes[].attribute_id |
string or null | Yes | |
references[].records[].attributes[].attribute_slug |
string or null | Yes | |
references[].records[].via |
array of object | Yes | |
references[].records[].via[].source_object_id |
string or null | Yes | |
references[].records[].via[].source_object_slug |
string or null | Yes | |
references[].records[].via[].source_record_id |
string | Yes | |
references[].records[].via[].attribute_id |
string or null | Yes | |
references[].records[].via[].attribute_slug |
string or null | Yes | |
references[].records[].via[].target_object_id |
string or null | Yes | |
references[].records[].via[].target_object_slug |
string or null | Yes | |
references[].records[].via[].target_record_id |
string | Yes | |
references[].lookup_hint |
object | Yes | |
references[].lookup_hint.resource |
"records" |
Yes | |
references[].lookup_hint.object |
string | Yes | |
references[].lookup_hint.record_ids |
array of string | Yes |
Errors
| HTTP status | Meaning |
|---|---|
400 |
One of: credential_in_url, invalid_parameter, unknown_parameter. |
401 |
One of: missing_credential, invalid_credential, credential_expired, credential_revoked. |
402 |
One of: payment_overdue. |
403 |
The current permissions, plan or policy do not allow this request. |
404 |
One of: not_found. |
409 |
The request conflicts with current resource state, authorization or an earlier request. Read the error code and current resource before retrying. |
410 |
The requested resource or retained recovery data has expired. |
412 |
One of: version_conflict. |
413 |
One of: request_too_large. |
422 |
A field, filter or selection cannot be used for this request. |
423 |
One of: organization_reopening. |
428 |
One of: customer_agreement_required. |
429 |
One of: rate_limited. |
503 |
One of: service_unavailable. |
See errors and recovery and safe retries before repeating a write.
Revise merge plan
POST /v1/deduplication/merge-plans/{plan_id}/revisions
Replaces the whole selection of a merge plan this key prepared and has not executed, and prepares it again; poll the returned operation. What this replaces is not kept, so it can't be undone.
Requires dedupe:propose. The connection also needs access to the relevant tables, objects, or workflows.
MCP action: revise_dedupe_merge_plan. Its argument names match the fields below; an HTTP header is passed using its argument name in MCP.
Parameters
| Name | Send in | Required | Type or allowed values | Notes |
|---|---|---|---|---|
plan_id |
path | Yes | string | |
expected_version |
JSON body | Yes | string | |
replacement |
JSON body | Yes | object | |
replacement.sets |
Within the JSON value | Within this object | array of object | |
replacement.sets[].set_id |
Within the JSON value | Within this object | string | |
replacement.sets[].expected_version |
Within the JSON value | Within this object | string | |
replacement.sets[].keeper_record_id |
Within the JSON value | Within this object | string | |
replacement.sets[].merge_record_ids |
Within the JSON value | Within this object | array of string | |
replacement.limits |
Within the JSON value | Within this object | object | |
replacement.limits.max_groups |
Within the JSON value | Within this object | integer | |
replacement.limits.max_records_to_merge |
Within the JSON value | Within this object | integer | |
replacement.limits.max_paid_units |
Within the JSON value | No | 0 |
|
client_reference |
JSON body | No | string | |
request_key |
Header: Idempotency-Key |
Yes | string |
Response
| HTTP status | Result |
|---|---|
202 |
The requested result. Asynchronous work returns an operation to poll. |
Required fields are required within their containing object. Notes identify fields that apply only to a particular response alternative.
| Response field | Type | Required in object | Notes |
|---|---|---|---|
data |
object | Yes | |
data.id |
string | Yes | |
data.object |
"operation" |
Yes | |
data.version |
string | Yes | |
data.resource_id |
string | Yes | |
data.resource_type |
string | Yes | |
data.status |
"pending", "succeeded", "failed", "canceled", "needs_attention" |
Yes | |
data.terminal |
boolean | Yes | |
data.created_at |
string (date-time) | Yes | |
data.ended_at |
string (date-time) or null | Yes | |
data.reason_code |
string or null | Yes | |
data.replayed |
true |
No | |
data.operation_type |
"scan_cancel", "ai_assessment", "ai_assessment_cancel", "review_update", "exclusion_create", "exclusion_update", "scheduled_batch_create", "scheduled_batch_update", "scheduled_batch_cancel", "scheduled_run_cancel", "scheduled_record_cancel", "schedule_create", "schedule_update", "webhook_endpoint_create", "webhook_endpoint_update", "webhook_secret_rotate", "endpoint_verification", "webhook_replay", "merge_rebuild", "scheduled_batch_transfer", "webhook_endpoint_transfer" |
Yes | When data.operation_type is "scan_cancel", "ai_assessment", "ai_assessment_cancel", "review_update", "exclusion_create", "exclusion_update", "scheduled_batch_create", "scheduled_batch_update", "scheduled_batch_cancel", "scheduled_run_cancel", "scheduled_record_cancel", "schedule_create", "schedule_update", "webhook_endpoint_create", "webhook_endpoint_update", "webhook_secret_rotate", "endpoint_verification", "webhook_replay", "merge_rebuild", "scheduled_batch_transfer", "webhook_endpoint_transfer". |
data.action_metadata |
object | Yes | |
data.action_metadata.resource_version |
string | Yes | |
data.action_metadata.allowed_actions |
array of string | Yes | |
data.operation_type |
"merge_execute", "merge_cancel" |
Yes | When data.operation_type is "merge_execute", "merge_cancel". |
data.action_metadata.execute_not_before |
string (date-time) | Yes | When data.operation_type is "merge_execute", "merge_cancel". |
data.action_metadata.authorization_expires_at |
string (date-time) | Yes | When data.operation_type is "merge_execute", "merge_cancel". |
data.operation_type |
"plan_prepare", "plan_revision_prepare", "retry_plan_prepare" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.plan_revision |
string | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.effects_digest |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.preview_digest |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.recovery_coverage_digest |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement |
object | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.scope |
"organization_baseline" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.mode |
"api_key_allowed", "not_allowed" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.policy_version |
string | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.grant_evaluation |
"at_execution" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.reason_code |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_state |
"unauthorized", "authorized", "rejected", "invalidated", "expired" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.operation_type |
"review_bulk_update", "exclusion_bulk_create" |
Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results |
array of object | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].index |
integer | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].outcome |
"succeeded", "failed" |
Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].resource_ref |
object | No | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].resource_ref.id |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].resource_ref.version |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error |
object | No | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error.code |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error.message |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error.param |
string | No | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.operation_type |
"scan", "scan_continue" |
Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child |
object | Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.state |
"not_requested", "pending_admission", "admitted", "skipped", "failed" |
Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.reason_code |
string or null | Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.ai_run_id |
string | No | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.ai_operation_id |
string | No | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.operation_url |
string (uri) | No | When data.operation_type is "scan", "scan_continue". |
Accepted work can continue after the request returns. Follow its returned status or operation until it finishes; accepting work does not mean every item succeeded. See operation status.
Errors
| HTTP status | Meaning |
|---|---|
400 |
One of: credential_in_url, invalid_parameter, unknown_parameter, invalid_request. |
401 |
One of: missing_credential, invalid_credential, credential_expired, credential_revoked. |
402 |
One of: payment_overdue. |
403 |
The current permissions, plan or policy do not allow this request. |
404 |
One of: not_found. |
409 |
The request conflicts with current resource state, authorization or an earlier request. Read the error code and current resource before retrying. |
410 |
The requested resource or retained recovery data has expired. |
412 |
One of: version_conflict. |
413 |
One of: request_too_large. |
422 |
A field, filter or selection cannot be used for this request. |
423 |
One of: organization_reopening. |
428 |
One of: customer_agreement_required. |
429 |
One of: rate_limited. |
503 |
One of: service_unavailable. |
See errors and recovery and safe retries before repeating a write.
Execute merge plan
POST /v1/deduplication/merge-plans/{plan_id}/executions
Merges the records in a saved merge plan, exactly as it was prepared. Needs this key's whole-record merge permission and must fit your organization's merge limits. Your organization's Duplicate Detection settings must allow merges through the API or an AI app; otherwise a person runs the plan in Neon Deer. Backups follow your organization's setting. Attio has no native unmerge; when Neon Deer saves a pre-merge backup, its recovery operations can unmerge by reconstructing records with new IDs and restoring supported data. This does not restore every detail exactly. Access and record values are checked again before each merge. Poll the operation until it finishes; a webhook only signals a change, so read the current state.
Requires dedupe:merge. The connection also needs access to the relevant tables, objects, or workflows.
MCP action: execute_dedupe_merge_plan. Its argument names match the fields below; an HTTP header is passed using its argument name in MCP.
Parameters
| Name | Send in | Required | Type or allowed values | Notes |
|---|---|---|---|---|
plan_id |
path | Yes | string | |
expected_version |
JSON body | Yes | string | |
revision |
JSON body | Yes | string | |
effects_digest |
JSON body | Yes | string | |
preview_digest |
JSON body | Yes | string | |
recovery_coverage_digest |
JSON body | Yes | string | |
authorization_policy_version |
JSON body | Yes | string | |
expected_grant_version |
JSON body | Yes | string | |
max_records_to_merge |
JSON body | Yes | integer | |
request_key |
Header: Idempotency-Key |
Yes | string |
Response
| HTTP status | Result |
|---|---|
202 |
The requested result. Asynchronous work returns an operation to poll. |
Required fields are required within their containing object. Notes identify fields that apply only to a particular response alternative.
| Response field | Type | Required in object | Notes |
|---|---|---|---|
data |
object | Yes | |
data.id |
string | Yes | |
data.object |
"operation" |
Yes | |
data.version |
string | Yes | |
data.resource_id |
string | Yes | |
data.resource_type |
string | Yes | |
data.status |
"pending", "succeeded", "failed", "canceled", "needs_attention" |
Yes | |
data.terminal |
boolean | Yes | |
data.created_at |
string (date-time) | Yes | |
data.ended_at |
string (date-time) or null | Yes | |
data.reason_code |
string or null | Yes | |
data.replayed |
true |
No | |
data.operation_type |
"scan_cancel", "ai_assessment", "ai_assessment_cancel", "review_update", "exclusion_create", "exclusion_update", "scheduled_batch_create", "scheduled_batch_update", "scheduled_batch_cancel", "scheduled_run_cancel", "scheduled_record_cancel", "schedule_create", "schedule_update", "webhook_endpoint_create", "webhook_endpoint_update", "webhook_secret_rotate", "endpoint_verification", "webhook_replay", "merge_rebuild", "scheduled_batch_transfer", "webhook_endpoint_transfer" |
Yes | When data.operation_type is "scan_cancel", "ai_assessment", "ai_assessment_cancel", "review_update", "exclusion_create", "exclusion_update", "scheduled_batch_create", "scheduled_batch_update", "scheduled_batch_cancel", "scheduled_run_cancel", "scheduled_record_cancel", "schedule_create", "schedule_update", "webhook_endpoint_create", "webhook_endpoint_update", "webhook_secret_rotate", "endpoint_verification", "webhook_replay", "merge_rebuild", "scheduled_batch_transfer", "webhook_endpoint_transfer". |
data.action_metadata |
object | Yes | |
data.action_metadata.resource_version |
string | Yes | |
data.action_metadata.allowed_actions |
array of string | Yes | |
data.operation_type |
"merge_execute", "merge_cancel" |
Yes | When data.operation_type is "merge_execute", "merge_cancel". |
data.action_metadata.execute_not_before |
string (date-time) | Yes | When data.operation_type is "merge_execute", "merge_cancel". |
data.action_metadata.authorization_expires_at |
string (date-time) | Yes | When data.operation_type is "merge_execute", "merge_cancel". |
data.operation_type |
"plan_prepare", "plan_revision_prepare", "retry_plan_prepare" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.plan_revision |
string | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.effects_digest |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.preview_digest |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.recovery_coverage_digest |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement |
object | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.scope |
"organization_baseline" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.mode |
"api_key_allowed", "not_allowed" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.policy_version |
string | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.grant_evaluation |
"at_execution" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_requirement.reason_code |
string or null | Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.action_metadata.authorization_state |
"unauthorized", "authorized", "rejected", "invalidated", "expired" |
Yes | When data.operation_type is "plan_prepare", "plan_revision_prepare", "retry_plan_prepare". |
data.operation_type |
"review_bulk_update", "exclusion_bulk_create" |
Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results |
array of object | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].index |
integer | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].outcome |
"succeeded", "failed" |
Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].resource_ref |
object | No | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].resource_ref.id |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].resource_ref.version |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error |
object | No | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error.code |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error.message |
string | Yes | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.action_metadata.results[].error.param |
string | No | When data.operation_type is "review_bulk_update", "exclusion_bulk_create". |
data.operation_type |
"scan", "scan_continue" |
Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child |
object | Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.state |
"not_requested", "pending_admission", "admitted", "skipped", "failed" |
Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.reason_code |
string or null | Yes | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.ai_run_id |
string | No | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.ai_operation_id |
string | No | When data.operation_type is "scan", "scan_continue". |
data.action_metadata.ai_child.operation_url |
string (uri) | No | When data.operation_type is "scan", "scan_continue". |
Accepted work can continue after the request returns. Follow its returned status or operation until it finishes; accepting work does not mean every item succeeded. See operation status.
Errors
| HTTP status | Meaning |
|---|---|
400 |
One of: credential_in_url, invalid_parameter, unknown_parameter, invalid_request. |
401 |
One of: missing_credential, invalid_credential, credential_expired, credential_revoked. |
402 |
One of: payment_overdue. |
403 |
The current permissions, plan or policy do not allow this request. |
404 |
One of: not_found. |
409 |
The request conflicts with current resource state, authorization or an earlier request. Read the error code and current resource before retrying. |
410 |
The requested resource or retained recovery data has expired. |
412 |
One of: version_conflict. |
413 |
One of: request_too_large. |
422 |
A field, filter or selection cannot be used for this request. |
423 |
One of: organization_reopening. |
428 |
One of: customer_agreement_required. |
429 |
One of: rate_limited. |
503 |
One of: service_unavailable. |
See errors and recovery and safe retries before repeating a write.
Delete merge plan
DELETE /v1/deduplication/merge-plans/{plan_id}
Deletes a merge plan this key prepared and has not executed. No CRM records are deleted. This can't be undone.
Requires dedupe:propose. The connection also needs access to the relevant tables, objects, or workflows.
MCP action: delete_dedupe_merge_plan. Its argument names match the fields below; an HTTP header is passed using its argument name in MCP.
Parameters
| Name | Send in | Required | Type or allowed values | Notes |
|---|---|---|---|---|
plan_id |
path | Yes | string | |
expected_version |
query | Yes | string |
Response
| HTTP status | Result |
|---|---|
204 |
The resource was removed. No response body. |
Errors
| HTTP status | Meaning |
|---|---|
400 |
One of: credential_in_url, invalid_parameter, unknown_parameter, invalid_request, invalid_cursor. |
401 |
One of: missing_credential, invalid_credential, credential_expired, credential_revoked. |
402 |
One of: payment_overdue. |
403 |
The current permissions, plan or policy do not allow this request. |
404 |
One of: not_found. |
409 |
The request conflicts with current resource state, authorization or an earlier request. Read the error code and current resource before retrying. |
410 |
The requested resource or retained recovery data has expired. |
412 |
One of: version_conflict. |
413 |
One of: request_too_large. |
422 |
A field, filter or selection cannot be used for this request. |
423 |
One of: organization_reopening. |
428 |
One of: customer_agreement_required. |
429 |
One of: rate_limited. |
503 |
One of: service_unavailable. |
See errors and recovery and safe retries before repeating a write.
The public OpenAPI document lists generally available API families. Alpha operations may not appear in that document; their request and response fields are listed on this page. Use the API Playground to inspect a real response with your connection's access.