Organizations
An organization (org) is the tenant that owns your data. Templates, template versions, renders, API keys, signing certificates, categories, and the audit/usage logs all belong to an organization — never to a bare user account. This is the boundary a subscription will attach to once billing ships.
This page describes how organizations behave today, in both the web UI and the HTTP API. For the machine-facing API reference, see the API reference.
The ownership model
- Every account owns exactly one organization. It is created automatically
the moment you register (local signup, the initial
/auth/setupadmin, or the first OIDC login). You never create it by hand, and you can never own a second one. - Membership is many-to-many. Beyond your own org, you can be invited into other organizations to collaborate — for example, a colleague adds you to their company's org so you can share its templates.
- Each account has exactly one active organization. Every request you make
resolves to
(you, your active org), and all data you see and create is scoped to that active org. For API-key requests, the org is the one the key was pinned to when it was created.
Roles
Within an organization, each member has one of three roles:
| Role | Who | Can do |
|---|---|---|
owner | The account that created the org. Exactly one, permanent, cannot be transferred or removed. | Everything an admin can, and is the future billing owner. Manages categories. |
admin | Invited collaborators with management rights. | Rename the org, invite/remove members, change member roles, manage resources. |
member | Invited collaborators. | Use the org's templates and resources. |
The owner role cannot be changed or removed, and an owner cannot leave their own organization.
What the account page exposes
Open Account → Organization. You will see:
- Current organization — its name. Owners and admins get a Rename button.
- Members — everyone in the org with their role. Owners and admins can:
- Add member — by the email of an existing account, as
adminormember. (Email invites to people without an account are not supported yet.) - Change role between
adminandmember. - Remove a non-owner member.
- Add member — by the email of an existing account, as
- Leave organization — shown only for orgs you were invited into (not your own owned org). Leaving an org you are currently active in repoints your active org back to the organization you own.
There is no organization switcher
You cannot switch your active organization from the app. Your active org is set once, at signup, to the organization you own, and it stays there. The only way it changes is when you leave an org you were invited into — at which point it falls back to your owned org.
This is deliberate for the current phase. The earlier in-app switcher (and its
POST /api/orgs/active endpoint) has been removed.
Working with more than one organization
If you genuinely need separate sets of templates, categories, and (future) billing — for two different companies, say — you have two options today:
- Be invited into the other org. If a colleague owns it, they add you as a member from their account page. You then collaborate inside that org. Note that because you cannot switch active orgs, this works best when the invited org is the one you primarily work in.
- Create a separate account with a different email. Each account owns its own organization, with its own templates, categories, API keys, and (once billing lands) its own subscription. This is the supported path for keeping two companies' data fully separate under one person.
How a request resolves to an organization
Every request that touches org-scoped data is filtered by exactly one org id, resolved from how you authenticated:
- Session (cookie login) → your active organization. The web UI always works inside your active org.
- API key → the organization the key was pinned to when it was created. A key is bound to one org for life; it never follows your active org around. (Because keys are created from the dashboard inside your active org, a key is pinned to whatever your active org was at creation time — today that is always your owned org.)
Endpoints that require an org return 403 no active organization if the
caller has none.
What belongs to an organization
Once a request resolves to an org, these resources are scoped to it. Data in one org is never visible from another.
Templates and versions
Templates, their versions, renders, and scaffolds all belong to the org that created them. Reading, rendering, renaming, or deleting a template you don't own returns 404 (the API does not reveal that the resource exists in another org).
Template deduplication is per-org: uploading a file byte-identical to one
your org already has returns the existing template; another org uploading the
same bytes gets its own independent copy. The hash-first endpoints
(/api/render/{hash}, /api/templates/by-hash/{hash}/versions) look up the
hash within the API key's org — the same hash can map to different templates
in different orgs.
Signing certificates
PKCS#12 signing certificates are stored per-org (encrypted at rest). Each org has its own set and its own default certificate. A cert uploaded in one org is invisible to and unusable by another. The same certificate bytes can be registered independently in two different orgs.
API keys
Each organization has exactly one key pair — a live and a test key —
shared by the whole org and pinned to it for life. An owner or admin generates the
pair once and can later regenerate either key independently; there is no
per-user or multi-pair creation. The key list (Account → API Keys) shows
only the active org's keys, with no cross-org view. See the
API reference for the lifecycle endpoints.
Categories
Categories are per-org. Any member can list them, but only the org owner can create or delete a category (this replaced the old admin-only rule).
Audit and usage logs
Every audit-log and usage-log row carries the org_id it happened in.
GET /api/audit, GET /api/usage/summary, and GET /api/usage/timeseries only
ever return rows for your active org, so each org sees its own activity and
aggregate stats in isolation. The usage endpoints also accept an optional
?env=live|test to scope the stats to one environment.
API surface
All organization-management endpoints require a session (cookie login) and an
active organization; they are not part of the API-key machine surface. :id is
the organization id.
| Method | Path | Min role | Purpose |
|---|---|---|---|
| GET | /api/orgs | member | List the orgs you belong to, plus your active_org_id. |
| PATCH | /api/orgs/:id | admin | Rename the org. |
| POST | /api/orgs/:id/leave | member | Leave an org (owners cannot leave). |
| GET | /api/orgs/:id/members | member | List members. |
| POST | /api/orgs/:id/members | admin | Add an existing user by email as admin/member. |
| PATCH | /api/orgs/:id/members/:userId | admin | Change a member's role. |
| DELETE | /api/orgs/:id/members/:userId | admin | Remove a non-owner member. |
GET /auth/me includes your active_org_id so the UI can scope itself without
an extra round-trip.
There is no
POST /api/orgs/activeendpoint — active-org switching was removed (see above).
Backward compatibility
A single-user install keeps working with zero manual steps: the existing
"default" tenant is migrated into a real organization, the first/oldest (or
first admin) user becomes its owner, and any other users join as members.
Users without an active org are backfilled to default.