Quickstart

This guide walks through a full Paid integration using a real-world example: an AI chatbot where customers subscribe to monthly plans, receive Chat Credits, and consume them with every message.

By the end you will have signals, AI cost tracking, delivered value, balance enforcement, and checkout working together.

The example

Your product is an AI chatbot. It offers three monthly plans, each granting a different number of Chat Credits. Every chat message consumes 1 credit.

PlanPriceChat Credits per month
Starter$29/month500
Pro$79/month2,000
Business$199/month10,000

Prerequisites

$npm install @paid-ai/paid-node

1. Create your product

Set up the credit currency and product in the Paid dashboard.

  1. Go to Settings > Credit currencies and create a currency named Chat Credits with the key chat_credits.
  2. Go to Products and create a product named ChatBot with external ID chatbot.
  3. Add three plans to the product. Each plan needs two pricing lines: a Recurring fee for the monthly charge ($29, $79, or $199), and a Prepaid credits line for the usage metering with event name chat_message, credit currency Chat Credits, and credit cost 1. Add a credit benefit to each plan that grants the monthly allowance shown in the table above.

You can also create products via the API or the CLI.

2. Send signals

Signals represent actions your agent takes, like a chat message, tool call, or completed workflow. They can drive billing, track delivered value, or do both. In this example, send one every time a customer sends a chat message; because the product’s pricing rule matches on the event name chat_message, Paid automatically deducts 1 Chat Credit per signal.

1import { PaidClient } from "@paid-ai/paid-node";
2
3const paid = new PaidClient({ token: "YOUR_PAID_API_KEY" });
4
5await paid.signals.createSignals({
6 signals: [
7 {
8 eventName: "chat_message",
9 customer: { externalCustomerId: currentUser.id },
10 attribution: { externalProductId: "chatbot" },
11 data: { model: "gpt-4o", tokens: 350 },
12 },
13 ],
14});

The data field is optional. Include any metadata you want to see in the dashboard or use in delivered value calculations later.

3. Track AI costs

Cost traces capture what each AI call costs you. Initialize autoinstrumentation once at startup, then wrap your AI calls in a tracing context. Paid records the provider, model, token counts, and cost automatically.

1import { initializeTracing, trace } from "@paid-ai/paid-node/tracing";
2import { paidAutoInstrument } from "@paid-ai/paid-node/tracing/auto";
3import OpenAI from "openai";
4
5// Run once at startup
6initializeTracing(process.env.PAID_API_KEY!);
7await paidAutoInstrument(["openai"]);
8
9const openai = new OpenAI();
10
11// Wrap each request in a trace
12const reply = await trace(
13 {
14 externalCustomerId: currentUser.id,
15 externalProductId: "chatbot",
16 },
17 async () => {
18 const completion = await openai.chat.completions.create({
19 model: "gpt-4o",
20 messages: [{ role: "user", content: userMessage }],
21 });
22 return completion.choices[0]?.message?.content ?? "";
23 },
24);

Pass the library names you use ("openai", "anthropic"), or call with no arguments to instrument all supported libraries.

Cost-attributed signals combine steps 2 and 3. Send the signal inside a tracing context and Paid links the agent action to the AI costs captured in the same trace. This gives you per-signal margin visibility.

1import { trace, signal } from "@paid-ai/paid-node/tracing";
2
3const reply = await trace(
4 {
5 externalCustomerId: currentUser.id,
6 externalProductId: "chatbot",
7 },
8 async () => {
9 const completion = await openai.chat.completions.create({
10 model: "gpt-4o",
11 messages: [{ role: "user", content: userMessage }],
12 });
13
14 signal("chat_message", true, {
15 model: "gpt-4o",
16 tokens: completion.usage?.total_tokens,
17 });
18
19 return completion.choices[0]?.message?.content ?? "";
20 },
21);

The customer and product are inherited from the tracing context, so you do not need to pass them again in the signal.

5. Track delivered value

Add data fields to your signals that describe the value delivered to the customer. Paid uses these to calculate metrics like time saved or money saved, which appear on value receipts and in the customer portal.

Configure value types in Signals > Signal values to tell Paid how to calculate delivered value from your signal data.

1await paid.signals.createSignals({
2 signals: [
3 {
4 eventName: "chat_message",
5 customer: { externalCustomerId: currentUser.id },
6 attribution: { externalProductId: "chatbot" },
7 data: {
8 model: "gpt-4o",
9 tokens: 350,
10 time_saved: 3,
11 time_saved_unit: "minutes",
12 money_saved: 1500, // cents ($15.00)
13 },
14 },
15 ],
16});

Duration fields need a value and a _unit suffix (seconds, minutes, hours, or days). Monetary fields are always in cents. See Delivered value for the full list of parameter types and binding modes.

6. Check credit balances

Before processing a message, check whether the customer has credits remaining. This lets you show upgrade prompts or block usage when the balance hits zero.

1const balances = await paid.customers.getCreditBalances(
2 currentUser.paidCustomerId,
3);
4
5const chatCredits = balances.data.find(
6 (b) => b.currencyKey === "chat_credits",
7);
8
9if (!chatCredits || chatCredits.available < 1) {
10 throw new Error("No Chat Credits remaining. Upgrade your plan to continue.");
11}
12
13// Proceed with the chat message

7. Add checkout

Create a checkout session when a customer wants to subscribe. Paid handles payment collection, plan selection, customer creation, and credit provisioning in one flow.

1const checkout = await paid.checkouts.createCheckout({
2 products: [{ id: "YOUR_PRODUCT_ID" }], // from the Products page or API
3 externalCustomerId: currentUser.id,
4 successUrl: "https://yourapp.com/welcome?checkout_id={CHECKOUT_ID}",
5});
6
7// Redirect the customer to complete payment
8redirect(checkout.url);

The customer picks their plan during checkout. After payment, Paid creates an order and grants that plan’s Chat Credits.

When the customer returns, verify the checkout before granting access:

1const checkoutId = req.query.checkout_id;
2const result = await paid.checkouts.getCheckout({ id: checkoutId });
3
4if (
5 result.status === "completed" &&
6 result.externalCustomerId === currentUser.id
7) {
8 await provisionAccess(currentUser.id);
9}

Next steps