Publication invitations, applications and notifications

Blog authorizes callers and applies accepted requests to the publication’s pinned Management group. Relationship Service owns the request lifecycle and recipient mailbox. Relationship state alone never authorizes a Blog content or management operation.

All routes below use POST, the normal Blog response envelope and bearer authentication and an active Blog roster principal. The selected organization is resolved through Blog’s live, recipient-authenticated organization check. Omitting organization selection acts as the authenticated personal user or machine app. Clients may send workspace_principal_id to reject an action if the resolved workspace changed.

Routes

All paths below begin with /v1/. UUID fields identify the selected publication, Blog roster principal, or enrollment command. Decision version is the integer returned by the current status response.

Route Required input Optional input / result
/v1/member/invite/create publication_id, recipient principal_id, idempotency_key message; returns request identifiers and status.
/v1/member/apply/create publication_id, idempotency_key message; applicant is the verified current workspace.
/v1/member/invite/status, /v1/member/apply/status command_id Returns request, can_respond, grant_applied, and access_active.
/v1/member/invite/accept, /v1/member/invite/decline, /v1/member/invite/cancel command_id, version Applies the permitted recipient or manager decision.
/v1/member/apply/accept, /v1/member/apply/decline, /v1/member/apply/cancel command_id, version Applies the permitted manager or applicant decision.
/v1/member/requests/list publication_id count (1–50, default 25), offset; paged items, nullable next_offset.
/v1/notification/list None Same count and offset; current workspace's mailbox.
/v1/notification/read notification_id Idempotent delivery acknowledgement.

All routes accept optional workspace_principal_id to check that the current workspace still matches the client's displayed selection. Organization selection follows workspaces.

Invitations

A current publication Owner or Admin with member.save sends /v1/member/invite/create with publication_id, the recipient’s Blog principal_id, a fresh UUID idempotency_key, and an optional message (up to 2,000 UTF-8 bytes). The recipient must already be active in the Blog roster. Enrollment grants ordinary Member access; role assignment is a separate management action.

The response contains request.command_id, request.request_id, state, version, the publication/recipient/issuer labels, message and expiry. Reuse the same idempotency key and input after a timeout. Changing input under that key returns a conflict. A fresh request uses a fresh key.

/v1/member/invite/status takes command_id. Only its recipient workspace or an authorized publication manager may inspect it. The response includes can_respond, grant_applied, and access_active.

The recipient sends /v1/member/invite/accept or /v1/member/invite/decline with command_id and the integer version displayed by status. Organization responses require a currently verified organization Owner or Admin whose organization profile includes member.save. Membership is granted to the organization principal, while the actual actor is recorded separately.

An authorized publication manager can send /v1/member/invite/cancel using the same command/version fields. Invitations expire after seven days. Expired or cancelled requests cannot grant access. The issuer must still actively manage the publication when an uncompleted invitation grant is applied.

Applications

An authenticated user or machine app sends /v1/member/apply/create with publication_id, a fresh UUID idempotency_key, and optional message. principal_id is not accepted: the applicant is the verified current workspace. Organization applicants require the same organization authority as invitation responses. Existing membership must be managed before requesting enrollment.

/v1/member/apply/status takes command_id. The applicant can withdraw with /v1/member/apply/cancel. A current publication Owner or Admin with member.save approves or declines through /v1/member/apply/accept or /v1/member/apply/decline, using command_id and version. An applicant cannot approve their own application without current publication management authority.

Managers load the Access queue through /v1/member/requests/list with publication_id, optional count (1–50, default 25), and offset. Its items contain pending invitations/applications and, on the first page, accepted grants that require recovery. Continue with next_offset until it is null.

Notifications

/v1/notification/list takes optional count and offset. It returns the current principal_id, workspace_name, items and next_offset. Each item contains notification_id, type, action, created_at, delivered_at, and optionally a publication label. An invitation includes command_id for its separate status and consent dialog. Mailboxes are scoped to the verified Personal or organization principal and span publications.

After displaying an entry to its recipient, send /v1/notification/read with notification_id. This records an idempotent delivery acknowledgement. Listing and previewing do not acknowledge entries. Delivery acknowledgement is separate from accepting or declining a request and never grants membership. Acknowledged entries remain in the notification list.

Removal and leaving

/v1/member/remove retains its existing publication_id, principal_id and exact member_id contract; /v1/member/leave accepts the current workspace’s publication_id. Both recheck current Blog authority and the last-owner rule before removing the matching active Relationship bond in the protected publication scope, then delete the Blog membership.

Relationship records manager removal as kick (or system_kick) and the recipient’s own departure as voluntary_leave. The notification projection includes removal_type, and the mailbox displays “You were removed from the publication” or “You left the publication” with the publication name. Removing a legacy direct membership with no Relationship bond still removes it locally; there is no Relationship removal event for a bond that never existed.

Remote failure returns an error instead of reporting successful removal. If Relationship committed but its reply was lost, or the subsequent Blog membership write failed, retry the same Remove/Leave: Blog finishes the local deletion after seeing that the remote bond is already absent. The existing remote event is retained, without a duplicate removal notification. A completed grant receipt is retained so replaying an old acceptance never restores removed access.

Recovery and access authority

Remote acceptance and the Blog membership grant can complete separately. If acceptance succeeds while the membership write fails, status shows accepted with grant_applied=false; retry Accept to finish the grant. The Access queue also shows these incomplete grants to managers.

Once status reports a successfully applied membership grant, replaying Accept never creates or reactivates a membership. Removing, disabling or leaving a membership continues to take effect through Blog Management, regardless of the historical request or Relationship event. Existing active memberships keep their roles and capabilities. Inactive memberships require an administrator to resolve them before enrollment can continue.

The browser closes stale invitation details on workspace changes and discards responses belonging to the previous workspace. Backend authority is independently checked on every action. Use the verified current workspace for every action.

An unavailable Relationship service returns an error for invitation, application, or mailbox actions; an empty result is not a successful fallback. It does not remove otherwise valid publication content access. Reload the request status after recovery and keep the original idempotency key for a timed-out creation. A publication with no enrollment history has an empty Access queue. Existing direct membership management remains available under its documented Blog authority; do not interpret a Relationship outage as a reason to grant access or auto-accept a request.