Skip to content

Commit 194be75

Browse files
marcmlcclaude
andauthored
Add merge preview endpoint to Preview spec (#581)
* Add PreviewMergeContact to the Preview spec Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Reword merge preview description (drop em dash, clarify dry run) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Drop user_to_lead from merge preview reasons (u2u not officially supported) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Drop source_is_user from merge preview reasons (shadowed by invalid_merge_role_pair) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Make from and into required on merge_preview_request Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Split conversation reassignments into owned and participated in merge preview Mirror intercom/intercom#536857: the merge preview response now reports owned_conversations (primary-user ownership transfer) and participated_conversations (participation move) instead of a single conversations count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Report a single distinct conversations count in merge preview Follow intercom/intercom#536857: the merge preview reports one conversations reassignment count — the distinct number of conversations the merge reassigns, covering both conversations the contact owns and those it participates in (de-duped) — rather than separate owned/participated counts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0828c79 commit 194be75

1 file changed

Lines changed: 237 additions & 0 deletions

File tree

descriptions/0/api.intercom.io.yaml

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8235,6 +8235,108 @@ paths:
82358235
from: 6762f0d51bb69f9f2193bb7f
82368236
into: 6762f0d51bb69f9f2193bb80
82378237
skip_duplicate_validation: true
8238+
"/contacts/merge/preview":
8239+
post:
8240+
summary: Preview a contact merge
8241+
parameters:
8242+
- name: Intercom-Version
8243+
in: header
8244+
schema:
8245+
"$ref": "#/components/schemas/intercom_version"
8246+
tags:
8247+
- Contacts
8248+
operationId: PreviewMergeContact
8249+
description: |
8250+
Preview the result of merging one contact into another without applying the merge. This is a read-only dry run: it reports whether the merge is allowed and, if so, exactly what would change, **without modifying any data**.
8251+
8252+
Send the same `from` (a `lead`) and `into` (a `user`) contact IDs you would pass to [Merge a lead and a user](/docs/references/preview/rest-api/api.intercom.io/contacts/mergecontact).
8253+
8254+
- When `allowed` is `true`, the response contains a `summary`: every attribute change (with the resulting value on the surviving contact) and the number of conversations, notes, and tags that would be reassigned.
8255+
- When `allowed` is `false`, the response contains `reasons`. Each reason has a stable `code` you can branch on and a human-readable `message` you can display.
8256+
8257+
{% admonition type="warning" name="Previewing non-duplicate contacts" %}
8258+
By default a merge requires the two contacts to share an identifier (email, phone, or external ID). Set `skip_duplicate_validation` to `true` to preview a merge of contacts that don't — the same rule the merge endpoint enforces.
8259+
{% /admonition %}
8260+
8261+
When `allowed` is `false`, `reasons[].code` is one of:
8262+
8263+
| code | meaning |
8264+
| --- | --- |
8265+
| `invalid_merge_role_pair` | The `from`/`into` roles aren't mergeable. `from` must be a lead and `into` a user. |
8266+
| `no_shared_identifier` | The contacts share no email, phone, or external ID. Set `skip_duplicate_validation` to preview anyway. |
8267+
| `facebook_user` | Facebook contacts can't be merged. |
8268+
| `external_lead_channel_not_allowed` | The source is an external-channel lead that isn't eligible for merging. |
8269+
| `mailing_list` | Contacts created from a mailing list can't be merged. |
8270+
responses:
8271+
'200':
8272+
description: successful
8273+
content:
8274+
application/json:
8275+
examples:
8276+
allowed:
8277+
summary: Merge allowed
8278+
value:
8279+
allowed: true
8280+
summary:
8281+
attribute_changes:
8282+
attributes:
8283+
- field: name
8284+
outcome: adopted
8285+
from_value: Jo
8286+
new_into_value: Jo
8287+
- field: companies
8288+
outcome: merged
8289+
from_value:
8290+
- Beta
8291+
into_value:
8292+
- Acme
8293+
new_into_value:
8294+
- Acme
8295+
- Beta
8296+
- field: phone
8297+
outcome: discarded
8298+
from_value: "+1555"
8299+
into_value: "+1999"
8300+
new_into_value: "+1999"
8301+
total_count: 3
8302+
reassignments:
8303+
conversations: 14
8304+
tags: 3
8305+
notes: 2
8306+
blocked:
8307+
summary: Merge not allowed
8308+
value:
8309+
allowed: false
8310+
reasons:
8311+
- code: no_shared_identifier
8312+
message: These contacts don't share a common identifier (email, phone, or external ID), so they aren't recognised as duplicates. Set skip_duplicate_validation to true to preview the merge anyway.
8313+
schema:
8314+
"$ref": "#/components/schemas/merge_preview_response"
8315+
'401':
8316+
description: Unauthorized
8317+
content:
8318+
application/json:
8319+
examples:
8320+
Unauthorized:
8321+
value:
8322+
type: error.list
8323+
request_id: ff328c7c-6140-48eb-84dd-bb8960b66cd0
8324+
errors:
8325+
- code: unauthorized
8326+
message: Access Token Invalid
8327+
schema:
8328+
"$ref": "#/components/schemas/error"
8329+
requestBody:
8330+
content:
8331+
application/json:
8332+
schema:
8333+
"$ref": "#/components/schemas/merge_preview_request"
8334+
examples:
8335+
allowed:
8336+
summary: successful
8337+
value:
8338+
from: 6762f0d51bb69f9f2193bb7f
8339+
into: 6762f0d51bb69f9f2193bb80
82388340
"/contacts/search":
82398341
post:
82408342
summary: Search contacts
@@ -34936,6 +35038,141 @@ components:
3493635038
description: Set to `true` to merge two contacts that are not duplicates
3493735039
(they share no matching email or phone).
3493835040
example: true
35041+
merge_preview_request:
35042+
description: Preview a contact merge.
35043+
type: object
35044+
title: Merge preview request
35045+
required:
35046+
- from
35047+
- into
35048+
properties:
35049+
from:
35050+
type: string
35051+
description: The unique identifier for the contact to merge away from. Must be a lead.
35052+
example: 6762f0d51bb69f9f2193bb7f
35053+
into:
35054+
type: string
35055+
description: The unique identifier for the contact to merge into. Must be a user.
35056+
example: 6762f0d51bb69f9f2193bb80
35057+
skip_duplicate_validation:
35058+
type: boolean
35059+
default: false
35060+
description: When true, previews the merge even if the contacts don't share a common identifier. Defaults to false.
35061+
merge_preview_response:
35062+
title: Merge preview response
35063+
description: The result of a merge preview. Either the merge is allowed (with a summary of the changes) or it is not (with reasons).
35064+
oneOf:
35065+
- "$ref": "#/components/schemas/merge_preview_allowed"
35066+
- "$ref": "#/components/schemas/merge_preview_blocked"
35067+
merge_preview_allowed:
35068+
title: Merge preview allowed
35069+
type: object
35070+
description: Returned when the merge is allowed.
35071+
properties:
35072+
allowed:
35073+
type: boolean
35074+
enum:
35075+
- true
35076+
description: Always true. The merge is allowed.
35077+
summary:
35078+
"$ref": "#/components/schemas/merge_preview_summary"
35079+
merge_preview_blocked:
35080+
title: Merge preview not allowed
35081+
type: object
35082+
description: Returned when the merge is not allowed.
35083+
properties:
35084+
allowed:
35085+
type: boolean
35086+
enum:
35087+
- false
35088+
description: Always false. The merge is not allowed.
35089+
reasons:
35090+
type: array
35091+
description: One or more reasons the merge is not allowed.
35092+
items:
35093+
"$ref": "#/components/schemas/merge_preview_reason"
35094+
merge_preview_reason:
35095+
title: Merge preview reason
35096+
type: object
35097+
description: A reason a merge is not allowed.
35098+
properties:
35099+
code:
35100+
type: string
35101+
description: A stable code identifying the reason. Branch on this value.
35102+
enum:
35103+
- invalid_merge_role_pair
35104+
- no_shared_identifier
35105+
- facebook_user
35106+
- external_lead_channel_not_allowed
35107+
- mailing_list
35108+
message:
35109+
type: string
35110+
description: A human-readable explanation. Display this to people; do not match on it, as the wording may change.
35111+
merge_preview_summary:
35112+
title: Merge preview summary
35113+
type: object
35114+
description: A summary of the changes the merge would make to the surviving contact.
35115+
properties:
35116+
attribute_changes:
35117+
"$ref": "#/components/schemas/merge_preview_attribute_changes"
35118+
reassignments:
35119+
"$ref": "#/components/schemas/merge_preview_reassignments"
35120+
merge_preview_attribute_changes:
35121+
title: Merge preview attribute changes
35122+
type: object
35123+
description: The attribute changes the merge would make to the surviving contact.
35124+
properties:
35125+
attributes:
35126+
type: array
35127+
description: The individual attribute changes. Capped at 50 items; total_count reflects the full number of changes.
35128+
items:
35129+
"$ref": "#/components/schemas/merge_preview_attribute_change"
35130+
total_count:
35131+
type: integer
35132+
description: The total number of attribute changes detected, which may exceed the number of items in attributes.
35133+
example: 3
35134+
merge_preview_attribute_change:
35135+
title: Merge preview attribute change
35136+
type: object
35137+
description: A single attribute change on the surviving contact.
35138+
properties:
35139+
field:
35140+
type: string
35141+
description: The contact field that would change.
35142+
example: name
35143+
outcome:
35144+
type: string
35145+
description: How the value is resolved. adopted = the source's value is taken; discarded = the source's value is dropped and the target's kept; merged = the two values are combined.
35146+
enum:
35147+
- adopted
35148+
- discarded
35149+
- merged
35150+
from_value:
35151+
nullable: true
35152+
description: The source contact's value. Omitted when null. May be a string, array, or object depending on the field.
35153+
into_value:
35154+
nullable: true
35155+
description: The target contact's value. Omitted when null. May be a string, array, or object depending on the field.
35156+
new_into_value:
35157+
nullable: true
35158+
description: The resulting value on the surviving contact. Always present; may be null. May be a string, array, or object depending on the field.
35159+
merge_preview_reassignments:
35160+
title: Merge preview reassignments
35161+
type: object
35162+
description: The number of records that would be reassigned to the surviving contact, by type. Only types with reassignments are included.
35163+
properties:
35164+
conversations:
35165+
type: integer
35166+
description: The number of distinct conversations that would be reassigned to the surviving contact, counting both conversations the contact owns and conversations it participates in.
35167+
example: 14
35168+
tags:
35169+
type: integer
35170+
description: The number of tags that would be reassigned.
35171+
example: 3
35172+
notes:
35173+
type: integer
35174+
description: The number of notes that would be reassigned.
35175+
example: 2
3493935176
merge_conversations_request:
3494035177
title: Merge Conversations Request
3494135178
type: object

0 commit comments

Comments
 (0)