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.