> This page is for version v2 (default).
> For other versions, use one of these documentation indexes:
> - v2 (default): https://docs.paid.ai/v-2/llms.txt
> - v1: https://docs.paid.ai/v-1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.paid.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.paid.ai/_mcp/server.

# Quickstart

> Build a complete credit-based billing integration for an AI chatbot with Paid.

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.

| Plan     | Price       | Chat Credits per month |
| -------- | ----------- | ---------------------- |
| Starter  | \$29/month  | 500                    |
| Pro      | \$79/month  | 2,000                  |
| Business | \$199/month | 10,000                 |

## Prerequisites

* A [Paid API key](https://app.paid.ai/settings/api-keys)
* The Paid SDK installed in your project

#### Node.js

```bash
npm install @paid-ai/paid-node
```

#### Python

```bash
pip install paid
```

#### Go

```bash
go get github.com/paid-ai/paid-go@main
```

## 1. Create your product

Set up the credit currency, product, pricing, and plans in the [Paid dashboard](https://app.paid.ai).

1. Go to [**Configure > Credits**](https://app.paid.ai/credits-currencies), click **Create credit currency**, and create a currency named `Chat Credits` with the key `chat_credits`.
2. Go to [**Configure > Products**](https://app.paid.ai/products) and create a single product named `ChatBot` with external ID `chatbot`.
3. In the product's **Pricing** section, click **Add > Platform fee**. This creates the subscription fee pricing that each plan will use.
4. On the **Platform fee**, click **Add credit benefit**. Leave the recipient as **Organization**, select **Chat Credits** as the credit currency, and enter an initial Credits amount such as `500`. This makes the subscription fee grant credits into the customer's Chat Credits pool; you will set each plan's final amount in the plan step.
5. Click **Add > Usage pricing**. Set the display name to `Chat message`, the event name to `chat_message`, **Pricing mode** to **Credits**, **Credit model** to **Flat credits**, **Credit currency** to **Chat Credits**, and **Credits per signal** to `1`. Define any other usage prices that map to signals on this same product.
6. Go to [**Configure > Plans**](https://app.paid.ai/plans), click **Create plan**, choose `ChatBot`, and create the `Starter`, `Pro`, and `Business` plans. For each plan, keep the **Platform fee** and **Usage based** pricing enabled, set the platform fee price to \$29, \$79, or \$199 per month, and set the platform fee's Credit Benefit amount to the monthly allowance shown in the table above.

You can also create products via the [API](/api-reference/api-reference/products/create-product) or the [CLI](/cli/cli/quickstart).

## 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 usage pricing matches on the event name `chat_message`, Paid automatically deducts 1 Chat Credit per signal.

#### Node.js

```typescript
import { PaidClient } from "@paid-ai/paid-node";

const paid = new PaidClient({ token: "YOUR_PAID_API_KEY" });

await paid.signals.createSignals({
  signals: [
    {
      eventName: "chat_message",
      customer: { externalCustomerId: currentUser.id },
      attribution: { externalProductId: "chatbot" },
      data: { model: "gpt-4o", tokens: 350 },
    },
  ],
});
```

#### Python

```python
from paid import Paid, Signal, CustomerByExternalId, ProductByExternalId

paid = Paid(token="YOUR_PAID_API_KEY")

paid.signals.create_signals(
    signals=[
        Signal(
            event_name="chat_message",
            customer=CustomerByExternalId(
                external_customer_id=current_user.id,
            ),
            attribution=ProductByExternalId(
                external_product_id="chatbot",
            ),
            data={"model": "gpt-4o", "tokens": 350},
        )
    ],
)
```

#### Go

```go
import (
    paid "github.com/paid-ai/paid-go"
    paidclient "github.com/paid-ai/paid-go/client"
    "github.com/paid-ai/paid-go/option"
)

paidClient := paidclient.NewClient(option.WithToken("YOUR_PAID_API_KEY"))

_, err := paidClient.Signals.CreateSignals(ctx, &paid.BulkSignalsRequest{
    Signals: []*paid.Signal{
        {
            EventName: "chat_message",
            Customer: &paid.CustomerAttribution{
                CustomerByExternalID: &paid.CustomerByExternalID{
                    ExternalCustomerID: currentUser.ID,
                },
            },
            Attribution: &paid.Attribution{
                ProductByExternalID: &paid.ProductByExternalID{
                    ExternalProductID: "chatbot",
                },
            },
            Data: map[string]interface{}{"model": "gpt-4o", "tokens": 350},
        },
    },
})
if err != nil {
    return err
}
```

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

> **Note**
>
> This example uses a fixed credit cost of 1 per signal. To consume a variable
> number of credits, configure a quantity mapping on the pricing rule and pass a
> `quantity` field in signal `data`. See [First signals](/documentation/getting-started/first-signals#consuming-multiple-credits-per-signal)
> for details.

## 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.

#### Node.js

```typescript
import { initializeTracing, trace } from "@paid-ai/paid-node/tracing";
import { paidAutoInstrument } from "@paid-ai/paid-node/tracing/auto";
import OpenAI from "openai";

// Run once at startup
initializeTracing(process.env.PAID_API_KEY!);
await paidAutoInstrument(["openai"]);

const openai = new OpenAI();

// Wrap each request in a trace
const reply = await trace(
  {
    externalCustomerId: currentUser.id,
    externalProductId: "chatbot",
  },
  async () => {
    const completion = await openai.chat.completions.create({
      model: "gpt-4o",
      messages: [{ role: "user", content: userMessage }],
    });
    return completion.choices[0]?.message?.content ?? "";
  },
);
```

#### Python

```python
from openai import OpenAI
from paid.tracing import initialize_tracing, paid_autoinstrument, paid_tracing

# Run once at startup
initialize_tracing(api_key="YOUR_PAID_API_KEY")
paid_autoinstrument(libraries=["openai"])

openai_client = OpenAI()

# Wrap each request in a trace
with paid_tracing(current_user.id, external_product_id="chatbot"):
    completion = openai_client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": user_message}],
    )
    reply = completion.choices[0].message.content
```

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

## 4. Link signals to AI costs

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.

#### Node.js

```typescript
import { trace, signal } from "@paid-ai/paid-node/tracing";

const reply = await trace(
  {
    externalCustomerId: currentUser.id,
    externalProductId: "chatbot",
  },
  async () => {
    const completion = await openai.chat.completions.create({
      model: "gpt-4o",
      messages: [{ role: "user", content: userMessage }],
    });

    signal("chat_message", true, {
      model: "gpt-4o",
      tokens: completion.usage?.total_tokens,
    });

    return completion.choices[0]?.message?.content ?? "";
  },
);
```

#### Python

```python
from paid.tracing import paid_tracing, cost_attributed_signal

with paid_tracing(current_user.id, external_product_id="chatbot"):
    completion = openai_client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": user_message}],
    )

    cost_attributed_signal(
        event_name="chat_message",
        data={"model": "gpt-4o", "tokens": completion.usage.total_tokens},
    )

    reply = completion.choices[0].message.content
```

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**](https://app.paid.ai/signals/signal-values) to tell Paid how to calculate delivered value from your signal data.

#### Node.js

```typescript
await paid.signals.createSignals({
  signals: [
    {
      eventName: "chat_message",
      customer: { externalCustomerId: currentUser.id },
      attribution: { externalProductId: "chatbot" },
      data: {
        model: "gpt-4o",
        tokens: 350,
        time_saved: 3,
        time_saved_unit: "minutes",
        money_saved: 1500, // cents ($15.00)
      },
    },
  ],
});
```

#### Python

```python
paid.signals.create_signals(
    signals=[
        Signal(
            event_name="chat_message",
            customer=CustomerByExternalId(
                external_customer_id=current_user.id,
            ),
            attribution=ProductByExternalId(
                external_product_id="chatbot",
            ),
            data={
                "model": "gpt-4o",
                "tokens": 350,
                "time_saved": 3,
                "time_saved_unit": "minutes",
                "money_saved": 1500,  # cents ($15.00)
            },
        )
    ],
)
```

#### Go

```go
_, err := paidClient.Signals.CreateSignals(ctx, &paid.BulkSignalsRequest{
    Signals: []*paid.Signal{
        {
            EventName: "chat_message",
            Customer: &paid.CustomerAttribution{
                CustomerByExternalID: &paid.CustomerByExternalID{
                    ExternalCustomerID: currentUser.ID,
                },
            },
            Attribution: &paid.Attribution{
                ProductByExternalID: &paid.ProductByExternalID{
                    ExternalProductID: "chatbot",
                },
            },
            Data: map[string]interface{}{
                "model":           "gpt-4o",
                "tokens":          350,
                "time_saved":      3,
                "time_saved_unit": "minutes",
                "money_saved":     1500, // cents ($15.00)
            },
        },
    },
})
if err != nil {
    return err
}
```

Duration fields need a value and a `_unit` suffix (`seconds`, `minutes`, `hours`, or `days`). Monetary fields are always in cents. See [Delivered value](/documentation/getting-started/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.

#### Node.js

```typescript
const balances = await paid.customers.getCreditBalances(
  currentUser.paidCustomerId,
);

const chatCredits = balances.data.find(
  (b) => b.currencyKey === "chat_credits",
);

if (!chatCredits || chatCredits.available < 1) {
  throw new Error("No Chat Credits remaining. Upgrade your plan to continue.");
}

// Proceed with the chat message
```

#### Python

```python
balances = paid.customers.get_credit_balances(
    current_user.paid_customer_id,
)

chat_credits = next(
    (b for b in balances.data if b.currency_key == "chat_credits"),
    None,
)

if not chat_credits or chat_credits.available < 1:
    raise Exception("No Chat Credits remaining. Upgrade your plan to continue.")

# Proceed with the chat message
```

#### Go

```go
import "errors"

balances, err := paidClient.Customers.GetCustomerCreditBalances(
    ctx,
    &paid.GetCustomerCreditBalancesRequest{ID: currentUser.PaidCustomerID},
)
if err != nil {
    return err
}

var chatCredits *paid.CreditBalance
for _, balance := range balances.Data {
    if balance.CurrencyKey == "chat_credits" {
        chatCredits = balance
        break
    }
}

if chatCredits == nil || chatCredits.Available < 1 {
    return errors.New("no Chat Credits remaining. Upgrade your plan to continue.")
}

// 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.

#### Node.js

```typescript
const checkout = await paid.checkouts.createCheckout({
  products: [{ id: "YOUR_PRODUCT_ID" }], // from the Products page or API
  externalCustomerId: currentUser.id,
  successUrl: "https://yourapp.com/welcome?checkout_id={CHECKOUT_ID}",
});

// Redirect the customer to complete payment
redirect(checkout.url);
```

#### Python

```python
checkout = paid.checkouts.create_checkout(
    products=[{"id": "YOUR_PRODUCT_ID"}],  # from the Products page or API
    external_customer_id=current_user.id,
    success_url="https://yourapp.com/welcome?checkout_id={CHECKOUT_ID}",
)

# Redirect the customer to complete payment
redirect(checkout.url)
```

#### Go

```go
checkout, err := paidClient.Checkouts.CreateCheckout(ctx, &paid.CreateCheckoutRequest{
    Products: []*paid.CheckoutProductInput{
        {ID: "YOUR_PRODUCT_ID"}, // from the Products page or API
    },
    ExternalCustomerID: paid.String(currentUser.ID),
    SuccessURL:         "https://yourapp.com/welcome?checkout_id={CHECKOUT_ID}",
})
if err != nil {
    return err
}

// Redirect the customer to complete payment
redirect(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:

#### Node.js

```typescript
const checkoutId = req.query.checkout_id;
const result = await paid.checkouts.getCheckout({ id: checkoutId });

if (
  result.status === "completed" &&
  result.externalCustomerId === currentUser.id
) {
  await provisionAccess(currentUser.id);
}
```

#### Python

```python
checkout_id = request.args.get("checkout_id")
result = paid.checkouts.get_checkout(checkout_id)

if (
    result.status == "completed"
    and result.external_customer_id == current_user.id
):
    provision_access(current_user.id)
```

#### Go

```go
checkoutID := r.URL.Query().Get("checkout_id")
result, err := paidClient.Checkouts.GetCheckout(
    ctx,
    &paid.GetCheckoutRequest{ID: checkoutID},
)
if err != nil {
    return err
}

if result.Status == paid.CheckoutStatusCompleted &&
    result.ExternalCustomerID != nil &&
    *result.ExternalCustomerID == currentUser.ID {
    provisionAccess(currentUser.ID)
}
```

## Next steps

* [Checkout](/documentation/customers-users/checkout): session expiration, metadata, currency locking, and reusable links
* [Credits](/documentation/credits/credits): query balances and enforce credit limits in your app
* [How credit balances work](/documentation/credits/how-credit-balances-work): allocation lifecycle, ledger entries, and balance breakdown
* [Examples of plans with included credits](/documentation/credits/examples-of-plans-with-included-credits): seat-level credits, rollover, and allocation cadence
* [Webhooks](/documentation/billing/webhooks): react to billing events in real time
* [Delivered value](/documentation/getting-started/delivered-value): value types, parameter binding, backfills, and custom formulas
* [Value receipts](/documentation/value-receipts/overview): surface delivered value to your customers