Organizations
An organization is the tenancy boundary. Every record in TagShape belongs to exactly one, and nothing reaches across.
What an organization holds
| Held in | Records |
|---|---|
| Account API | Users, groups and their permissions, identity providers, branding, account patterns, API keys |
| Library set API | Tag groups, tags, library sets, variables, values |
| Audit API | Log entries |
How the boundary is enforced
Every query is filtered by the org claim on the caller's JWT. The consequence worth knowing is that an ID
belonging to another organization does not produce a 403 — it produces a 404. As far as the query is
concerned the record is not there, and saying otherwise would confirm that an ID exists.
The exceptions are the four global organization permissions (OrganizationList, OrganizationRead,
OrganizationCreate, OrganizationUpdate, OrganizationDelete), which by their nature reach beyond a single
organization. See Permissions model for what global means and who may hand one out.
Identity providers
An organization brings its own. Each provider record holds the client ID, client secret and any tenant identifier needed to run a login, and the login page offers whichever ones the organization has configured. Microsoft Entra ID and Google are supported. See Identity providers.
Credentials can be tested before they are saved, via POST /organizations/identityproviders/check — the check
runs server-side rather than from the browser, because Entra's token endpoint allows the CORS preflight and then
omits the Access-Control-Allow-Origin header from the response, so a browser never gets the reply.
Account patterns
An organization can say that email addresses matching a pattern may have an account created for them
automatically at first login. When someone signs in successfully with a provider but has no account,
POST /users/provision checks their address against the organization's patterns; a match creates the account,
and no match refuses the login.
A pattern that cannot be compiled is logged and passed over rather than failing the login.
Branding
An organization can supply a logo and custom CSS, both served to the login page by
GET /organizations/{id}/signin. The CSS is filtered against an allow-list of rules rather than injected
wholesale.
Which organization is being signed in to
The auth UI resolves the organization per request. Where nothing in front of the deployment sets it, the
x-tagshape-org header names it — that is how the local Aspire host runs it.
Deleting an organization
DELETE /organizations/{id} needs the global OrganizationDelete. A disabled organization (active: false)
stops answering GET /organizations/{id}/signin — an ID naming a disabled organization and an ID naming no
organization get the same 404, because in both cases there is nothing there to sign in to.