Outcome
Revoke one explicit workspace member grant without changing organization membership.
These examples prove local handler behavior with real isolated SQL and explicitly simulated identity/permission dependencies.
Before you start
-
Verify identity and the current credential-scoped action contract before preparing input. Workspace-access operations require a consistent active organization context and target workspace authority; metadata discovery does not prove execution permission.
-
Discover the existing canonical workspace in the exact application/organization and intended recipient. These actions do not provision identities, organization memberships, application seats or entitlement.
-
Review the validated plan and intended effect before confirmation. Capture current owner and member grants before a write. Review new-owner eligibility and previous-owner retention separately before transferring ownership.
Discover the target
topolo whoami --json
topolo apps --json
topolo actions get app_topolo_auth.workspace_access.members.revoke --detail --json
Read app_topolo_auth.workspace_access.list_mine for this organization/app; copy data.resources[].id. For management read workspace_access.members.list. Recipient candidates require current active membership and app access.
Input fields
| Field | Required | Meaning |
|---|---|---|
resourceId | Yes | Canonical workspace id copied from workspace_access.list_mine data.resources[].id, not a resourceId property. Use a current active workspace in the exact organization/application. |
organizationId | Yes | Exact organization owning this workspace. It is checked against both the authenticated context and canonical workspace record, independently of CLI selection. |
appId | Yes | Canonical owning application id. This operation does not assign application entitlement or create a seat. |
userId | Yes | Existing recipient with active membership in this organization and application access. Directory entries are candidates, not proof of eligibility; the write checks actual membership and entitlement. Current owner access cannot be revoked as a member grant. |
reason | No | Optional reason stored with soft revocation. Repeating member revoke after success returns NOT_FOUND rather than a new successful revocation. |
Effects
- Soft revokes one explicit member grant; keeps recipient identity, organization membership and unrelated workspace grants.
Interpret the response
Raw target responses use success/message/data/timestamp. Grant returns HTTP 201; the other successes return 200. CLI/MCP envelopes may wrap or project the target response. Epoch-number record times differ from the ISO envelope timestamp.
list_mine data.resources contains canonical workspace records whose identifier is id. accessMode all and canManageAll true require organization owner/super_admin or valid platform authority; ordinary organization admin remains restricted. Owner and explicit unexpired invited_member grants drive the restricted projection.
members.list returns one owner, explicit member grants and at most 500 directory candidates. This directory has no continuation and is not guaranteed exhaustive. A candidate may still fail active membership/app assignment validation. No returned user data should be exported beyond the authorized workflow.
Member data.id is the grant id after profile and grant objects are combined; data.userId is the recipient identity id. Owner data.id is its user id. Owner profile can contain only id/name/accessRole when it is absent from the directory, and member profile fields can be absent. Profile activeContext/orgId describe that user context and do not replace the workspace organization boundary.
Grant is a natural-key upsert. Repetition changes scope, clears old revocation, resets expiry/metadata through creation defaults and preserves its id/createdAt. It does not create organization membership, application assignment, a seat or ownership. Audit persistence is nonfatal; successful grant/revoke/transfer does not prove an audit entry.
Member revoke returns userId/resourceId and removes explicit access only. Repetition returns NOT_FOUND. Owner removal returns CONFLICT. Organization-wide authority and independent application permissions still require separate verification.
The workspace owner and ownership projection update in one atomic access-database batch. Removing the new owner redundant grant and retaining/removing the previous owner grants occur afterward in separate writes; the full transfer is not atomic. A 500 can leave a new owner and a redundant member grant while previous-owner retention is unfinished.
A blind retry after partial transfer is unsafe: the former owner may be denied and a transfer to the now-current owner returns CONFLICT. Read members/current ownership first. The public revoke action refuses the current owner even if a redundant owner grant remains; this family has no customer cleanup operation for that partial state. Preserve evidence and seek authorized operator repair rather than inventing an endpoint or bypassing ownership.
Worked examples
These are complete raw target-handler responses, not CLI/gateway envelopes or live authorization proof. Replace synthetic identifiers using authorized discovery. Save only the reviewed input as payload.json; inspect validation and plan before executing. --confirm assumes the intended operation is approved. Metadata read-back requires separate recipient-principal and owning-application access tests.
Revoke removes only the explicit workspace member grant
Input · payload.json:
{
"organizationId": "org_example",
"appId": "app_topolo_campaigns",
"resourceId": "ws_fixture_one",
"userId": "usr_member",
"reason": "campaign_done"
}
CLI:
topolo actions validate app_topolo_auth.workspace_access.members.revoke --data @payload.json --json
topolo actions plan app_topolo_auth.workspace_access.members.revoke --data @payload.json --confirm --json
topolo actions call app_topolo_auth.workspace_access.members.revoke --data @payload.json --confirm --json
MCP call after validation and plan review:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.workspace_access.members.revoke",
"input": {
"organizationId": "org_example",
"appId": "app_topolo_campaigns",
"resourceId": "ws_fixture_one",
"userId": "usr_member",
"reason": "campaign_done"
},
"confirm": true
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "Workspace access revoked",
"data": {
"userId": "usr_member",
"resourceId": "ws_fixture_one"
},
"timestamp": "2026-10-02T18:24:03.962Z"
}
A separately approved revoke after regrant removes renewed member access
Input · payload.json:
{
"organizationId": "org_example",
"appId": "app_topolo_campaigns",
"resourceId": "ws_fixture_one",
"userId": "usr_member",
"reason": "regrant_review_completed"
}
CLI:
topolo actions validate app_topolo_auth.workspace_access.members.revoke --data @payload.json --json
topolo actions plan app_topolo_auth.workspace_access.members.revoke --data @payload.json --confirm --json
topolo actions call app_topolo_auth.workspace_access.members.revoke --data @payload.json --confirm --json
MCP call after validation and plan review:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_auth.workspace_access.members.revoke",
"input": {
"organizationId": "org_example",
"appId": "app_topolo_campaigns",
"resourceId": "ws_fixture_one",
"userId": "usr_member",
"reason": "regrant_review_completed"
},
"confirm": true
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "Workspace access revoked",
"data": {
"userId": "usr_member",
"resourceId": "ws_fixture_one"
},
"timestamp": "2026-10-02T18:24:03.963Z"
}
Verify the result
-
Read members.list and confirm the target userId is absent from explicit members. Repeated revoke reports NOT_FOUND.
-
Verify target-principal workspace denial through the owning application, accounting for organization-wide authority and independent access.
Errors and recovery
Repeated member revocation reports missing live grant
Captured local refusal · HTTP 404:
{
"input": {
"organizationId": "org_example",
"appId": "app_topolo_campaigns",
"resourceId": "ws_fixture_one",
"userId": "usr_member"
},
"response": {
"success": false,
"error": {
"message": "Workspace member grant not found",
"code": "NOT_FOUND",
"details": null,
"timestamp": "2026-10-02T18:24:03.962Z"
}
}
}
Ownership must be transferred before owner removal
Captured local refusal · HTTP 409:
{
"input": {
"organizationId": "org_example",
"appId": "app_topolo_campaigns",
"resourceId": "ws_fixture_one",
"userId": "usr_owner"
},
"response": {
"success": false,
"error": {
"message": "Transfer ownership before removing the workspace owner",
"code": "CONFLICT",
"details": null,
"timestamp": "2026-10-02T18:24:03.963Z"
}
}
}
Owner grant cannot be cleaned up using the customer revoke action
Captured local refusal · HTTP 409:
{
"input": {
"organizationId": "org_example",
"appId": "app_topolo_campaigns",
"resourceId": "ws_fixture_one",
"userId": "usr_new_owner"
},
"response": {
"success": false,
"error": {
"message": "Transfer ownership before removing the workspace owner",
"code": "CONFLICT",
"details": null,
"timestamp": "2026-10-02T18:24:03.967Z"
}
}
}
Published recovery guidance
-
state_conflict(HTTP 409): Read the current resource state, reconcile the intended change, and retry only if still valid. -
VALIDATION_ERROR(HTTP 400): Correct the reported fields and use an existing active same-organization app member. Directory candidates alone do not prove eligibility. -
AUTH_ERROR(HTTP 401): Verify the intended authenticated identity and refresh an expired token if necessary. Do not change credential context automatically. -
FORBIDDEN(HTTP 403): Check consistent active organization context and exact workspace management authority. Personal/conflicting context is refused even with stale org metadata or platform roles. -
NOT_FOUND(HTTP 404): Refresh canonical workspace/member discovery. An already revoked member grant is absent; do not fabricate a stored grant. -
CONFLICT(HTTP 409): Read current owner and grants before reviewing any new decision. Do not grant/revoke the owner as a member or blindly repeat a completed/partial transfer. -
SERVER_ERROR(HTTP 500): Read current owner and member grants before retrying. Ownership may already have changed. Preserve a partial owner/grant state for authorized repair when customer actions cannot complete cleanup.
Recovery boundaries
Regranting captured scope requires a separate approved access decision and current eligibility. Ownership must be reviewed independently; member revoke cannot remove current owner access or repair a redundant current-owner grant.
Discovery and evidence
This record belongs to app_topolo_auth. Local tests prove real canonical workspace/grant SQL and handler responses with simulated identity, directory lookup, memberships and application assignment. They do not prove recipient sign-in, invitation delivery, owning-application resource enforcement, live permissions, deployment or production completion.