Outcome
Read only the selected user/org preference scope; omitted scope means global *, without effective delivery resolution.
These examples prove local handler behavior with real isolated notification SQL and explicitly simulated identity dependencies.
Before you start
-
Verify pinned identity and the exact published action. Preference/address storage permits self or platform authority; organization admin alone cannot manage another user. Organization forbid rules require authority over the exact organization.
-
Discover the intended user/org independently. These storage handlers do not verify target existence and stored preference lists do not resolve effective delivery. Omitted preference org_id means global wildcard *, while replacement selects organization scope through query.org_id.
-
Before replacement capture the complete previous scope, review the full intended rows and inspect the validated plan. Empty rows clears explicit rules and may permit more delivery. Replacement is sequential and can leave partial state.
-
Channel verify only sets a stored flag and performs no possession challenge. Retain independent authorized ownership evidence before approving it; fixture addresses are unused and authorize no live verification or outreach.
Discover the target
topolo whoami --json
topolo apps --json
topolo actions get app_topolo_auth.user_notification_preferences.list --detail --json
Discover the authorized target user and exact organization. Read user_notification_preferences.list for each scope separately, user_notification_channels.list for stored channel_id values, or organization_notification_policy.list for the managed organization. Copy ids only from the exact selected scope. Effective delivery requires the published resolve action and delivery read-back separately.
Input fields
| Field | Required | Meaning |
|---|---|---|
userId | Yes | Existing intended Auth user from authorized discovery. User preference/address handlers allow self or platform authority; organization administrator alone does not authorize another user. These storage handlers do not independently verify target-user existence. |
org_id | No | Exact stored preference organization scope; omitted means the global wildcard scope *. Listing this scope does not merge global/organization rules or resolve effective delivery. |
Effects
- Reads only the selected user preference scope; no merging or delivery.
Interpret the response
Reads only the exact user/org scope, defaulting omitted org_id to *. Rows are ordered by event_type then channel and have boolean enabled; there is no pagination or effective delivery resolution.
An empty scope means no explicit rows there. It does not prove no user, no global rules, no verified primary email or disabled delivery. Organization admin alone cannot read another user preferences.
Worked examples
These are complete raw target-handler responses, not CLI/gateway envelopes or live authorization proof. Synthetic users, organizations and unused addresses exist only in isolated fixtures. Discover real intended targets and review effects separately. Save only reviewed input as payload.json; validate and inspect the unchanged plan before an authorized call. --confirm assumes intended approval. Cookie/Bearer identities must consistently select the intended principal. Stored rows, ownership evidence, effective policy and actual delivered messages are separate outcomes. No fixture sends a message or performs an address challenge.
Omitted org_id reads only the global wildcard preference scope
Input · payload.json:
{
"userId": "usr_self"
}
CLI:
topolo actions validate app_topolo_auth.user_notification_preferences.list --data @payload.json --json
topolo actions plan app_topolo_auth.user_notification_preferences.list --data @payload.json --json
topolo actions call app_topolo_auth.user_notification_preferences.list --data @payload.json --json
MCP call after validation and plan review:
{
"tool": "topolo_read_action",
"arguments": {
"actionId": "app_topolo_auth.user_notification_preferences.list",
"input": {
"userId": "usr_self"
}
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "Notification preferences fetched",
"data": {
"userId": "usr_self",
"orgId": "*",
"rows": []
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Global preference read excludes organization-scope rows
Input · payload.json:
{
"userId": "usr_self"
}
CLI:
topolo actions validate app_topolo_auth.user_notification_preferences.list --data @payload.json --json
topolo actions plan app_topolo_auth.user_notification_preferences.list --data @payload.json --json
topolo actions call app_topolo_auth.user_notification_preferences.list --data @payload.json --json
MCP call after validation and plan review:
{
"tool": "topolo_read_action",
"arguments": {
"actionId": "app_topolo_auth.user_notification_preferences.list",
"input": {
"userId": "usr_self"
}
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "Notification preferences fetched",
"data": {
"userId": "usr_self",
"orgId": "*",
"rows": [
{
"user_id": "usr_self",
"organization_id": "*",
"event_type": "*",
"channel": "push",
"enabled": false,
"updated_at": 1790971419
},
{
"user_id": "usr_self",
"organization_id": "*",
"event_type": "campaigns.send_completed",
"channel": "email",
"enabled": true,
"updated_at": 1790971419
}
]
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Explicit organization read returns its stored toggles without effective delivery resolution
Input · payload.json:
{
"userId": "usr_self",
"org_id": "org_example"
}
CLI:
topolo actions validate app_topolo_auth.user_notification_preferences.list --data @payload.json --json
topolo actions plan app_topolo_auth.user_notification_preferences.list --data @payload.json --json
topolo actions call app_topolo_auth.user_notification_preferences.list --data @payload.json --json
MCP call after validation and plan review:
{
"tool": "topolo_read_action",
"arguments": {
"actionId": "app_topolo_auth.user_notification_preferences.list",
"input": {
"userId": "usr_self",
"org_id": "org_example"
}
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "Notification preferences fetched",
"data": {
"userId": "usr_self",
"orgId": "org_example",
"rows": [
{
"user_id": "usr_self",
"organization_id": "org_example",
"event_type": "auth.password.changed",
"channel": "email",
"enabled": false,
"updated_at": 1790971419
}
]
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Read after replacement failure exposes the actual partial scope
Input · payload.json:
{
"userId": "usr_self"
}
CLI:
topolo actions validate app_topolo_auth.user_notification_preferences.list --data @payload.json --json
topolo actions plan app_topolo_auth.user_notification_preferences.list --data @payload.json --json
topolo actions call app_topolo_auth.user_notification_preferences.list --data @payload.json --json
MCP call after validation and plan review:
{
"tool": "topolo_read_action",
"arguments": {
"actionId": "app_topolo_auth.user_notification_preferences.list",
"input": {
"userId": "usr_self"
}
}
}
Captured local handler response · HTTP 200:
{
"success": true,
"message": "Notification preferences fetched",
"data": {
"userId": "usr_self",
"orgId": "*",
"rows": [
{
"user_id": "usr_self",
"organization_id": "*",
"event_type": "campaigns.send_completed",
"channel": "email",
"enabled": true,
"updated_at": 1790971419
}
]
},
"timestamp": "2026-10-02T00:00:00.000Z"
}
Verify the result
-
Require userId/orgId to match the selected target and scope; omitted scope must return *. Inspect ordered stored rows.
-
Read global and organization scopes separately, then use the published resolve action for the intended event. Stored rows alone do not prove deliverability.
Errors and recovery
Organization admin alone cannot read another user preferences
Captured local refusal · HTTP 401:
{
"input": {
"userId": "usr_other"
},
"response": {
"success": false,
"error": {
"message": "Access denied",
"code": "AUTH_ERROR",
"details": null,
"timestamp": "2026-10-02T00:00:00.000Z"
}
}
}
Anonymous preferences read is refused
Captured local refusal · HTTP 401:
{
"input": {
"userId": "usr_self"
},
"response": {
"success": false,
"error": {
"message": "Access token required",
"code": "AUTH_ERROR",
"details": null,
"timestamp": "2026-10-02T00:00:00.000Z"
}
}
}
Invalid token is an authentication error
Captured local refusal · HTTP 401:
{
"input": {
"userId": "usr_self"
},
"response": {
"success": false,
"error": {
"message": "Invalid access token",
"code": "AUTH_ERROR",
"details": null,
"timestamp": "2026-10-02T00:00:00.000Z"
}
}
}
Published recovery guidance
-
AUTH_ERROR(HTTP 401): Verify pinned identity. If authenticated, respect self/platform user authority or exact organization management denial; organization ownership alone does not authorize another user address/preferences. Do not assume OAuth expiry or change context automatically. -
VALIDATION_ERROR(HTTP 400): Correct the reported fields only. Use unique event/channel pairs and valid channel-specific addresses. Validate and review the exact complete replacement again before approval. -
NOT_FOUND(HTTP 404): Refresh the target user channels list. A row for another user is not authorized by a known channel id; repeated delete is already absent, not a new successful deletion. -
SERVER_ERROR(HTTP 500): Preserve the result and read the exact stored scope before any new write. A replacement may have deleted old rules and inserted only some new ones; review finishing the intended set or restoring the captured set as a new approved replacement.
Recovery boundaries
Read-only stored metadata. Refresh exact scope after separately approved changes; effective resolution and delivery require their own verification.
Discovery and evidence
This record belongs to app_topolo_auth. Fixtures prove actual NotificationPreferencesController and AuthController authority, validation, canonical notification SQL, duplicate/address identity and sequential replacement failure boundaries. Authenticated identities and unused targets/addresses are simulated and second-insert SQL failures injected. No live storage, ownership challenge, notification, deployment or production publication is proved.