token— the signed embed JWT. Present it to analytics-server as a bearer credential.expiresAt— RFC 3339 expiry. Token lifetime istokenTtlSeconds, defaulting to and hard-capped at 15 minutes (900s) — the analytics-server ceiling. Mint a fresh token when it expires; do not cache it.models— the closed model allowlist baked into the token.orgs— the organization slug(s) the token is scoped to.
Organization scope is server-derived
The caller can never request an organization scope. Scope is derived server-side from the authenticated user’s team/org assignments, and every granted dataset is row-filtered to exactly those orgs. The minter attaches a per-object row filter —{ field: "org_slug", op: "in", value: [<caller's slugs>] } — to every object in the token’s allowlist, so an object granted without a filter cannot leak rows. A caller who belongs to no organization is rejected.
The models and objects allowlists are closed: analytics-server denies anything not explicitly listed, so an object added to the warehouse but omitted from the config fails closed (denied), never exposed.
Enabling the minter
The endpoint is opt-in through theanalyticsEmbed block in the kodexa-api config. Everything in this block is non-secret:
ANALYTICS_EMBED_SIGNING_KEY_PEM environment variable. The keyId above is emitted as the token’s kid header so verifiers can rotate keys.
The endpoint returns 503 Service Unavailable when the minter is disabled (
enabled: false) or when it is enabled but ANALYTICS_EMBED_SIGNING_KEY_PEM is empty — in that case the signer is never wired up and the minter stays disabled. Set both to bring the endpoint online.