The zone middleware leaves two pieces of state on the request:
x-zone-claims— the verified JWT payload as a JSON string.x-zone-token— the raw bearer token (forward this when you call the platform).
The getSession helper reads both.
In a server component
import { getSession } from '@cxpa/sdk/zone'
export default async function Page() {
const { claims, token } = await getSession()
return (
<main>
<h1>Welcome, {claims.userName ?? claims.userEmail}</h1>
<p>Email: {claims.userEmail}</p>
<p>Organization: {claims.orgSlug}</p>
<p>Role: {claims.role}</p>
</main>
)
}
In a server action
'use server'
import { getSession } from '@cxpa/sdk/zone'
export async function triggerSomething(input: { url: string }) {
const { claims, token } = await getSession()
// token can now be forwarded to createZoneClient(...)
}
Claims
interface ZoneClaims {
userId: string
userName: string | null
userEmail: string
orgId: string
orgSlug: string
agentId: number
role: 'owner' | 'member' | 'admin'
aud?: string
iat?: number
exp?: number
}
The admin role is a virtual value computed for platform admins; it is not a stored membership role.
userName is nullable — Supabase Auth metadata may not include a full name. Fall back to userEmail (or its local-part / initials) when rendering.
role is information, not enforcement. The platform admits every member of the bound organization to the zone and does not gate anything inside it. If an action in your zone should be restricted, compare claims.role yourself in the server action — and note that a member on this platform is an operator, not a read-only viewer. Likewise orgId and userId: the platform guarantees which organization and agent you are serving, and nothing finer. Scoping rows to a user, and to an org when several deployments share a database, is yours. See Zone data and tenancy.
The token itself is short-lived — minted fresh by the platform on every proxied request and never exposed to the browser — so there is nothing to refresh and no session for a long-open tab to outlive. Do not stash a token and reuse it later; use the one on the current request.
Continue
Call the platform API using the bearer token from getSession().