Troubleshooting: merge refused with WEAK_IDENTITY_MATCH_REQUIRES_CONFIRMATION
A422 with code WEAK_IDENTITY_MATCH_REQUIRES_CONFIRMATION means POST /api/v1/conversations/:id/merge looked at the rows you asked it to fold and found a phone number or an email that matches — but also found contact_id values that disagree with each other. The merge is refused rather than risk folding two different customers into one thread.
This is a confirmation gate, not a hard block: re-submit the request with confirm_weak_identity=true to proceed.
What a merge does
POST /api/v1/conversations/:id/merge folds one or more sibling conversation rows into the target (primary) conversation:
- Each source row is archived (status
archived, not deleted) withmetadata.merged_intopointing at the primary, so deep links and audit trails still resolve. - The primary’s
channels[]is unioned with every source’s channels, so the existing thread query loads messages from all of them. - A pre-merge snapshot is stamped on each source, which powers
POST /api/v1/conversations/:id/unmerge— you can reverse a merge within 30 minutes (see Undo a recent conversation merge).
Identity signals, from strongest to weakest
The merge gate classifies each primary/source pair by which identifier lines up:
The refusal exists for the collisions a phone-or-email-only match cannot rule out: a shared family email, a shared office or call-center number, a recycled phone number now registered to a new person. In all of those, the weak identifier overlaps while the rows resolve to different contacts — folding them merges two customers’ histories into one thread.
When it fires, the response tells you which rows and which identifier collided in
error.details:
match_type is phone or email — check it first. A VALIDATION_ERROR instead of this code means the rows share nothing at all; no confirmation flag will help there.
Confirm and retry
Once you have verified the two conversations really do belong to the same customer, send the same request with the confirmation flag:contact_id match then passes without the flag.
If you confirmed a merge and it folded rows it should not have, POST /api/v1/conversations/:id/unmerge restores every source from its pre-merge snapshot within 30 minutes. After the window, support can do a manual revert.
Where matching rules live
Conversation merging reuses the identifier layers from contact identity resolution — normalized email, E.164 phone,external_id. The full guide covers merge candidates, match reviews, the rule simulator, and survivorship policy: Identity resolution. Conversation rows inherit the contacts those rules produce, so resolving duplicate contacts upstream makes conversation merges pass on the strong tier.
Send support
When you open a ticket for a merge question, include:- The
request_idfrommeta.request_idon the failed (or confirmed) response - Both conversation ids — the primary from the path and every id in
source_conversation_ids - The identifier pair that overlapped:
match_typefromerror.detailsplus the phone or email value, and the twocontact_idvalues fromdetails
A weak-identity refusal is the merge doing its job. Confirm only after you have checked the two threads belong to one customer — unmerge past the 30-minute window needs a manual support revert.