Skip to main content
The TypeScript SDK ships as @pylva/sdk. It auto-instruments supported provider clients and emits server-priced telemetry to Pylva.
pnpm add @pylva/sdk

Initialize

import { init } from "@pylva/sdk";

init({
  apiKey: process.env.PYLVA_API_KEY!,
});
You can pass endpoint for self-hosted deployments. Omit it for Pylva Cloud.

Auto-instrumentation

Importing the SDK patches supported provider clients in the host process. Current coverage:
  • OpenAI.
  • Anthropic.
  • Vercel AI.
Captured fields include provider, model, tokens in, tokens out, latency, status, customer_id, and step_name when a tracking context is active.

Tracking context

import { track } from "@pylva/sdk";

await track(
  "acme-corp",
  { step: "draft_reply" },
  async () => {
    return openai.chat.completions.create({
      model: "gpt-4o-mini",
      messages,
    });
  },
);
track uses async context so metadata follows nested awaits without manual argument threading.

LangGraph and LangChain callbacks

Use the callback entrypoint when LangGraph.js or LangChain.js owns the model calls. This path is observer-only and does not import the root SDK entrypoint, so it does not auto-patch providers.
pnpm add @pylva/sdk @langchain/core @langchain/langgraph
import { PylvaCallbackHandler } from "@pylva/sdk/langgraph";

const handler = new PylvaCallbackHandler({
  apiKey: process.env.PYLVA_API_KEY!,
});

await graph.invoke(input, {
  callbacks: [handler],
  metadata: { pylva_customer_id: "cust_acme" },
});
@pylva/sdk/langchain re-exports the same handler. Pylva records run ids, parent run ids, graph node attribution, provider, model, token usage, latency, status, and customer id. It does not send prompts, completions, tool inputs, or tool outputs. Customer id resolution order is:
  • Constructor customerId.
  • metadata.pylva_customer_id.
  • metadata.customer_id.
  • Active track() context.
  • anonymous.
Enable tool-call usage only when the tool execution itself is billable:
const handler = new PylvaCallbackHandler({
  apiKey: process.env.PYLVA_API_KEY!,
  trackToolCalls: true,
});
Tool events report metric="calls" and metric_value=1.

Non-LLM usage

import { reportUsage } from "@pylva/sdk";

reportUsage({
  customer_id: "acme-corp",
  step: "speak_answer",
  metric: "characters",
  value: 4200,
});
Report raw usage, not dollars. Pylva applies pricing on the server.

Budget hard stops

import { init, PylvaBudgetExceeded } from "@pylva/sdk";

init({ apiKey: process.env.PYLVA_API_KEY! });

try {
  await openai.chat.completions.create({ model, messages });
} catch (err) {
  if (err instanceof PylvaBudgetExceeded) {
    return cachedOrSmallerResponse();
  }
  throw err;
}
SDK errors fail open unless an active, cached rule intentionally blocks the call.

Webhook verification

import { verifyWebhook } from "@pylva/sdk";

const ok = verifyWebhook(
  rawBody,
  signatureHeader,
  process.env.PYLVA_WEBHOOK_SECRET!,
  timestampHeader,
);
The helper accepts raw hex signatures and sha256= prefixed signatures.