NetworkSubjectMergedPayload network.subject.merged@1
Generated at from
openvibe-contracts v0.76.0 and
openvibe-sdk v0.20.1.
- Version
- 1.0.0
- Owner
- network
- Visibility
- first-party
- Status
- active
- Compatibility
- backward
- Decision
- ADR-029
- Schema
https://openvibe.network/contracts/events/payloads/network.subject.merged.v1.json
network.subject.merged v1 (OpenVibe.Network; roadmap WS-B task 5, ADR-029). Two accounts became one: `from`, the folded-in subject, is now an alias of `into`, the survivor, which keeps its subject, username, profile and settings. Network has already moved what it owns (linked providers, sessions, OAuth grants, developer projects, OpenCoins and user modules) in the transaction that writes this event to its outbox. Every service that stores subjects repoints its own rows from `from` to `into` in its own transaction and keeps no copy under `from`; a row it cannot move (a unique conflict, such as both accounts following the same channel) keeps the survivor's row and drops the other, as a counted, logged outcome. Until a service has applied it, its rows stay under `from`, which still resolves to `into` through Network's aliases. Apply once per merge_id. Envelope: subject { type: user, id: <into> }, visibility internal, actor the person who merged (or the staff member, for an account-recovery merge).
Fields
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
merge_id | string | yes | The merge; a redelivery carries the same id. |
|
from | string | yes | The folded-in subject, now an alias. |
|
into | string | yes | The survivor. |
|
merged_at | string | yes |
| |
initiated_by | enum | yes | person: the account holder, signed in to both accounts. staff: an account-recovery merge (staff.identity.merge, with a written reason and an audit row). |
|
split_until | string | Until then Network keeps the folded-in account's pre-merge state, so staff can split a mistaken merge by hand (30 days). |
|
Examples
From the contract's own test fixtures: valid ones validate, rejected ones must fail.
Valid: person
{
"merge_id": "mrg_01JAB2C3D4E5F6G7H8J9K0MNPS",
"from": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR",
"into": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"merged_at": "2026-09-27T21:00:00.000Z",
"initiated_by": "person",
"split_until": "2026-10-27T21:00:00.000Z"
}Valid: staff
{
"merge_id": "mrg_01JAB2C3D4E5F6G7H8J9K0MNPS",
"from": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR",
"into": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"merged_at": "2026-09-27T21:00:00.000Z",
"initiated_by": "staff"
}Rejected: leaks-username
{
"merge_id": "mrg_01JAB2C3D4E5F6G7H8J9K0MNPS",
"from": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR",
"into": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"merged_at": "2026-09-27T21:00:00.000Z",
"initiated_by": "person",
"username": "bob"
}Rejected: legacy-id
{
"merge_id": "mrg_01JAB2C3D4E5F6G7H8J9K0MNPS",
"from": "42",
"into": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"merged_at": "2026-09-27T21:00:00.000Z",
"initiated_by": "person"
}Validate
const contracts = require('openvibe-contracts');
contracts.validate('network.subject.merged@1', value); // { valid, errors: [{ path, message }] }