AgentOwl

Roles and Permissions

Operator roles, tenant scoping, authentication methods, the audit trail, and authorization errors in AgentOwl.

AgentOwl uses a role-based access control (RBAC) model to govern who can perform operations across the platform. Every authenticated API request carries an operator identity, a role, and a tenant scope that together determine what the caller is allowed to do.

This guide covers the two operator roles, how tenant scoping works, the supported authentication methods, and the audit trail that records every administrative action. In the dashboard, tenants are called workspaces.


Roles overview

AgentOwl defines two human-operator roles. There are no end-user or read-only roles in v1 — the system is designed for operators and automation, not end-user inboxes.

RoleScopeIntended for
platform-adminGlobal — all tenantsInfrastructure operators who manage the entire AgentOwl deployment
tenant-adminScoped — explicitly listed tenants onlyTeam leads or service owners who manage their own tenants

Platform admin

A platform admin has unrestricted access to every resource and operation in the system. The role is designed for the small number of operators who provision and maintain the AgentOwl deployment itself.

Platform admins can:

  • Create, list, read, and delete any tenant.
  • Create and manage bindings, endpoints, routes, and policies for any tenant without being listed in a tenant scope.
  • Trigger worker-role deployments (POST /v1/deployments).
  • Query the audit log across all tenants or for a specific tenant.
  • Publish, validate, diff, and roll back runtime configuration.
  • Trigger provider reconciliation and view drift reports.
  • View dead-letter entries and replay events.

When a platform admin authenticates, tenant_scope is null, which the authorization layer interprets as "access all tenants."

Tenant admin

A tenant admin has full administrative access, but only within the tenants explicitly listed in their tenant_scope array. Any request that touches a tenant outside that list receives a 403 Forbidden response.

Tenant admins can:

  • Create tenants, up to the limit of their account's plan (see Plan limits). New tenants are added to their scope.
  • List and read tenants they are scoped to.
  • Create and manage bindings, endpoints, routes, and policies within their authorized tenants.
  • Query the audit log for their own tenants (the tenant_id query parameter is required).
  • View events, dead letters, and replay events within their tenants.

Tenant admins cannot:

  • Exceed their plan's tenant limit.
  • Trigger worker deployments.
  • Query the audit log without specifying a tenant_id.
  • Access any resource belonging to a tenant outside their scope.

Tenant scoping

Multi-tenancy is the core isolation boundary in AgentOwl. Every resource — bindings, endpoints, routes, policies, events — belongs to exactly one tenant. The authorization layer enforces this on every request.

How it works

Each authenticated request carries an AuthContext with a tenant_scope field:

platform-admin  →  tenant_scope: null          (global access)
tenant-admin    →  tenant_scope: ["t_abc123"]  (scoped access)

Before any tenant-scoped operation executes, the system calls requireTenantAccess(auth, tenantId):

  1. If the caller is a platform-admin, access is granted immediately.
  2. If the caller is a tenant-admin and the target tenantId is in their tenant_scope array, access is granted.
  3. Otherwise, the request is rejected with:
{
  "error": {
    "code": "forbidden.tenant_scope",
    "type": "forbidden",
    "message": "Operator does not have access to tenant 't_xyz'"
  }
}

Scoping in list operations

When a tenant-admin lists resources, the query is automatically filtered to only return results from their authorized tenants. For example, GET /v1/tenants returns only the tenants in the caller's scope — not the full list.


Permission matrix

The table below shows which operations each role can perform.

OperationPlatform adminTenant admin
Tenants
Create tenantYesYes, within plan limits
List tenantsAllScoped only
Get tenantAnyScoped only
Delete tenantAnyScoped only
Bindings
Create bindingAny tenantScoped only
List bindingsAny tenantScoped only
Get bindingAny tenantScoped only
Delete bindingAny tenantScoped only
Endpoints
Create endpointAny tenantScoped only
List endpointsAny tenantScoped only
Get / update endpointAny tenantScoped only
Delete endpointAny tenantScoped only
Routes
Create routeAny tenantScoped only
List routesAny tenantScoped only
Get / update routeAny tenantScoped only
Delete routeAny tenantScoped only
Policies
Create policyAny tenantScoped only
List policiesAny tenantScoped only
Get / update policyAny tenantScoped only
Delete policyAny tenantScoped only
Configuration
Validate configYesYes (scoped)
Diff configYesYes (scoped)
Publish configYesYes (scoped)
Rollback configYesYes (scoped)
Deployments
Trigger deploymentYesNo
List deploymentsYesNo
Audit log
Query all tenantsYesNo
Query own tenantYesYes
Events
List / get eventsAny tenantScoped only
Replay eventAny tenantScoped only
Operations
Trigger reconciliationYesYes (scoped)
View driftYesYes (scoped)
View dead lettersAny tenantScoped only
Ingress (webhooks)
Receive email / SMS / voiceN/A — provider-verified, no operator auth

Plan limits

Tenant admins are limited by their account's plan. New accounts start on the Free plan.

PlanWorkspaces (tenants)Messages per dayBindings
Free11003
Pro1010,00050
EnterpriseCustomCustomCustom

These plan limits describe the allowances for each account plan. Check purchase and upgrade availability in Admin and phone-number availability for your account. Phone provisioning requires an active paid entitlement. If a purchase, upgrade or number setup action is unavailable, contact support@agentowl.dev. Sandbox checkout does not grant production privileges.

Free email sending requires reviewed pilot enablement and an exact address that is ready. Enabled accounts have a separate account-wide limit of 10 recipient submissions per UTC day and a burst limit of five new recipients per rolling minute. The Messages per day column above describes inbound platform messages, not outbound recipient allowance.

Phone numbers (SMS and voice bindings) require an active paid entitlement; a stored plan label alone does not establish provisioning eligibility.


Authentication methods

AgentOwl supports three authentication methods. API keys and OAuth access tokens use the Authorization: Bearer <token> header; the dashboard uses a session cookie.

API key

A pre-shared API key stored as a SHA-256 hash in the D1 api_keys table. The raw token is never persisted.

Authorization: Bearer aok_<48-hex>
  • Format: aok_<48-hex>
  • Storage: SHA-256 hash in D1 api_keys table
  • Created via: dashboard (Settings → API Keys → Create Key) or the API. The key is shown once; copy it immediately.
  • AuthContext: auth_method: "api-key"
  • Scope: API keys receive wildcard scope ["*"] and act on behalf of the account that created them.
  • Revocation: Revoke in Settings → API Keys.

When to use: CLI scripts, CI/CD pipelines, programmatic access.

OAuth 2.1 access token

An opaque access token obtained via the OAuth 2.1 authorization code flow with PKCE or the device code flow. Stored as a SHA-256 hash in the D1 oauth_tokens table.

Authorization: Bearer oat_<48-hex>
  • Format: oat_<48-hex> (opaque, not JWT)
  • Storage: SHA-256 hash in D1 oauth_tokens table
  • Obtained via: authorization code flow with PKCE or device code flow
  • AuthContext: auth_method: "oauth"
  • Scoped: tokens carry specific scopes (e.g., tenants:read, endpoints:write)
  • Revocation: POST /oauth/revoke (RFC 7009)

agentowl login without --token uses the device code flow and stores an oat_ token.

When to use: MCP clients, third-party integrations, scoped automation.

Dashboard session

When you sign in to the dashboard with GitHub or Google, the browser holds a signed session cookie. Requests without an Authorization header fall back to that cookie.

  • AuthContext: auth_method: "session"
  • Used by: the admin dashboard only. Use an API key or OAuth token for anything else.

Internal worker roles

Worker deployment environments are separate from human operator roles. The main production Worker combines the API, HTTP ingress, queue consumer, and maintenance tasks. The recorded agent-work release also deployed a separate Cloudflare email Worker with no HTTP routes. Other role environments are templates that need their own bindings and secrets before use.

EnvironmentPurposeConfiguration scope
Default main WorkerAPI, SMTP/SMS/voice ingress, dispatch, reconciliation and maintenanceD1, runtime and secret KV, Queue producer/consumer, private attachment R2, outbound fetch
email-ingressCloudflare Email Routing handlerD1, runtime KV, Queue producer, private attachment R2; no HTTP routes
sms-ingress, voice-ingressOptional separate HTTP ingress deploymentsOperator must provision the required bindings and secrets
queue-consumer, reconcilerOptional separate processing deploymentsOperator must provision the required bindings and secrets

Ingress persists events to D1, and the API can make outbound provider calls. The current deployment does not enforce the earlier proposed separation in which ingress had no database access and the API had no provider access. Tenant authorization is enforced by application checks; deployment environment names alone do not restrict handlers or outbound network access. See Architecture.

Only platform-admin operators can trigger deployments via POST /v1/deployments.


Audit trail

Every authenticated API request is logged to the audit_log table with full actor attribution.

What is recorded

FieldDescription
audit_idUnique log entry ID
timestampISO 8601 timestamp
operator_idWho performed the action
roleplatform-admin or tenant-admin
auth_methodapi-key, oauth, or session
tenant_idTarget tenant (if applicable)
actionHTTP method + path (e.g., POST /v1/bindings)
resource_typetenant, binding, endpoint, route, policy, etc.
resource_idID of the affected resource
request_idCorrelation ID for the request
status_codeHTTP response status
ip_addressCaller IP address
user_agentCaller user-agent string

Querying the audit log

GET /v1/audit-log?tenant_id=t_abc123&since=2026-03-01T00:00:00Z&limit=100

Access rules:

  • Platform admin: Can query with or without tenant_id. Omitting it returns entries across all tenants.
  • Tenant admin: Must include tenant_id, and it must be in their scope. Omitting it returns a validation error.

Error responses

All authorization failures return structured error responses with machine-readable codes.

HTTP statusError codeMeaning
401auth.missing_credentialsNo Authorization header
401auth.invalid_schemeHeader does not use Bearer scheme
401auth.missing_tokenBearer token is empty
401auth.invalid_tokenToken is invalid or expired
403forbidden.tenant_scopeOperator lacks access to the target tenant
403forbidden.platform_admin_requiredOperation requires platform-admin role
403forbidden.no_accountThe API key is not linked to an account
403forbidden.account_inactiveThe account is not active
429rate_limit.tenant_limitCreating a tenant would exceed the plan's tenant limit

All error responses include a request_id for correlation with the audit log and operational logs.


Best practices

For platform admins

  • Use scoped OAuth tokens for automation. API keys carry wildcard scope; prefer OAuth access tokens with only the scopes a pipeline needs.
  • Rotate keys regularly. Create a new API key, switch automation to it, then revoke the old one.
  • Monitor the audit log. Regularly review GET /v1/audit-log for unexpected actions or unfamiliar operator IDs.

For tenant admins

  • Name your API keys. Give each key a name that identifies where it is used (for example ci-deploy) so you can revoke the right one.
  • Use OAuth 2.1 tokens for agents. Use scoped OAuth access tokens for MCP clients and automation so you can revoke them independently via POST /oauth/revoke.
  • Use dry-run for destructive operations. Tenant deletion supports ?plan=true to preview what will be removed before executing.

For automation and MCP clients

  • Prefer scoped OAuth tokens over API keys. OAuth 2.1 access tokens carry specific scopes and can be revoked individually, providing better security than wildcard API keys.
  • Handle token revocation gracefully. If your token is revoked, surface the error to the operator and re-authenticate.
  • Include a descriptive User-Agent. The audit log records it, making it easier to identify which automation performed an action.

Quick reference

Auth context shape

interface AuthContext {
  operator_id: string;
  role: "platform-admin" | "tenant-admin";
  tenant_scope: string[] | null;   // null = global access
  auth_method: "api-key" | "oauth" | "session";
  scope?: string[];
}

Role decision tree

Is the operation platform-level? (deployments, unscoped audit log)
  ├── Yes → Requires platform-admin
  └── No  → Is a tenant_id involved?
              ├── Yes → requireTenantAccess() checks:
              │           ├── platform-admin → allowed
              │           └── tenant-admin   → allowed if tenant_id in scope
              └── No  → Authenticated access sufficient

On this page