Skip to main content
Authority in Duro comes from two places: the credential you authenticate with, and (for dashboard users) the role that credential maps to.

Machine credentials act with full scope

A secret key (sk_…) or an OAuth token carries scope * — full authority within its one tenant and one mode. There’s no per-key permission slicing; a server integration is trusted to act as the merchant. Its blast radius is bounded by the tenant boundary and the mode, not by scopes. A publishable key (pk_…) is the exception: it can only start checkout sessions, never read or mutate data, so it’s safe in the browser or the inline SDK.

Dashboard members are role-gated

Human users in the dashboard hold one of five roles. Every write endpoint is gated by a PermissionGuard.
RoleCan do
ownerEverything, including tenant management. Cannot be removed or demoted.
adminEverything except tenant management.
developerPlans, customers, subscriptions, invoices, API keys, webhooks, appearance, read.
financePlans, customers, subscriptions, invoices, refunds, read.
read_onlyRead only.
Team changes (invite, role change, removal) are written to an audit log. Owner and admin roles are flagged for 2FA enforcement.

The permission set

The guard checks named permissions, derived from the role: tenant:manage · member:manage · plan:write · customer:write · subscription:write · invoice:write · refund:write · apikey:manage · webhook:manage · appearance:manage · read A 403 with code forbidden means the credential authenticated but lacks the permission for that route — distinct from a 401, which means the credential itself was missing or invalid. See Errors.

Customer identity tokens

The customer-facing endpoints (/identity/*, /portal/*) use an identity token, which resolves to a person, not a tenant. It can read and manage only that customer’s own data, and is rejected outright by the merchant /v1 API.