JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 30
  • Score
    100M100P100Q89183F
  • License MIT

Business telemetry and payments SDK for AI products

Package Exports

  • @onstoa/sdk

Readme

Stoa TypeScript SDK

Business telemetry and payments for AI products.

Stoa instruments native AI provider clients, attaches your application's userId, and helps you understand which users, features, and product areas are driving AI usage, cost, and outcomes.

Installation

npm install @onstoa/sdk

Quick Start

import OpenAI from "openai";
import { Stoa } from "@onstoa/sdk";

const stoa = new Stoa();

// Idempotently create or update the user profile in Stoa.
await stoa.identify({
  userId: user.id,
  metadata: { plan: "pro" },
});

// Build the native provider client in your app, then wrap it with Stoa.
const openai = stoa.openai(new OpenAI({ timeout: 30_000 }), {
  userId: user.id,
});

const generateSummary = stoa.context(
  { feature: "summary_export", product: "docs" },
  async (document: string) => {
    const response = await openai.responses.create({
      model: "gpt-4.1-mini",
      input: `Summarize this document:\n\n${document}`,
    });

    await stoa.track("summary_export.completed", {
      userId: user.id,
      metadata: { format: "pdf" },
    });

    return response;
  }
);

Identify Users

Call identify when a user signs up, logs in, or when you want to sync stable user metadata.

await stoa.identify({
  userId: "user_123",
  metadata: {
    plan: "enterprise",
    workspaceId: "ws_456",
  },
});

identify is idempotent. It creates the user profile if it does not exist and updates metadata if it does. It does not set active identity; provider instrumentation and custom events still require an explicit userId.

Instrument AI Providers

Construct provider clients exactly as you would without Stoa, then pass them to Stoa.

const openai = stoa.openai(new OpenAI({ apiKey: "sk-...", timeout: 30_000 }), {
  userId: user.id,
});

Every provider call made through the returned client is attributed to that userId.

OpenAI

const openai = stoa.openai(new OpenAI(), { userId: user.id });

const response = await openai.responses.create({
  model: "gpt-4.1-mini",
  input: "Write a release note",
});

Anthropic

import Anthropic from "@anthropic-ai/sdk";

const anthropic = stoa.anthropic(new Anthropic(), { userId: user.id });

const message = await anthropic.messages.create({
  model: "claude-sonnet-4-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Write a release note" }],
});

ElevenLabs

import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const elevenlabs = stoa.elevenlabs(new ElevenLabsClient(), {
  userId: user.id,
});

const audio = await elevenlabs.textToSpeech.convert(
  "JBFqnCBsd6RMkjVDRZzb",
  {
    modelId: "eleven_multilingual_v2",
    text: "Welcome back",
  }
);

OpenRouter

OpenRouter uses the OpenAI SDK with an OpenRouter base URL.

const openrouter = stoa.openrouter(
  new OpenAI({
    apiKey: process.env.OPENROUTER_API_KEY,
    baseURL: "https://openrouter.ai/api/v1",
  }),
  { userId: user.id }
);

Add Context

Use withContext to attach feature, product, workflow, experiment, or route metadata to work done inside a scope.

await stoa.withContext({ feature: "chat", product: "assistant" }, async () => {
  await openai.responses.create(...);
});

context is available when you want a reusable function wrapper.

const generateSummary = stoa.context(
  { feature: "summary_export", product: "docs" },
  async (document: string) => {
    return await openai.responses.create({
      model: "gpt-4.1-mini",
      input: document,
    });
  }
);

Nested context merges automatically. Inner values override outer values.

Track Custom Events

App-defined business events.

await stoa.track("report.exported", {
  userId: user.id,
  metadata: {
    format: "csv",
    rows: 1200,
  },
});

Tracked events inherit active context metadata.

Create Payments

Use createPayment when you want to start a Stripe payment flow for a user.

const payment = await stoa.createPayment({
  userId: user.id,
  email: user.email,
  returnUrl: "https://app.example.com/billing/return",
});

redirectUserTo(payment.checkoutUrl);

Billing Webhooks

Stoa's SDK does not block AI provider calls by default.

Use webhooks to react to billing events such as:

balance.low
balance.depleted
payment.succeeded
payment.failed
subscription.updated

Your app decides what to do when those events arrive, such as notifying the user, prompting for payment, or changing access.

Environment Variables

STOA_API_KEY=...
STOA_BASE_URL=https://www.onstoa.com/api