Veritrellis
sdk developers

Integrating Veritrellis before agent execution

A practical pattern in two calls: authorize before the risky path, verify the permit before the side effect. Plus idempotency and polling for approval flows.

Veritrellis Team

Adding a permit boundary to an existing system is deliberately small. There are two calls, and they go in two places you already control: where the decision to act is made, and where the action actually runs. Your architecture does not move.

Call one: authorize before the risky path

At the point where your agent or service is about to do something it cannot take back, ask for authorization first:

import { createClient } from "@veritrellis/sdk-node";

const client = createClient({
  apiKey: process.env.VERITRELLIS_API_KEY,
  workspaceId: process.env.VERITRELLIS_WORKSPACE_ID,
  environment: "production"
});

const result = await client.authorizeRequest({
  action_type: "issue_refund",
  resource_ref: "cus_123",
  payload: { amount: 350, currency: "USD" },
  idempotency_key: orderId
});

The idempotency_key matters anywhere a retry is possible — a queue redelivery, a webhook replay, a user double-click. With a stable key, replaying the same request returns the same decision instead of creating a second one.

Handle the three outcomes

Authorization returns allowed, denied, or pending approval. Each has an obvious next step:

switch (result.decision) {
  case "allowed":
    await runWithPermit(result.permit);
    break;
  case "pending_approval":
    await saveForPolling(result.request_id);
    break;
  case "denied":
    throw new Error(`blocked: ${result.reason_code}`);
}

Call two: verify before the side effect

Receiving a permit is not the same as trusting it. At the execution boundary — which is usually a different service from the one that asked — verify before you touch anything:

const claims = await verifyPermit({
  permitJwt: permit,
  workspaceId: process.env.VERITRELLIS_WORKSPACE_ID,
  issuer: "https://api.veritrellis.ai"
});
await processRefund(claims.resource_ref);

Verification checks signature, issuer, audience, and expiry against the published JWKS. Never treat model output, a request id, or a status field as authorization evidence — only a permit that verifies unlocks the action. Express and Fastify middleware can enforce this on a route so a handler never runs without a valid permit.

Polling an approval to completion

When a request is pending approval, poll the permit endpoint until it resolves:

const url = `https://api.veritrellis.ai/v1/permits/${requestId}`;
const res = await fetch(url, {
  headers: { Authorization: `Bearer ${apiKey}` }
});
// 200 -> permit is ready, verify then execute
// 202 -> still pending, wait and retry
// 409 -> denied or expired, no permit will ever come

A 409 is terminal: the request was rejected or expired and no permit will be issued. Treat it as a hard stop, not a reason to retry.

Put the gate where writes happen

The most common integration mistake is gating the chat layer instead of the write. The boundary belongs where the side effect actually occurs — the refund handler, the billing mutation, the contract sender — which is rarely where the diagram pretends it is. Authorize before the risky path, verify before the write, and the guarantee holds no matter what the model upstream decides.