Identity Merges
When one person ends up with two accounts, a merge folds the duplicate into the survivor. Approving one does three things at once:
- every moved alias is reassigned to the target user,
- a redirect is written from the source user id to the target, and
- the merge record is stamped
completedwith the ids it actually moved.
Anyone may request a merge; only a platform admin may execute one.
Requesting
snug alias merge request --source docs4-dup-acct --target docs4-keep-acct \
--reason "Duplicate account created during signup"{
"merge_id": "mrg_pVnTpMgNcsMAJgQnHYRc",
"source_user_id": "docs4-dup-acct",
"target_user_id": "docs4-keep-acct",
"status": "pending_review",
"alias_ids_to_move": [],
"moved_alias_ids": [],
"created_at": "2026-08-28T23:10:22.272350Z",
"completed_at": null
}Leaving alias_ids_to_move empty means all of the source's active
aliases. Pass --alias-id (repeatable) to move a subset. Source and target
must differ, or the request is 400 invalid_request. Nothing checks that
either id belongs to a real user, so a typo in --target produces a merge
that completes into an id nobody owns.
Approving and rejecting
snug alias merge approve --merge-id mrg_pVnTpMgNcsMAJgQnHYRc
snug alias merge reject --merge-id mrg_xTgqqZNGQXDHRvSjWjJC --reason "evidence insufficient"Each acts on a merge still in pending_review, and both need a platform
admin token - an ordinary caller gets 403 insufficient_permissions.
Approval reports what actually moved:
{
"merge_id": "mrg_pVnTpMgNcsMAJgQnHYRc",
"source_user_id": "docs4-dup-acct",
"target_user_id": "docs4-keep-acct",
"status": "completed",
"moved_alias_ids": [
"als_DaNVqFLgDSAxAFubRccq",
"als_xTmBbhusDKTdzQnKXeJy"
],
"completed_at": "2026-08-28T23:10:27.167674Z"
}There is no endpoint that reads a merge back. Re-approving a completed merge
(or re-rejecting a rejected one) is a no-op returning the current record,
which is the closest thing to a read. Crossing between the two terminal
states is refused with 409 invalid_merge_transition.
What the redirect does
The redirect is what makes a merge stick. Afterwards the source user id
still works everywhere: resolving any moved alias returns the target user,
and snug alias list --user docs4-dup-acct looks the target up instead.
Listing still applies the caller's own visibility, so an outsider sees
pagination.total: 2 alongside an empty array where an admin sees both
aliases.
Redirects are collapsed, not chained. Merging dup -> keep and then
keep -> final rewrites the first pointer so dup lands on final
directly:
docs4-alias-dup -> docs4-alias-final
docs4-alias-dup2 -> docs4-alias-final
docs4-alias-keep -> docs4-alias-finalThe redirect is written whether or not you moved everything. A partial
merge leaves the unmoved aliases owned by the source on paper while
resolution already points at the target, so an alias can report
user_id: docs4-alias-dup2 on GET and still resolve to
docs4-alias-final. Move everything unless you have a reason not to.
Conflict policy
conflict_policy.revoked_aliases decides what happens to the source's
revoked aliases, and only bites when you name them in alias_ids_to_move -
an empty list sweeps up active aliases only, so revoked ones stay put either
way. With a source holding one revoked and one active alias,
both named explicitly:
preserve_revoked(default) moved both, leaving the revoked one attached to the survivor with itsrevoked_atintact.drop_revokedmoved only the active one.
curl -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"source_user_id":"docs4-alias-dup3","target_user_id":"docs4-alias-final",
"reason":"Drop the dead link",
"conflict_policy":{"revoked_aliases":"drop_revoked"}}' \
http://localhost:4000/api/v1/alias/mergeThe body also accepts conflict_policy.primary_identity and
conflict_policy.metadata. Both are stored on the merge record but are not
consulted when it executes, so do not rely on them to pick a winning primary
or reconcile metadata - do that yourself afterwards with
primary set.
Reference
- Alias API - the merge endpoints and their full request bodies
- Alias overview - linking, resolution, and revocation