Ulsav Docs

Capabilities

How Capabilities, Roles, RoleAssignments, and Scopes compose into authority, and where API keys differ

Authority is composed from four nouns and one rule.

NounWhat it is
CapabilityOne named action, such as events.publish or payments.refund
RoleAn Organization-defined named set of Capabilities
RoleAssignmentOne Role granted to one Member at one Scope
ScopeThe resource subtree a grant applies to

The rule: effective Capabilities are the additive union of every active assignment that applies. There is no deny, no priority order, and no nested Role.

Scopes and inheritance

Allowed scopes are Organization, EventGroupSeries, EventSeries, EventGroup, Event, and CheckInSession. Three ancestry paths exist:

Diagram source
flowchart TD
  O[Organization] --> GS[EventGroupSeries]
  GS --> G[EventGroup]
  G --> GE[Event]
  O --> RS[EventSeries]
  RS --> SE[Event]
  O --> ST[Standalone Event]
  GE --> C[CheckInSession]
  SE --> C
  ST --> C

A grant at any level reaches everything beneath it on its path. An Event that belongs to both an EventGroup and an EventSeries inherits from both, plus the Organization and the Event itself.

EventSession and Pass operations inherit Event scope, so a Pass is never a grant target.

Scope is stored, but domain operations do not yet honour it. Role assignment and API-key issuance resolve a grant against the target ancestry. Every domain service — publishing, registration, payments, requirements, check-in — authorizes from Organization-scoped assignments only. An Event-scoped grant therefore does not currently authorize work on that Event.

Moving an Event between groups changes the ancestry immediately, because ancestry is read live. The move itself is an ordinary Event update: no confirmation and no audit record.

The catalog

Capability names are stable and versioned in code, and the catalog is exposed for the Role editor.

FamilyExamples
Organization and accessorganization.read, organization.update, members.invite, roles.assign, domains.verify, sso.manage, api_keys.create
Publishingevents.read, events.create, events.update, events.publish, events.cancel, event_sessions.delete, passes.update, passes.cancel, coupons.manage
Participationregistrations.read, registrations.create, registrations.cancel, participants.remove, registration_approvals.review
Moneyorders.read, orders.export, payments.refund, ledger.read, payouts.manage
Admissioncheckin_sessions.create, checkin_sessions.operate
Reporting and gatingaudit.read, notifications.read, requirements.manage
Integrationwebhooks.create, webhooks.replay

Seeded Roles

Organizations begin with reviewed Roles; custom Roles use the same catalog.

RoleReachesNotes
OwnerEverythingProtected wildcard; its invariants cannot be edited away
AdministratorThe whole catalogThe practical "run the Organization" Role
Event ManagerEvents, Sessions, Passes, coupons, applications, Registrations, Participants, approvals, Orders read and export, Check-InNo refunds, ledger, payouts, or audit
Finance ViewerOrganization read, Orders, payments, ledger, payouts, exportsRead-only
Check-in OperatorOrganization read, Check-In read and operate, PassIssue read, Teams readThe narrowest door Role

Wildcards are reserved for protected Roles and platform operations; platform capabilities are never assignable by an Organization user.

Separations that exist on purpose

Some Capabilities are split because the money or the risk differs.

SeparationWhy it exists
audit.read from organization.readReading the Organization does not grant reading who changed what.
registrations.transfer from registrations.switchTransferring who holds a Registration on the same Pass is money-neutral. Moving a Registration to another Pass is a switch; a switch that credits also calls the refund path, so it additionally needs payments.refund. A switch that charges needs no extra capability.
participants.transferCatalogued for person-in-place roster moves. No operation checks it today: a roster replacement checks participants.remove for an organizer, or the buyer's own authority.
Report and Insight readsGated by the domain read Capability of the data they present: Orders for revenue, checkin_sessions.read for attendance, events.read for traffic and attribution, audit.read for the activity log.

Human principals and API-key principals

A browser session acts as a human Account. An API key acts as itself.

Account (Member)

  • Authority source — Organization-scoped RoleAssignments
  • Lifetime — follows membership
  • Narrowing — change the Role or assignment
  • Key management — allowed with api_keys.read, .create, or .revoke
  • Expiry — not applicable

API key

  • Authority source — its own Capability and Scope set
  • Lifetime — independent of the issuer once created
  • Narrowing — change its capabilities, change its Scope, revoke, or reissue
  • Key management — forbidden, whatever it declares
  • Expiry — optional; a key with no expiry never expires

Four rules follow from that difference.

  • Issue time. A key's Capabilities and Scope must be a subset of the issuer's authority at that Scope.
  • Afterwards. The issuer's departure or demotion does not revoke it. api_keys.read can inspect it, and api_keys.revoke can revoke it independently of the issuer.
  • Suspension. Organization suspension rejects every key; API_KEYS_SUSPENDED is a platform kill-switch.
  • Key management. Session-only and human; no key can create, read, update, or revoke a key.

How authorization is applied

An API operation resolves the principal, the explicit Organization, the Capability, and the target resource; an API-key request also resolves the key's declared Scope. The server decides; UI visibility is convenience only.

API-key authorization checks the key's Capability and walks the target's ancestry to confirm its Scope covers the resource. Human authorization checks the Organization-scoped RoleAssignments only, as described above.

Capabilities are recomputed from the database on each request; there is no capability cache to invalidate.

Unauthorized reads of another tenant's resource are indistinguishable from missing ones.

Does `organization.read` let me read the audit log?

No. audit.read is separate and is not implied by reading the Organization.

What happens to an API key when the person who issued it leaves?

It survives. A key's authority becomes independent of the human issuer after creation; administrators holding api_keys.read can inspect it and api_keys.revoke can revoke it.

Can an API key create, read, or revoke another API key?

No. Key management is session-only and human, whatever the key declares.

Do I need a separate permission to read revenue?

No separate taxonomy exists: report and Insight reads are gated by the domain read Capability of the data they present, so revenue needs Orders access and attendance needs Check-In access.

On this page