Current relationships
Use current relationship records to determine whether a connection or resource membership exists in an exact scope. Your application decides the resulting resource permissions. Historical accepted events do not prove a bond is still active.
Routes and authority
All routes use POST under https://api.relationship.service.m7.org/v1/ and the
authorization contract. Read requires relationship.list.
Mutations require relationship.manage and project Owner or Admin.
Every route requires the scope: UUID strings project_id,
space_id and optional subspace_id (omitted/null means the root).
| Route | Other required fields | Optional fields | Result inside data |
|---|---|---|---|
/projects/relationships/list |
— | status, source, count, offset |
project_id, items, next_offset, space. |
/projects/relationships/create |
source; pairwise also requires target |
metadata |
relationship. |
/projects/relationships/save |
relationship_id, version, status |
metadata |
relationship. |
/projects/relationships/remove |
relationship_id, version, confirm_relationship_id |
removed_by, reason |
removed: true, relationship_id. |
List
status, if supplied, must be the string active, the only current-record
status. source is a principal object; it matches either endpoint for pairwise
records and the member for resource membership. There is no separate target
list filter. count and offset follow the common pagination rules.
curl --fail-with-body --silent --show-error \
--request POST 'https://api.relationship.service.m7.org/v1/projects/relationships/list' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{"project_id":"PROJECT_UUID","space_id":"SPACE_UUID","source":{"namespace":"example.app","principal_id":"PERSON_UUID"},"count":100,"offset":0}'
Each record includes id, project_id, space_id, nullable subspace_id,
source_ref, target_ref, status, metadata, positive integer version,
creation/update timestamps and actor attribution. Membership target_ref is
null. Pairwise records also identify their direction. Store the returned id
and version for later mutations.
Create and update
Pairwise request body:
{
"project_id": "PROJECT_UUID",
"space_id": "SPACE_UUID",
"source": {"namespace": "example.app", "principal_id": "FIRST_PERSON_UUID"},
"target": {"namespace": "example.app", "principal_id": "SECOND_PERSON_UUID"},
"metadata": {}
}
Resource membership request body:
{
"project_id": "PROJECT_UUID",
"space_id": "SPACE_UUID",
"subspace_id": "SUBSPACE_UUID",
"source": {"namespace": "example.app", "principal_id": "PERSON_UUID"},
"metadata": {"label": "Reader"}
}
source and pairwise target follow the exact principal-reference contract.
Omit target for membership. metadata defaults to an empty object and follows
the metadata limits.
An identical active create returns the existing record without another create event. Creating the same bond with changed metadata returns body code 409; save its current version instead:
{
"project_id": "PROJECT_UUID",
"space_id": "SPACE_UUID",
"relationship_id": "RELATIONSHIP_UUID",
"version": 1,
"status": "active",
"metadata": {"label": "Updated reader"}
}
relationship_id is a UUID; version is the returned positive integer,
not a string. status is required on save and must be active. A save cannot
move a bond or change its principals. Omit metadata to preserve it or send {}
to clear it. A stale version returns body code 409; a record from another scope
returns body code 404.
Removal, leaving and re-adding
Use the same remove command for a consumer's Leave or Remove action, after applying your own user permissions:
{
"project_id": "PROJECT_UUID",
"space_id": "SPACE_UUID",
"relationship_id": "RELATIONSHIP_UUID",
"version": 1,
"confirm_relationship_id": "RELATIONSHIP_UUID",
"removed_by": "PERSON_UUID",
"reason": "Left the group"
}
confirm_relationship_id must exactly identify the relationship being removed.
reason is an optional UTF-8 string, at most 8,192 bytes; an empty value is
recorded as null. removed_by is optional UUID attribution:
removed_by |
Logged removal type |
|---|---|
| Omitted, empty string or nil UUID | system_kick. |
| UUID matches the member or either pairwise endpoint | voluntary_leave. |
| Another non-nil UUID | kick. |
This attribution does not authenticate the remover or grant authority; the
actual authenticated actor is recorded separately. Removal deletes the
current bond and retains its previous record in an event with
snapshot.removal. Re-adding creates a fresh record and UUID. Reload current
relationships after removal rather than treating a historical event as an
active membership.