> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting: merge refused with WEAK_IDENTITY_MATCH_REQUIRES_CONFIRMATION

> Resolve a 422 from POST /conversations/:id/merge — the endpoint refuses to fold two conversations when the only overlapping signal is a phone number or email and the rows point at different contacts. Confirm the match explicitly to proceed.

# Troubleshooting: merge refused with WEAK\_IDENTITY\_MATCH\_REQUIRES\_CONFIRMATION

A `422` 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) with `metadata.merged_into` pointing 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](/api-reference/endpoints/conversations#undo-a-recent-conversation-merge)).

## Identity signals, from strongest to weakest

The merge gate classifies each primary/source pair by which identifier lines up:

| Tier          | Signal                                                                                                                  | Result                                                                     |
| ------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| Strong        | Same `contact_id` on both rows                                                                                          | Merge proceeds                                                             |
| Weak          | Same phone or same email, and `contact_id` agrees or is empty on both sides                                             | Merge proceeds                                                             |
| Weak-conflict | Same phone or same email, **but** `contact_id` contradicts — two different ids, or one row has an id and the other none | `422 WEAK_IDENTITY_MATCH_REQUIRES_CONFIRMATION` unless confirmed           |
| None          | No shared `contact_id`, phone, or email at all                                                                          | `422 VALIDATION_ERROR` — these rows genuinely belong to different contacts |

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`:

```json theme={null}
{
  "error": {
    "code": "WEAK_IDENTITY_MATCH_REQUIRES_CONFIRMATION",
    "message": "Conversations share phone or email but have different contact_ids. This may be a shared family email, a shared call-center DID, or two unrelated contacts colliding on a weak identifier. Re-submit with confirm_weak_identity=true to proceed.",
    "status": 422,
    "details": {
      "primary_id": "conv_01J...",
      "conflicting_id": "conv_01K...",
      "primary_contact_id": "ct_01A...",
      "source_contact_id": "ct_01B...",
      "match_type": "phone"
    }
  },
  "meta": {
    "request_id": "req_01J9X4KZT8Q2M7W3N5VSP8DH6B",
    "timestamp": "2026-08-25T14:03:11.482Z"
  }
}
```

`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:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/conversations/{id}/merge" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
  "source_conversation_ids": ["conv_01K..."],
  "confirm_weak_identity": true
}'
```

Before confirming, check the two conversations in the inbox. If the phone or email is genuinely shared (a household, an office line), decide which contact is the right one first: if you intended these conversations to live under one contact record, resolve the duplicate contacts in **Audience → Identity resolution** and re-run the merge — a strong `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](/guides/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_id` from `meta.request_id` on 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_type` from `error.details` plus the phone or email value, and the two `contact_id` values from `details`

With those, support can replay the gate decision and confirm which tier fired without asking you to reproduce it.

<Note>
  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.
</Note>
