Topolo Docs Topolo Platform
Open workspace

Create user permission override

Insert a new declared permission allow/deny override; repeated creation can duplicate rows and does not assign app access.

Outcome

Insert a new declared permission allow/deny override; repeated creation can duplicate rows and does not assign app access.

These examples prove local handler behavior with real isolated SQL and explicitly simulated identity/permission dependencies.

Before you start

  • Verify the intended authenticated identity and the current credential-scoped action before preparing input. Self and managed-user security operations have different authority; an override id does not authorize a different user or organization.

  • Use authorized identity and user/session discovery. Recovery security data and session/device identifiers must remain inside the caller-approved workflow.

  • Review the validated plan and intended effect before confirmation. Allow/deny changes and deny removal can change effective access. Capture current rows and evaluate the reviewed user/organization/application permissions before and after the confirmed decision. Fixture examples authorize no live mutation or external delivery.

Discover the target

topolo whoami --json
topolo apps --json
topolo actions get app_topolo_auth.user_permission_overrides.create --detail --json

Discover the exact user and organization membership, application and service_permissions.list entries. Read user_permission_overrides.list with exact org/app filters; use its row id, user and organization together. Read user_permissions.get separately for effective access.

Input fields

FieldRequiredMeaning
userIdYesExisting Topolo identity from authorized user discovery. Read allows self or a permitted manager. Writes require an organization administrator role and authority over the actual override organization, not merely a known row id.
orgIdYesRead filter or required create organization. The target identity must belong to it under the handler membership resolution. An omitted read filter queries this user across organizations; it is not a pagination cursor. Delete obtains organization from the exact stored user/override row and checks that organization authority.
appIdYesCanonical application from credential-scoped discovery. Select the exact application owning the declared permission. An omitted read filter queries stored overrides across applications; override creation does not assign application access or a seat.
permissionNameYesDeclared permission name from service_permissions.list for this application. Unknown names currently produce SERVER_ERROR. Use the returned local permission name, not a fabricated cross-application wildcard.
effectYesallow or deny stored as a new explicit override. Duplicate and conflicting rows are permitted; repeated creation is not an upsert. Deny removal can increase effective access, so verify the complete role/grant/override evaluation before and after a change.
expiresAtNoOptional nonnegative integer epoch seconds or null. null/omission stores unbounded expiry. A past positive timestamp remains visible in stored-row listing while current evaluation treats it as inactive. Current evaluation treats zero as unbounded; do not use zero to expire access.

Effects

  • Inserts one new declared permission allow/deny row with a fresh id, including on repeated identical calls.

  • Does not create app entitlement, membership, a seat or a role; audit errors are nonfatal.

Interpret the response

Raw target successes have success/message/data/timestamp; creation is HTTP 201 and read/delete are 200. CLI/MCP may wrap or project the response. Stored fields are snake_case with epoch seconds; the envelope timestamp is ISO text. Fixtures prove local canonical SQL/handler behavior with simulated token identity, target memberships and permission catalog partitions, not live effective permission enforcement.

Creation always inserts a fresh id; repeated calls can produce duplicate user/org/app/permission rows. allow and deny can coexist. The application must declare permissionName, but the write itself does not grant app membership or entitlement. Unknown declared permission is a current SERVER_ERROR. Nonfatal audit errors can follow a successful write.

A past positive expiry is stored, not rejected or omitted from the row list. Current evaluation treats null and zero as unbounded. Review actual user_permissions.get and protected application requests to establish effective access; the new row alone cannot prove an allow is effective or a deny is the only denying source.

The write requires organization role owner, super_admin or admin and actual organization authority. A direct platform_super_admin role alone is refused by this existing organization-role gate; discovery is not runtime authorization. Authenticated authority denial uses 401 AUTH_ERROR and does not by itself mean OAuth expired.

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. Storage read-back and actual sign-in outcomes require separate verification. Explicit rows do not assign app access or seats, and duplicate/conflicting overrides can remain. Removing a deny can increase access. Cookies and Bearer tokens must identify the intended principal consistently.

Create a declared allow override for the reviewed target organization/application

Input · payload.json:

{
  "userId": "usr_managed",
  "orgId": "org_example",
  "appId": "app_topolo_campaigns",
  "permissionName": "campaigns.read",
  "effect": "allow"
}

CLI:

topolo actions validate app_topolo_auth.user_permission_overrides.create --data @payload.json --json
topolo actions plan app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json

MCP call after validation and plan review:

{
  "tool": "topolo_call_action",
  "arguments": {
    "actionId": "app_topolo_auth.user_permission_overrides.create",
    "input": {
      "userId": "usr_managed",
      "orgId": "org_example",
      "appId": "app_topolo_campaigns",
      "permissionName": "campaigns.read",
      "effect": "allow"
    },
    "confirm": true
  }
}

Captured local handler response · HTTP 201:

{
  "success": true,
  "message": "User permission override created",
  "data": {
    "override": {
      "id": "upo_FfLusJrOwlvd",
      "user_id": "usr_managed",
      "organization_id": "org_example",
      "app_id": "app_topolo_campaigns",
      "permission_name": "campaigns.read",
      "effect": "allow",
      "expires_at": null,
      "created_at": 1790899200
    }
  },
  "timestamp": "2026-10-02T00:00:00.000Z"
}

Repeated creation creates a distinct stored override instead of an idempotent replacement

Input · payload.json:

{
  "userId": "usr_managed",
  "orgId": "org_example",
  "appId": "app_topolo_campaigns",
  "permissionName": "campaigns.read",
  "effect": "allow"
}

CLI:

topolo actions validate app_topolo_auth.user_permission_overrides.create --data @payload.json --json
topolo actions plan app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json

MCP call after validation and plan review:

{
  "tool": "topolo_call_action",
  "arguments": {
    "actionId": "app_topolo_auth.user_permission_overrides.create",
    "input": {
      "userId": "usr_managed",
      "orgId": "org_example",
      "appId": "app_topolo_campaigns",
      "permissionName": "campaigns.read",
      "effect": "allow"
    },
    "confirm": true
  }
}

Captured local handler response · HTTP 201:

{
  "success": true,
  "message": "User permission override created",
  "data": {
    "override": {
      "id": "upo_E-G6cLsM_852",
      "user_id": "usr_managed",
      "organization_id": "org_example",
      "app_id": "app_topolo_campaigns",
      "permission_name": "campaigns.read",
      "effect": "allow",
      "expires_at": null,
      "created_at": 1790899200
    }
  },
  "timestamp": "2026-10-02T00:00:00.000Z"
}

A deny for the same permission is a separate row alongside existing allows

Input · payload.json:

{
  "userId": "usr_managed",
  "orgId": "org_example",
  "appId": "app_topolo_campaigns",
  "permissionName": "campaigns.read",
  "effect": "deny"
}

CLI:

topolo actions validate app_topolo_auth.user_permission_overrides.create --data @payload.json --json
topolo actions plan app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json

MCP call after validation and plan review:

{
  "tool": "topolo_call_action",
  "arguments": {
    "actionId": "app_topolo_auth.user_permission_overrides.create",
    "input": {
      "userId": "usr_managed",
      "orgId": "org_example",
      "appId": "app_topolo_campaigns",
      "permissionName": "campaigns.read",
      "effect": "deny"
    },
    "confirm": true
  }
}

Captured local handler response · HTTP 201:

{
  "success": true,
  "message": "User permission override created",
  "data": {
    "override": {
      "id": "upo_JnwqYI7Kow_Z",
      "user_id": "usr_managed",
      "organization_id": "org_example",
      "app_id": "app_topolo_campaigns",
      "permission_name": "campaigns.read",
      "effect": "deny",
      "expires_at": null,
      "created_at": 1790899200
    }
  },
  "timestamp": "2026-10-02T00:00:00.000Z"
}

A past positive expiry is stored; current permission evaluation must treat it as expired

Input · payload.json:

{
  "userId": "usr_managed",
  "orgId": "org_example",
  "appId": "app_topolo_campaigns",
  "permissionName": "campaigns.write",
  "effect": "allow",
  "expiresAt": 1790899199
}

CLI:

topolo actions validate app_topolo_auth.user_permission_overrides.create --data @payload.json --json
topolo actions plan app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json

MCP call after validation and plan review:

{
  "tool": "topolo_call_action",
  "arguments": {
    "actionId": "app_topolo_auth.user_permission_overrides.create",
    "input": {
      "userId": "usr_managed",
      "orgId": "org_example",
      "appId": "app_topolo_campaigns",
      "permissionName": "campaigns.write",
      "effect": "allow",
      "expiresAt": 1790899199
    },
    "confirm": true
  }
}

Captured local handler response · HTTP 201:

{
  "success": true,
  "message": "User permission override created",
  "data": {
    "override": {
      "id": "upo_7xT5fqF91l9B",
      "user_id": "usr_managed",
      "organization_id": "org_example",
      "app_id": "app_topolo_campaigns",
      "permission_name": "campaigns.write",
      "effect": "allow",
      "expires_at": 1790899199,
      "created_at": 1790899200
    }
  },
  "timestamp": "2026-10-02T00:00:00.000Z"
}

Zero expiry is stored and current evaluator treats zero as unbounded, not expired

Input · payload.json:

{
  "userId": "usr_managed",
  "orgId": "org_example",
  "appId": "app_topolo_campaigns",
  "permissionName": "campaigns.write",
  "effect": "allow",
  "expiresAt": 0
}

CLI:

topolo actions validate app_topolo_auth.user_permission_overrides.create --data @payload.json --json
topolo actions plan app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json

MCP call after validation and plan review:

{
  "tool": "topolo_call_action",
  "arguments": {
    "actionId": "app_topolo_auth.user_permission_overrides.create",
    "input": {
      "userId": "usr_managed",
      "orgId": "org_example",
      "appId": "app_topolo_campaigns",
      "permissionName": "campaigns.write",
      "effect": "allow",
      "expiresAt": 0
    },
    "confirm": true
  }
}

Captured local handler response · HTTP 201:

{
  "success": true,
  "message": "User permission override created",
  "data": {
    "override": {
      "id": "upo_nNFZ7808mFji",
      "user_id": "usr_managed",
      "organization_id": "org_example",
      "app_id": "app_topolo_campaigns",
      "permission_name": "campaigns.write",
      "effect": "allow",
      "expires_at": 0,
      "created_at": 1790899200
    }
  },
  "timestamp": "2026-10-02T00:00:00.000Z"
}

Unbounded override creation stays successful when audit persistence fails

Input · payload.json:

{
  "userId": "usr_managed",
  "orgId": "org_example",
  "appId": "app_topolo_campaigns",
  "permissionName": "campaigns.write",
  "effect": "allow",
  "expiresAt": null
}

CLI:

topolo actions validate app_topolo_auth.user_permission_overrides.create --data @payload.json --json
topolo actions plan app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json
topolo actions call app_topolo_auth.user_permission_overrides.create --data @payload.json --confirm --json

MCP call after validation and plan review:

{
  "tool": "topolo_call_action",
  "arguments": {
    "actionId": "app_topolo_auth.user_permission_overrides.create",
    "input": {
      "userId": "usr_managed",
      "orgId": "org_example",
      "appId": "app_topolo_campaigns",
      "permissionName": "campaigns.write",
      "effect": "allow",
      "expiresAt": null
    },
    "confirm": true
  }
}

Captured local handler response · HTTP 201:

{
  "success": true,
  "message": "User permission override created",
  "data": {
    "override": {
      "id": "upo_D-HNMcoMC0yj",
      "user_id": "usr_managed",
      "organization_id": "org_example",
      "app_id": "app_topolo_campaigns",
      "permission_name": "campaigns.write",
      "effect": "allow",
      "expires_at": null,
      "created_at": 1790899200
    }
  },
  "timestamp": "2026-10-02T00:00:00.000Z"
}

Verify the result

  • Read exact target org/app overrides and find the returned id with the requested effect/expiry. Check other duplicate/conflicting rows independently.

  • Read user_permissions.get in the exact organization/application and verify the intended protected request as the affected user. Expired and denied/role/app-access interactions need independent evaluation.

Errors and recovery

Undeclared application permission is a current repository server error

Captured local refusal · HTTP 500:

{
  "input": {
    "userId": "usr_managed",
    "orgId": "org_example",
    "appId": "app_topolo_campaigns",
    "permissionName": "campaigns.unknown",
    "effect": "allow"
  },
  "response": {
    "success": false,
    "error": {
      "message": "Permission does not exist for this service",
      "code": "SERVER_ERROR",
      "details": null,
      "timestamp": "2026-10-02T00:00:00.000Z"
    }
  }
}

Admin cannot create a row in another target membership organization

Captured local refusal · HTTP 401:

{
  "input": {
    "userId": "usr_managed",
    "orgId": "org_other",
    "appId": "app_topolo_campaigns",
    "permissionName": "campaigns.read",
    "effect": "allow"
  },
  "response": {
    "success": false,
    "error": {
      "message": "Access denied: You can only create overrides within your organization",
      "code": "AUTH_ERROR",
      "details": null,
      "timestamp": "2026-10-02T00:00:00.000Z"
    }
  }
}

Ordinary self member cannot create an administrator override

Captured local refusal · HTTP 401:

{
  "input": {
    "userId": "usr_managed",
    "orgId": "org_example",
    "appId": "app_topolo_campaigns",
    "permissionName": "campaigns.read",
    "effect": "allow"
  },
  "response": {
    "success": false,
    "error": {
      "message": "Admin access required",
      "code": "AUTH_ERROR",
      "details": null,
      "timestamp": "2026-10-02T00:00:00.000Z"
    }
  }
}

Direct platform role is not accepted by the organization-admin role gate

Captured local refusal · HTTP 401:

{
  "input": {
    "userId": "usr_managed",
    "orgId": "org_example",
    "appId": "app_topolo_campaigns",
    "permissionName": "campaigns.read",
    "effect": "allow"
  },
  "response": {
    "success": false,
    "error": {
      "message": "Admin access required",
      "code": "AUTH_ERROR",
      "details": null,
      "timestamp": "2026-10-02T00:00:00.000Z"
    }
  }
}

Unknown target membership is refused before SQL

Captured local refusal · HTTP 400:

{
  "input": {
    "userId": "usr_managed",
    "orgId": "org_missing",
    "appId": "app_topolo_campaigns",
    "permissionName": "campaigns.read",
    "effect": "allow"
  },
  "response": {
    "success": false,
    "error": {
      "message": "Target user does not belong to the provided organization",
      "code": "VALIDATION_ERROR",
      "details": {
        "orgId": true
      },
      "timestamp": "2026-10-02T00:00:00.000Z"
    }
  }
}

Missing override target is not provisioned

Captured local refusal · HTTP 404:

{
  "input": {
    "userId": "usr_missing",
    "orgId": "org_example",
    "appId": "app_topolo_campaigns",
    "permissionName": "campaigns.read",
    "effect": "allow"
  },
  "response": {
    "success": false,
    "error": {
      "message": "User not found",
      "code": "NOT_FOUND",
      "details": null,
      "timestamp": "2026-10-02T00:00:00.000Z"
    }
  }
}

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 fields and resolve the target organization membership through authorized identity discovery. An app permission name must be declared; stored-row metadata does not assign an app entitlement.

  • AUTH_ERROR (HTTP 401): Verify approved identity, organization-admin role, exact row organization authority and transport. Authenticated role/authority denial also uses 401; do not assume expiry or switch credential context automatically.

  • NOT_FOUND (HTTP 404): Refresh the exact user/organization/application row list. Missing target or already-deleted/exact-user-mismatched override is not a successful new deletion.

  • SERVER_ERROR (HTTP 500): Preserve the original error and rediscover current rows before reviewing a retry. Unknown permission names, token exceptions or storage errors are possible. Creation has no idempotency key; blindly retrying an unknown outcome can duplicate an override.

Recovery boundaries

Capture the prior stored rows and effective access. Deleting this exact newly returned id is a separate reviewed access decision: removing a deny may increase access. After an unknown response read rows before any retry; repeated create is not idempotent. Do not remove older valid grants or duplicate rows automatically.

Discovery and evidence

This record belongs to app_topolo_auth. Local tests prove real canonical override SQL, declared permission validation and exact stored-row organization authority with simulated tokens, target memberships and a permission catalog partition. They do not prove actual app assignment, effective access enforcement, live permissions, deployment or production completion.