Exports
| Symbol | Kind | Purpose |
|---|---|---|
createZoneMiddleware | factory | Next.js middleware that verifies the per-request zone JWT. |
getSession | helper | Read the verified claims and bearer token inside a server component. |
getZoneToken | helper | Read just the bearer token from request headers. |
createZoneClient | factory | HTTP client for the zone-scoped platform endpoints. |
ApiError | class | Thrown by client methods on non-2xx responses. |
createZoneMiddleware(options)
Drop into middleware.ts at the root of your zone app.
import { createZoneMiddleware } from '@cxpa/sdk/zone'
export const middleware = createZoneMiddleware({
secret: process.env.ZONE_JWT_SECRET!,
audience: process.env.AUDIENCE_ID!,
})
export const config = {
matcher: ['/:orgSlug/:agentSlug/app/:path*', '/:orgSlug/:agentSlug/app'],
}
Options:
| Field | Type | Default | Description |
|---|---|---|---|
secret | string | — | HS256 secret shared with the platform for this agent. |
audience | string | — | Audience claim — must match the value the platform mints. |
clockToleranceSeconds | number | 5 | Allowed clock skew. |
tokenHeader | string | 'x-zone-token' | Incoming header that carries the JWT. |
On success the middleware attaches the decoded claims to x-zone-claims and forwards the original token in x-zone-token. On failure it returns 401.
getSession()
import { getSession } from '@cxpa/sdk/zone'
export default async function Page() {
const { claims, token } = await getSession()
return <div>Hello {claims.userId}</div>
}
Throws if called outside a request that the zone middleware processed.
createZoneClient(options | session)
import { createZoneClient, getSession } from '@cxpa/sdk/zone'
const session = await getSession()
const cxpa = createZoneClient({
baseUrl: process.env.PLATFORM_API_BASE_URL!,
token: session.token,
agentId: session.claims.agentId,
})
const { runId } = await cxpa.runs.create({ payload: { url } })
const run = await cxpa.runs.get(runId)
const cred = await cxpa.credentials.get('hubspot')
Methods
| Method | HTTP under the hood |
|---|---|
runs.create({ payload }) | POST /api/zones/agents/{agentId}/runs |
runs.createAndWait({ payload, timeoutMs? }) | POST /api/zones/agents/{agentId}/runs/trigger-and-wait |
runs.createAndDownload({ payload, timeoutMs? }) | POST /api/zones/agents/{agentId}/runs/trigger-and-download |
runs.get(runId) | GET /api/zones/agents/{agentId}/runs/{runId} |
credentials.get(integrationKey) | GET /api/zones/agents/{agentId}/credentials/{key} |
All requests run with cache: 'no-store' and throw ApiError on non-2xx.
Synchronous run + output (PDF reports, generated documents)
runs.createAndWait is the zone equivalent of triggerRunAndWait: it starts a run and blocks until the run reaches a terminal state or the server-side wait window elapses. The returned object is either a terminal Run row (with output_data) or a { timedOut: true } placeholder. Use it when the zone UI hands the output directly to the user — for example, a "Generate Report" button that returns a PDF link.
const result = await cxpa.runs.createAndWait({
payload: { topic: 'Q3 financial summary' },
timeoutMs: 120_000,
})
if (result.timedOut) {
// Run is still in flight — fall back to cxpa.runs.get(result.runId).
} else if (result.status === 'completed') {
// result.output_data has whatever the agent returned.
}
The effective wait is capped server-side by the agent's running_timeout_ms and a platform hard ceiling. On wait-window expiry the route returns 202; the run continues asynchronously and can be retrieved with runs.get(runId).
Binary file download
runs.createAndDownload is the binary-streaming companion to runs.createAndWait. The platform pipes the runtime's response body straight through; the bytes never land in output_data. Use it when the agent's value is a file (PDF, spreadsheet, image) and the zone forwards that file to the user's browser.
const result = await cxpa.runs.createAndDownload({
payload: { topic: 'Q3 financial summary' },
})
// Forward straight to the browser — single byte stream end-to-end.
return new Response(result.body, {
headers: {
'content-type': result.contentType,
'content-disposition':
result.contentDisposition ?? `attachment; filename="${result.filename}"`,
},
})
Eligibility: the bound agent must be platform-managed with output_kind = 'file_download' on a runtime that supports streaming binary output. The platform records the run with metadata only (filename, mime type, byte count) — file bytes are not persisted, so re-download is not available.