JWT Authentication
When you run a self-managed Outpost, your application signs its own JWT (JSON Web Tokens) and the Outpost verifies each token against a public key you configure in the Ecosystem Hub. Because you sign the tokens yourself, you are responsible for including the claims that identify who a request belongs to.
This page describes the claims a self-managed Outpost expects, how to map them to the names your tokens actually use, and what happens when a required claim is missing.
This applies to self-managed integrations only. Cartesian managed integrations do not need any of this — the Cartesian Cloud signs their tokens and derives every identity claim server-side.
Claims
Cartesian scopes a caller's data — such as its installed offerings and transactions — to an organization: one of your customers. To do this reliably, the Outpost reads an immutable organization identifier from every token.
Whether a request needs a sub_organization_id depends on how your product is structured. Suppose you are a WordPress hosting company: one of your customers — an organization — may run several separate websites through you, each with its own users, transactions, and installed offerings. Each of those websites is a sub-organization, and a request acting for one website carries that site's sub_organization_id. A SaaS company is usually different: each customer has a single install and transaction base across the whole product, so there is no finer scope to distinguish and the sub_organization_id is omitted.
This gives two levels of scope:
- Organization (
organization_id, required) — one of your customers, and the primary scope for the caller's data. A request without a sub-organization is scoped to the organization as a whole. - Sub-organization (
sub_organization_id, optional) — a single site or install operated by that organization. A request carrying one is scoped to that specific site. Use it only when an organization runs several sites whose data must stay separate; omit it otherwise.
Your signed JWT must carry the following claims:
| Claim | Required | Purpose |
|---|---|---|
organization_id | Required | An immutable identifier for the organization the request belongs to; it may be opaque. This is the primary identity key for the caller's scoped data. |
user_id | Required | A unique identifier for the end user making the request. Can be any unique value, including an opaque handle. |
organization_name | Optional | A human-readable name for the organization. Used for display and telemetry. |
sub_organization_id | Optional | An immutable identifier for a finer scope within an organization — a single site or install. Include it only when the caller represents one specific site. |
Choosing an organization_id
The value you send for organization_id must be immutable, and may be opaque:
- Immutable (required) — it must never change for the lifetime of the organization. Because your scoped data is keyed on this value, changing it orphans the organization's existing data. Do not derive it from anything a customer can rename, such as a display name.
- Opaque (optional) — the value does not need to be human-readable. A stable internal identifier such as a database primary key or a UUID is fine, but a readable value is equally acceptable as long as it is immutable.
The same rules apply to sub_organization_id when you send one: it must be immutable for the lifetime of the site it identifies.
Configuring claim names
The Outpost reads each identity from whichever claim name you configure. You set these in the Ecosystem Hub under Settings > Outpost Settings, in the Outpost Configuration section, which has a field for each claim alongside the public key used to verify your tokens:
| Field | Identity | Pre-filled claim name |
|---|---|---|
| Organization ID | organization_id (required) | organization_id |
| User ID | user_id (required) | user_id |
| Organization Name | organization_name (optional) | organization_name |
| Sub-Organization ID | sub_organization_id (optional) | sub_organization_id |
Your Outpost's fields come pre-filled with the claim names Cartesian's managed integration uses (shown above). If your application signs tokens with different claim names, change each field to match. For example, set User ID to sub if you carry the user identity in the standard JWT sub claim.
Rejected tokens
A self-managed Outpost distinguishes two failure modes for the required identity claims (organization_id and user_id):
401 Unauthorized— the token is missing a required claim. A claim counts as missing if it is absent, empty, or contains only whitespace. This means the token itself is at fault.422 Unprocessable Entity— the Outpost has no claim-name mapping configured for a required claim (its field in the Outpost configuration is left blank, for example cleared). This is an Outpost configuration problem, not a bad token — set the missing claim name to resolve it.
Example
A token for a caller that operates at the whole-organization level:
{
"sub": "user-8f3c1a",
"organization_id": "org_2f9a1c7e4d",
"organization_name": "Acme Corporation",
"iat": 1737686400,
"exp": 1737690000
}
A token for a caller that represents one specific site within an organization:
{
"sub": "user-8f3c1a",
"organization_id": "org_2f9a1c7e4d",
"sub_organization_id": "site_5b3d8f0192",
"organization_name": "Acme Corporation",
"iat": 1737686400,
"exp": 1737690000
}
In both examples the user identity is carried in the standard sub claim, which requires setting the User ID field to sub in the Outpost configuration.