Relationship authorization
Use a trusted backend credential to call Relationship on behalf of your application. API caller identity, project access and the business principals stored in a relationship are separate boundaries.
Credential and transport
| Credential | Contract |
|---|---|
| M7 access token | Signed compact JWT; treat its value as opaque. Send Authorization: Bearer ACCESS_TOKEN. |
| API audience | Exact audience https://api.relationship.service.m7.org; a frontend token is not automatically an API credential. |
| Caller | A user or confidential machine admitted by the receiving service; current roster status must be active. |
| Sender binding | Use an unbound Bearer token. Sender-constrained tokens are rejected with sender_proof_required; this API does not implement a DPoP protected-resource flow. |
The service validates token signature, the permitted RS512 algorithm,
accepted issuer (https://sso.user.m7.org or id.m7.org), trusted signing
certificate source, audience and time constraints. It then uses
receiver-authenticated M7 admission checks. Decoding a JWT or possessing a
signed token does not establish project authority. Never authorize your users
from decoded token claims alone.
ID tokens, refresh tokens, client secrets, authorization codes and API keys are not substitutes for this access token. Send the access token only in the Authorization header, over HTTPS; keep it out of URLs, request bodies, logs and analytics. Keep confidential client credentials and refresh packages in server-side secure storage.
Obtain, replace and revoke tokens
Use M7 SSO with the OAuth client and
audience registered for your integration. Its discovery document is at
https://sso.user.m7.org/.well-known/openid-configuration.
| Caller | Token acquisition |
|---|---|
| Application acting for a user | Authorization code with PKCE; device authorization for an input-constrained client. |
| Confidential backend acting as itself | client_credentials, with the client's registered authentication method. |
For a confidential client configured for client_secret_basic:
curl --fail-with-body --silent --show-error \
--request POST 'https://sso.user.m7.org/token' \
--user 'CLIENT_ID:CLIENT_SECRET' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'aud=https://api.relationship.service.m7.org'
Request scope=orgs as well when using an organization workspace. The OAuth
client policy must permit that audience, grant and scope. Select an issuance
policy that returns an unbound Bearer token for this API.
Observe SSO's returned expires_in; do not assume a fixed lifetime. A machine
token has no refresh token: authenticate the client again to obtain a
replacement. User-authorized refresh credentials and their binding package go
to SSO's token endpoint under the applicable SSO lifecycle. Relationship
does not refresh tokens. Revoke credentials through SSO or the owning
application's credential controls; revoked, expired or denied admission is not
overridden by project membership. After a 401, obtain a valid replacement;
repeated 403 responses require an access decision rather than token retries.
Project authority and application principals
The effective workspace principal must have an active membership in the project's pinned Management group. Receiving-service Root or Admin status does not supply another project's membership. Use the returned project context to inspect current capabilities.
| Operation | Required project capability | Additional role boundary |
|---|---|---|
| Read project context | project.view |
Active project membership. |
| Read spaces/subspaces | space.list |
Current optional read grant. |
| Read current relationships, requests or activity | relationship.list |
Current optional read grant. |
| Create/save/remove relationships; create/decide requests | relationship.manage |
Project Owner or Admin. |
| Read or acknowledge mailbox events | mailbox.notifications |
Current mailbox grant; acknowledgement rechecks admission. |
Read grants can be changed independently of membership. Current role,
capabilities and admission are checked again during mutations. A business
reference such as {"namespace":"example.app","principal_id":"PERSON_UUID"}
does not enroll that person as an API caller or project operator. The consuming
application must verify that reference and decide which user can create,
accept, cancel or remove a relationship before forwarding a command.
Personal and organization workspaces
Omit organization selection for the caller's personal workspace. Select an
organization with header Org: ORGANIZATION_UUID or body field org_id.
The value must be a non-nil dashed UUID. If both are supplied they must match
case-insensitively; null or malformed selections are rejected. Do not send
organization names, membership objects or capability lists as authority.
Organization operations require an access token with orgs scope and a current
organization disclosure to Relationship. Every selected-organization request,
including reads, obtains current organization facts. For a machine, the
validated OAuth client must be the actual machine caller. The organization's
stable principal and own project membership supply workspace access; the
machine's personal memberships are not inherited. The actual actor remains
separately attributed in relationship events.
Current direct Management tag names resolve through the Relationship catalogue. Explicit selections replace role fallback; a nonempty selection of unknown names grants nothing. Only a verified empty selection can use its configured role fallback. Resolved organization grants are intersected with project capabilities. A tag never promotes the project membership role.
For an unbound Bearer token, no Org-DPoP companion header is required. That
header concerns an organization disclosure proof; it does not add DPoP
protected-resource support or make a sender-constrained token acceptable here.
curl --fail-with-body --silent --show-error \
--request POST 'https://api.relationship.service.m7.org/v1/projects/context' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--header 'Org: ORGANIZATION_UUID' \
--header 'Content-Type: application/json' \
--data '{"project_id":"PROJECT_UUID"}'
Authorization failures
| Failure | Action |
|---|---|
HTTP 401; missing_token, invalid_token or token_required |
Supply an unexpired M7 token for the exact API audience. |
HTTP 401; sender_proof_required |
Use an authorized unbound Bearer issuance profile. |
| Admission or operation code 403 | Check active receiving admission, workspace disclosure, project membership and the required capability. |
| Admission HTTP 503 or operation body code 5xx | Authorization verification is unavailable; fail closed and retry with backoff. |
| Code 400 for organization selection | Correct its UUID and conflicting selections. |
Inspect the HTTP status, status and data.code, as described in the
service response rules. Credentials
must never be resent to an arbitrary endpoint named in an error message.