Enrollment requests

Request a pairwise connection or resource membership, then accept, decline or cancel it after your application has checked the user's authority and consent. Relationship commits the resulting bond and request event together.

Routes

All routes use POST under https://api.relationship.service.m7.org/v1/ with the Bearer and workspace contract. Every request names UUID strings project_id, space_id and optional subspace_id.

Route Other required fields Optional fields Authority and result inside data
/projects/relationships/requests/list — kind, count, offset relationship.list; project_id, items, next_offset, space; supplied kind is echoed.
/projects/relationships/requests/create source, idempotency_key; pairwise also requires target kind, metadata, ttl_seconds relationship.manage, Owner/Admin; request.
/projects/relationships/requests/decide request_id, version, decision — relationship.manage, Owner/Admin; request.

Omitted or null subspace_id selects the root. References and metadata follow the scope contract. List paging defaults to 100 with count 1–200; only the pending requests in that scope are listed.

Create a request

Field Type and behavior
source Required principal object.
target Required principal object for pairwise; omit for resource membership.
kind Pairwise: connection (default). Membership: invitation (default) or application. Set it explicitly for clarity.
idempotency_key Required nonempty UTF-8 string, at most 191 bytes; keep it for retries of this attempt.
metadata Optional JSON object, defaults to {}; no built-in consent or resource-permission semantics.
ttl_seconds Optional integer 1–31,536,000, default 604,800 (seven days).
curl --fail-with-body --silent --show-error \
  --request POST 'https://api.relationship.service.m7.org/v1/projects/relationships/requests/create' \
  --header 'Authorization: Bearer ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{"project_id":"PROJECT_UUID","space_id":"SPACE_UUID","subspace_id":"SUBSPACE_UUID","source":{"namespace":"example.app","principal_id":"PERSON_UUID"},"kind":"application","idempotency_key":"example-application-attempt-1","metadata":{"message":"Please add me"},"ttl_seconds":604800}'

The returned request includes id, scope IDs, kind, state, source_ref, target_ref, metadata, expires_at, version and nullable relationship_id and decision_actor. Save the actual ID and positive integer version. New requests have state: "pending"; membership target_ref is null.

There is at most one pending request for the exact bond and kind. Invitation and application slots are independent; pairwise has one connection slot. Another pending request in that slot returns body code 409.

Retry identity

An idempotency key is scoped to the project and actual authenticated actor, across relationship kinds. The same key and same input return the prior result, including a terminal result. Changed input returns body code 409. Keep the same actor for a retry; switching authenticated actors changes retry identity. Use a new key for a new attempt after decline, cancellation or expiry.

Decide and observe completion

{
  "project_id": "PROJECT_UUID",
  "space_id": "SPACE_UUID",
  "subspace_id": "SUBSPACE_UUID",
  "request_id": "REQUEST_UUID",
  "version": 1,
  "decision": "accept"
}

decision is exactly accept, decline or cancel. request_id is a UUID; version is its current positive integer version.

Outcome Returned terminal state Relationship result
accept accepted Creates or reuses the active bond; relationship_id identifies it.
decline declined No bond created.
cancel cancelled No bond created.
Lifetime elapsed expired No bond created; a decision returns body code 409.

Terminal requests leave the pending list. Their results remain in activity and retry receipts; a terminal request is not reactivated. Retrying a decision with the same terminal outcome returns that result. A conflicting terminal decision returns body code 409. A stale version also returns 409; missing or wrong-scope IDs return 404. Observe both status and data.code under the response rules.

Request-list and activity reads, and relevant commands, settle elapsed requests. Mailbox reads do not settle expiry. Use the mailbox to deliver result events and the current graph for current state. Acceptance is not an automatic permission change in your application; an accepted event can remain after its bond is removed.