Skip to content

Integration Guide

Step-by-step guide to integrating the Converra SDK into your application.

The simplest integration — wrap your LLM client and all conversations are captured automatically:

typescript
import { Converra } from 'converra';
import OpenAI from 'openai';

const converra = new Converra({ apiKey: process.env.CONVERRA_API_KEY });
const openai = converra.wrap(new OpenAI());

// Use openai normally — conversations captured, A/B testing enabled
const response = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [
    { role: 'system', content: 'You are a helpful support agent.' },
    { role: 'user', content: userMessage }
  ]
});

Converra identifies your agent by hashing the system prompt content. For dynamic prompts (e.g., with RAG context), pass an explicit prompt ID:

typescript
const openai = converra.wrap(new OpenAI(), { promptId: 'support-agent' });

Option 2: Auto-Instrumentation (Zero Code)

No wrapping needed — every OpenAI and Anthropic client is patched automatically:

bash
# Via Node.js flag
CONVERRA_API_KEY=sk_live_... node --import converra/auto server.js

Or add one import at the top of your entrypoint:

typescript
// server.ts (first line)
import 'converra/auto';

// Later in your code — all clients are already instrumented
import OpenAI from 'openai';
const openai = new OpenAI(); // Already wrapped

Auto-instrumentation is controlled via environment variables:

VariableDescriptionDefault
CONVERRA_API_KEYAPI key (required)
CONVERRA_ENABLEDSet false to disabletrue
CONVERRA_PROMPT_IDExplicit prompt matching

Option 3: Vercel AI SDK Middleware

The standalone @converra/ai-sdk-middleware package captures all generateText() and streamText() calls with zero risk — it never modifies your LLM requests or responses:

bash
npm install @converra/ai-sdk-middleware ai@4.3.19 @ai-sdk/openai@1.3.24
typescript
import { createConverraMiddleware } from '@converra/ai-sdk-middleware';
import { wrapLanguageModel, generateText } from 'ai';
import { openai } from '@ai-sdk/openai';

const model = wrapLanguageModel({
  model: openai('gpt-4o'),
  middleware: createConverraMiddleware({
    apiKey: process.env.CONVERRA_API_KEY!,
    promptName: 'My Support Agent',
  }),
});

// Use normally — traces are captured automatically
const result = await generateText({ model, prompt: 'Help me reset my password' });

Configuration options:

OptionTypeDefaultDescription
apiKeystringrequiredYour Converra API key
promptNamestringUntitled PromptAgent name (auto-created in Converra if it doesn't exist)
batchSizenumber1Traces buffered before flushing
flushIntervalnumber30000Milliseconds between automatic flushes
enabledbooleantrueKill switch to disable capture

If you're already using the full converra SDK and want variant swapping (A/B testing on prompts), use the built-in middleware instead:

typescript
import { Converra } from 'converra';
import { createConverraMiddleware } from 'converra/ai-sdk';

const converra = new Converra({ apiKey: process.env.CONVERRA_API_KEY });
const middleware = createConverraMiddleware(converra.createInterceptor());

const result = await streamText({
  model: openai('gpt-4o'),
  messages,
  experimental_middleware: middleware,
});

Capturing Tool Definitions

If your agent calls tools, send their definitions (name, description, parameters) alongside the conversation. This lets Converra grade the tool contract — whether each tool's API is well-designed for the agent — and powers the Tool Calling view's "Fix the tool vs. Fix the agent" split. Without definitions, tool behavior is still graded, but description/parameter quality is marked unevaluable (Converra never invents a contract).

A tool definition is { name, description?, parameters? }, where parameters is the tool's JSON Schema.

Vercel AI SDK middleware — automatic. @converra/ai-sdk-middleware reads the tools you already pass to generateText/streamText and forwards their definitions for you. No extra code — just keep passing tools as usual.

Node SDK — pass toolDefinitions to send():

typescript
await converra.send({
  agent: 'support-agent',
  messages,
  toolDefinitions: [
    {
      name: 'lookup_order',
      description: 'Look up an order by its ID. Returns status and line items.',
      parameters: {
        type: 'object',
        properties: { order_id: { type: 'string' } },
        required: ['order_id'],
      },
    },
  ],
});

Python SDK — pass tool_definitions to send():

python
from converra import Converra, ToolDefinition

client.send(
    agent="support-agent",
    messages=messages,
    tool_definitions=[
        ToolDefinition(
            name="lookup_order",
            description="Look up an order by its ID. Returns status and line items.",
            parameters={
                "type": "object",
                "properties": {"order_id": {"type": "string"}},
                "required": ["order_id"],
            },
        ),
    ],
)

REST API — include toolDefinitions in the conversation body; see Tool Definitions in the API reference.

Definitions are deduped by name and bounded (descriptions trimmed, sensitive values redacted), so it's safe to send the full list on every call. Send them on the first send() for a conversation; later appends can add newly-seen tools.

Multi-Agent Tracing

For applications with multiple agents, use trace() to link related LLM calls:

typescript
const result = await converra.trace('customer-session-456').run(async () => {
  // All LLM calls inside are linked into one trace
  const classification = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [{ role: 'system', content: 'Classify this request...' }, ...],
  });

  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: [{ role: 'system', content: 'You are a billing agent...' }, ...],
  });

  return response;
});
// Agent boundaries auto-detected by system prompt changes

Traces use AsyncLocalStorage — all LLM calls within the callback are automatically linked, even across async boundaries.

Management API (Manual Integration)

For cases where you want explicit control over prompt fetching and conversation logging:

typescript
import { Converra } from 'converra';

const converra = new Converra({ apiKey: process.env.CONVERRA_API_KEY });

// 1. Fetch your prompt (cached for 5 minutes by default)
const prompt = await converra.prompts.get('prompt_123');

// 2. Use the prompt in your AI application
const response = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [
    { role: 'system', content: prompt.content },
    { role: 'user', content: userMessage }
  ]
});

// 3. Log the conversation for insights
await converra.conversations.create({
  promptId: 'prompt_123',
  content: `User: ${userMessage}\nAssistant: ${response.choices[0].message.content}`,
  status: 'completed'
});

Webhook-Based Cache Invalidation

For instant prompt updates when optimizations complete:

typescript
import { Converra, createWebhookHandler } from 'converra';

const converra = new Converra({
  apiKey: process.env.CONVERRA_API_KEY,
  cache: { strategy: 'memory', ttl: 5 * 60 * 1000 }
});

const webhookHandler = createWebhookHandler({
  secret: process.env.CONVERRA_WEBHOOK_SECRET,
  onPromptUpdated: (data) => {
    converra.cache.invalidate(data.promptId);
  },
  onOptimizationCompleted: (data) => {
    if (data.results.winningVariantId) {
      console.log(`${data.results.improvementPercentage}% improvement`);
    }
  },
});

// Express
app.post('/webhooks/converra', express.raw({ type: 'application/json' }), async (req, res) => {
  const result = await webhookHandler(req.body, req.headers['x-converra-signature']);
  res.status(result.success ? 200 : 400).json(result);
});

Framework Examples

Next.js App Router

typescript
// lib/converra.ts
import { Converra } from 'converra';

export const converra = new Converra({
  apiKey: process.env.CONVERRA_API_KEY!,
});

// app/api/chat/route.ts
import { converra } from '@/lib/converra';
import OpenAI from 'openai';

const openai = converra.wrap(new OpenAI());

export async function POST(req: Request) {
  const { messages } = await req.json();
  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages,
  });
  return Response.json(response);
}

Express.js

typescript
import express from 'express';
import { Converra } from 'converra';
import OpenAI from 'openai';

const app = express();
const converra = new Converra({ apiKey: process.env.CONVERRA_API_KEY! });
const openai = converra.wrap(new OpenAI());

app.post('/chat', async (req, res) => {
  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: req.body.messages,
  });
  res.json(response);
});

Graceful Shutdown

Flush pending data before process exit:

typescript
process.on('SIGTERM', async () => {
  await converra.shutdown();
  process.exit(0);
});

Auto-instrumentation (converra/auto) handles shutdown automatically.