Unmerge records
Unmerge Attio records from a saved backup. Neon Deer reconstructs records with new IDs and restores supported values, references, and captured history.
The APIs on this page require alpha access for your organization. App availability and API access are separate. Check access and permissions.
Get recovery plan
GET /v1/deduplication/executions/{execution_id}/items/{item_id}/recovery-plan
Gets what rebuilding one merged item from its backup would do: how many records it creates again, how many values and references it writes, where the kept record now differs from its backup and which of those differences the merge made, and what it cannot rebuild. Rebuilt records get new IDs in Attio. Record values are never returned. Only a merge made while your organization's backups were on can be rebuilt; otherwise available is false with reason_code backup_off. Send its revision when you rebuild.
Requires dedupe:recovery:read. The connection also needs access to the relevant tables, objects, or workflows.
MCP action: get_dedupe_recovery_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 |
|---|---|---|---|---|
execution_id |
path | Yes | string | |
item_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_recovery_plan" |
Yes | |
data.execution_id |
string | Yes | |
data.item_id |
string | Yes | |
data.object_type |
"companies", "people" |
Yes | |
data.available |
boolean | Yes | |
data.reason_code |
"merge_not_complete", "merged_again", "lineage_not_supported", "backup_off", "backup_expired", "backup_unreadable", "merged_record_unreadable", "connection_unavailable" or null |
Yes | |
data.revision |
string or null | Yes | |
data.merged_record_id |
string or null | Yes | |
data.kept_record_id |
string or null | Yes | |
data.rebuilt_record_ids |
array of string | Yes | |
data.records_to_create |
integer | Yes | |
data.values_to_write |
integer | Yes | |
data.references_to_repoint |
integer | Yes | |
data.kept_differences |
array of object | Yes | |
data.kept_differences[].attribute |
string | Yes | |
data.kept_differences[].unique |
boolean | Yes | |
data.kept_differences[].choice |
"keep_current", "undo_merge", "replace_with_saved" |
Yes | |
data.not_rebuilt |
array of object | Yes | |
data.not_rebuilt[].kind |
"attribute_not_writable", "attribute_type_not_supported", "attribute_history_not_restored", "attribute_history_not_kept_by_attio", "unique_value_kept_on_merged_record", "list_entries", "list_entry_references", "notes", "comment_threads", "files", "meetings", "call_recordings", "referrer_unreadable", "incoming_references_not_saved", "original_record_ids" |
Yes | |
data.not_rebuilt[].attribute |
string or null | Yes | |
data.not_rebuilt[].count |
integer or null | Yes | |
data.recovery |
object or null | Yes | |
data.recovery.id |
string | Yes | Alternative 1. |
data.recovery.state |
"queued", "running", "completed", "partial", "interrupted", "stopped" |
Yes | Alternative 1. |
data.recovery.reason_code |
string or null | Yes | Alternative 1. |
data.recovery.created_record_ids |
array of string | Yes | Alternative 1. |
data.recovery.unconfirmed_record_ids |
array of string | Yes | Alternative 1. |
data.recovery.history_links_dropped |
integer | Yes | Alternative 1. |
data.recovery.ended_at |
string (date-time) or null | Yes | Alternative 1. |
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.
Rebuild merged records
POST /v1/deduplication/executions/{execution_id}/items/{item_id}/recoveries
Unmerges records by reconstructing them from a saved pre-merge backup, exactly as the recovery plan describes; send its revision as expected_revision. Attio has no native unmerge. Rebuilt records get new IDs, and supported references from other records are pointed at them. The values the merge added to the kept record are taken off it, each listed in kept_differences with choice undo_merge; anything changed in Attio since the merge stays, unless it set a value a merged-away record had. List entries, tasks, notes, comments, files, meetings, and call recordings stay on the merged record, and nothing is deleted. Your organization's merge settings must allow merges through the API or an AI app. Poll the returned operation until it finishes.
Requires dedupe:recovery:execute. The connection also needs access to the relevant tables, objects, or workflows.
MCP action: start_dedupe_merge_rebuild. 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 |
|---|---|---|---|---|
execution_id |
path | Yes | string | |
item_id |
path | Yes | string | |
expected_revision |
JSON body | Yes | 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.
Answer for an unconfirmed record
POST /v1/deduplication/executions/{execution_id}/items/{item_id}/recoveries/unconfirmed
A rebuild stops when Attio received a create but its answer was lost, and the recovery plan lists that saved record in unconfirmed_record_ids. Check Attio, then answer for it: outcome created with the record_id of the record it made, which must exist in the merge's Attio workspace, or outcome not_created to create it again. Before you answer, tell the person in these words: If Attio did create this record and you say it wasn't created, you'll have two of it in Attio. Check Attio first. Then ask them which it is, and send only the answer they give. The rebuild then carries on. Nothing is decided for you. Your organization's merge settings must allow merges through the API or an AI app. Poll the returned operation until it finishes.
Requires dedupe:recovery:execute. The connection also needs access to the relevant tables, objects, or workflows.
MCP action: continue_dedupe_merge_rebuild. 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 |
|---|---|---|---|---|
execution_id |
path | Yes | string | |
item_id |
path | Yes | string | |
original_record_id |
JSON body | Yes | string (uuid) | |
outcome |
JSON body | Yes | "created", "not_created" |
|
record_id |
JSON body | No | string (uuid) | |
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.
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.