Node SDK
The Node SDK provides typed helpers for the full authorization flow: create a client, authorize an action, handle the three outcomes, and verify the permit before execution.
Install
pnpm add @veritrellis/sdk-node
Requires Node 22 or later. The package ships ES modules.
Quick reference
| Export | Purpose |
|---|---|
createClient(config) | Initialize a scoped client |
client.authorizeRequest(params) | Submit an action for authorization |
verifyPermit(params) | Verify a permit JWT via JWKS |
createJwksCache(config) | Build a standalone JWKS cache |
createExpressMiddleware(config) | Express permit verification middleware |
createFastifyPlugin(config) | Fastify permit verification plugin |
createClient
import { createClient } from "@veritrellis/sdk-node";
const client = createClient({
apiKey: process.env.VERITRELLIS_API_KEY!,
workspaceId: process.env.VERITRELLIS_WORKSPACE_ID!,
environment: "sandbox", // "sandbox" | "production"
apiUrl: "https://api.veritrellis.ai"
});
Configuration
| Option | Type | Required | Description |
|---|---|---|---|
apiKey | string | Yes | Workspace API key (vt_sandbox_… or vt_prod_…) |
workspaceId | string | Yes | Your workspace ID (ws_…) |
environment | "sandbox" | "production" | Yes | Must match the environment the API key is scoped to |
apiUrl | string | No | Defaults to https://api.veritrellis.ai |
authorizeRequest
Call this before any action that requires a permit. It contacts the Veritrellis API and returns one of three outcomes.
const result = await client.authorizeRequest({
action_type: "issue_refund",
resource_ref: "cus_123",
payload: {
customer_id: "cus_123",
amount: 75,
currency: "EUR",
reason: "customer request"
}
});
Handling the three outcomes
switch (result.decision) {
case "allowed":
// Permit is ready — verify it before executing
const claims = await verifyPermit({
permitJwt: result.permit!,
workspaceId: process.env.VERITRELLIS_WORKSPACE_ID!,
issuer: "https://api.veritrellis.ai"
});
// Proceed with the action using verified claims
await issueRefund(claims);
break;
case "pending_approval":
// Save result.request_id and poll GET /v1/permits/:requestId
await saveForPolling(result.request_id!);
break;
case "denied":
// Policy blocked this request — do not execute
throw new Error(`Action denied: ${result.reason_code}`);
}
Receiving a permit from authorizeRequest is not sufficient. You must call verifyPermit before executing the action. Permits that fail verification must not unlock execution.
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
action_type | string | Yes | The canonical action type (e.g. issue_refund) |
resource_ref | string | Yes | Identifier of the resource being acted on |
payload | object | Yes | Action-specific fields; validated against shared schemas |
actor_ref | string | No | ID of the human or agent initiating the action |
idempotency_key | string | No | Stable key for safe retries |
Response shape
| Field | Present when | Type | Description |
|---|---|---|---|
decision | Always | "allowed" | "pending_approval" | "denied" | Authorization outcome |
request_id | Always | string | Unique request identifier |
permit | allowed only | string | Signed ES256 permit JWT |
reason_code | denied only | string | Machine-readable denial reason |
verifyPermit
Verifies a permit JWT against the workspace JWKS. Throws if the signature, issuer, audience, or expiry check fails.
import { verifyPermit } from "@veritrellis/sdk-node";
const claims = await verifyPermit({
permitJwt: result.permit!,
workspaceId: process.env.VERITRELLIS_WORKSPACE_ID!,
issuer: "https://api.veritrellis.ai"
});
The returned claims object contains:
| Claim | Type | Description |
|---|---|---|
sub | string | Request ID this permit is bound to |
workspace_id | string | Workspace the permit was issued for |
action_type | string | Action type the permit authorizes |
resource_ref | string | Resource the permit is scoped to |
iat | number | Issued-at timestamp (Unix) |
exp | number | Expiry timestamp (Unix) |
Verification parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
permitJwt | string | Yes | The JWT from authorizeRequest or permit polling |
workspaceId | string | Yes | Must match the workspace_id claim in the JWT |
issuer | string | Yes | Must match the iss claim (https://api.veritrellis.ai) |
jwksCacheInstance | JwksCache | No | Pass a shared JWKS cache to avoid redundant key fetches |
Polling for a permit
When decision is pending_approval, poll the permit endpoint until the request resolves:
async function pollPermit(requestId: string, apiKey: string): Promise<string> {
const url = `https://api.veritrellis.ai/v1/permits/${requestId}`;
for (let attempt = 0; attempt < 60; attempt++) {
const res = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` }
});
if (res.status === 200) {
const { permit } = await res.json();
return permit; // Ready — verify before executing
}
if (res.status === 409) {
throw new Error("Request was denied or expired — no permit will be issued");
}
// 202: still pending, wait and retry
await new Promise((r) => setTimeout(r, 2000));
}
throw new Error("Timed out waiting for permit");
}
Express middleware
Protect a route by requiring a valid permit in the Authorization header:
import express from "express";
import { createExpressMiddleware } from "@veritrellis/sdk-node";
const app = express();
const requirePermit = createExpressMiddleware({
workspaceId: process.env.VERITRELLIS_WORKSPACE_ID!,
issuer: "https://api.veritrellis.ai"
});
// Only requests with a valid permit JWT reach the handler
app.post("/refunds", requirePermit, async (req, res) => {
const { permit } = req; // verified permit claims
await processRefund(permit.resource_ref, req.body);
res.json({ status: "ok" });
});
Fastify plugin
import Fastify from "fastify";
import { createFastifyPlugin } from "@veritrellis/sdk-node";
const fastify = Fastify();
await fastify.register(createFastifyPlugin, {
workspaceId: process.env.VERITRELLIS_WORKSPACE_ID!,
issuer: "https://api.veritrellis.ai"
});
fastify.post("/refunds", { config: { requirePermit: true } }, async (req) => {
const { permit } = req; // verified permit claims
await processRefund(permit.resource_ref, req.body);
return { status: "ok" };
});
Environment variables
| Variable | Required | Description |
|---|---|---|
VERITRELLIS_API_KEY | Yes | API key from the admin app |
VERITRELLIS_WORKSPACE_ID | Yes | Workspace ID from the admin app |
Both the API key and workspace ID are available in the admin app under Settings > API keys. The sandbox and production environments have separate keys.
Generated API reference
Full symbol-level documentation generated from source: