Quickstart
Install + paste + run. First agent_events row in under 5 minutes.
1. Install
The core SDK plus the two adapters you need for the Express + OpenAI example.
npm install @avanor/sdk @avanor/sdk-express @avanor/sdk-openai2. Set your environment
AVANOR_API_KEY binds events to your tenant server-side. Mint one at /dashboard/settings/api-keys on your Avanor workspace.
export AVANOR_API_KEY=ak_live_...
export OPENAI_API_KEY=sk-...3. Initialize the client
One file at the top of your app. The init call is idempotent — safe to call multiple times from any entry point.
// avanor.ts (one file, top of customer's app)
import { Avanor } from '@avanor/sdk';
export const avanor = Avanor.init({
apiKey: process.env.AVANOR_API_KEY!, // tenant binding derived server-side
environment: process.env.NODE_ENV ?? 'dev', // 'dev' | 'staging' | 'prod'
service: 'broker-portal', // becomes service.name on every span
});Multiple tenants in one process
Avanor.init() is a process singleton, the right default for a single-tenant app. When one process must emit to several Avanor tenants (a background worker servicing multiple customers, or an agency hosting multiple end-customers), use createClient() instead. Each call returns a fully isolated client with its own API key, queue, and lifecycle. The init() singleton is never touched.
// Multi-tenant worker: one process, many Avanor tenants.
import { createClient } from '@avanor/sdk';
const tenantA = createClient({
apiKey: process.env.AVANOR_KEY_A!,
environment: 'prod',
service: 'tenant-a-worker',
});
const tenantB = createClient({
apiKey: process.env.AVANOR_KEY_B!,
environment: 'prod',
service: 'tenant-b-worker',
});
tenantA.track('job_processed', { jobId });
tenantB.track('job_processed', { jobId });
// Drain both queues before the worker exits.
await Promise.all([tenantA.flush(), tenantB.flush()]);4. Paste the example
A complete Express + OpenAI handler that gates on allow(), makes an auto-instrumented LLM call, and writes a hash-chained attest() row when it finishes.
import express from 'express';
import { Avanor } from '@avanor/sdk';
import { instrumentOpenAI } from '@avanor/sdk-openai';
import OpenAI from 'openai';
const avanor = Avanor.init({
apiKey: process.env.AVANOR_API_KEY!,
environment: process.env.NODE_ENV ?? 'dev',
service: 'broker-portal',
});
const openai = instrumentOpenAI(new OpenAI()); // monkey-patches in place
const app = express();
app.use(express.json());
app.use(avanor.middleware()); // tracks every request
app.post('/send-email', async (req, res) => {
const { to, subject, body } = req.body;
const { allow, reason, approvalId } = await avanor.allow('email_send', {
'recipient.domain': to.split('@')[1],
'subject.length': subject.length,
'has_pii': /\b\d{3}-\d{2}-\d{4}\b/.test(body),
});
if (!allow) return res.status(403).json({ reason, approvalId });
const draft = await openai.chat.completions.create({ // auto-instrumented
model: 'gpt-4o',
messages: [{ role: 'user', content: `Rewrite professionally: ${body}` }],
});
// ... send via Resend/SES ...
const att = await avanor.attest('email_send', {
input: { to, subject }, output: { messageId: 'abc123' }, status: 'ok',
});
res.json({ ok: true, attestId: att.attestId });
});
app.listen(3000);5. Run it
# in one shell
node --experimental-strip-types app.ts
# in another
curl -X POST localhost:3000/send-email \
-H 'content-type: application/json' \
-d '{"to":"alice@example.com","subject":"hi","body":"hello"}'6. See your first event
Open /dashboard/events. Within a few seconds you should see a new row with:
platform=customer_sdksdk_version=0.3.0sdk_language=typescriptingestion_path=sdkgen_ai.*andavanor.*attributes populated from the wrappedchat.completions.createcall
What this gives you, in one sentence
Guarded actions your code makes land on the Avanor record. A policy denial at the enforcement gate is sealed into the hash-chained audit log; an allowed action is recorded append-only, and by default carries no seal. The evidence is yours to export, the policy decisions are yours to author, and the kill switch is yours (and Avanor support) to flip without a customer redeploy.
Next
- Express + OpenAI walkthrough — same code with line-by-line commentary.
- API Reference — every public type and method.