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.