Skip to content

Quickstart

This quickstart creates the minimum objects needed for a runtime integration, logs one usage event, and verifies that Spendware can read it back.

Prerequisites

  • A Spendware SaaS workspace and the hosted API base URL for your account.
  • A user API key with management permissions. Use SPENDWARE_API_KEY for management calls.
  • A customer key with preflight:read and usage:write. Use SPENDWARE_CUSTOMER_KEY for runtime usage.
  • Optional provider credentials such as OPENAI_API_KEY when you want the provider-adapter path.
  • Python package spendware-provider-sdk or npm package @spendware/provider-adapters-sdk if you are using an SDK path.

You do NOT need a Stripe account, Stripe API keys, or any Stripe integration to bill your customers through Spendware. Spendware handles all end-customer billing (cycles, invoices, ledger, templates) natively.

Use these placeholders throughout the guide:

export SPENDWARE_BASE_URL=https://api.spendware.dev
export SPENDWARE_API_KEY=spendware_uk_...
export SPENDWARE_CUSTOMER_KEY=spendware_ck_...

Create A Project, Customer, And Runtime Key

export SPENDWARE_BASE_URL=https://api.spendware.dev
export SPENDWARE_API_KEY=spendware_uk_...

PROJECT_ID=$(curl -sS "$SPENDWARE_BASE_URL/api/v1/projects/" \
  -H "Authorization: Bearer $SPENDWARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Acme AI","currency":"USD","external_id":"acme-ai"}' \
  | python -c 'import json,sys; print(json.load(sys.stdin)["id"])')

CUSTOMER_ID=$(curl -sS "$SPENDWARE_BASE_URL/api/v1/customers/" \
  -H "Authorization: Bearer $SPENDWARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"project_id\":\"$PROJECT_ID\",\"title\":\"Customer A\",\"external_id\":\"customer-a\"}" \
  | python -c 'import json,sys; print(json.load(sys.stdin)["id"])')

curl -sS "$SPENDWARE_BASE_URL/api/v1/customer-keys/" \
  -H "Authorization: Bearer $SPENDWARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"end_customer_id\":\"$CUSTOMER_ID\",\"name\":\"Runtime SDK key\",\"scopes\":[\"preflight:read\",\"usage:write\"]}"
import os

from spendware_provider_sdk.management import (
    EndCustomerCreate,
    EndCustomerKeyCreate,
    ProjectCreate,
    SpendwareManagementClient,
)

client = SpendwareManagementClient(
    base_url=os.environ["SPENDWARE_BASE_URL"],
    api_key=os.environ["SPENDWARE_API_KEY"],
)

project = client.projects.create(
    ProjectCreate(title="Acme AI", currency="USD", external_id="acme-ai"),
    idempotency_key="project:acme-ai",
)
customer = client.endcustomers.create(
    EndCustomerCreate(project_id=project.id, title="Customer A", external_id="customer-a")
)
customer_key = client.endcustomer_keys.create(
    EndCustomerKeyCreate(
        end_customer_id=customer.id,
        name="Runtime SDK key",
        scopes=["preflight:read", "usage:write"],
    )
)

print(customer_key.key)
client.close()
import { SpendwareManagementClient } from "@spendware/provider-adapters-sdk/management";

const client = new SpendwareManagementClient({
  baseUrl: process.env.SPENDWARE_BASE_URL ?? "https://api.spendware.dev",
  apiKey: process.env.SPENDWARE_API_KEY,
});

const project = await client.projects.create(
  { title: "Acme AI", currency: "USD", external_id: "acme-ai" },
  { idempotencyKey: "project:acme-ai" },
);
const customer = await client.customers.create({
  project_id: project.id,
  title: "Customer A",
  external_id: "customer-a",
});
const customerKey = await client.customerKeys.create({
  end_customer_id: customer.id,
  name: "Runtime SDK key",
  scopes: ["preflight:read", "usage:write"],
});

console.log(customerKey.key);

Store the full customer key immediately. The full key is shown once.

Customer keys are the recommended runtime credential because they are limited to the selected customer. A user API key is an advanced alternative for a single-key integration; configure it with usage:write and pass the intended customer ID in runtime context on every request. Do not use management scopes in provider-adapter traffic.

Log A Usage Event

Use the customer key from the previous step as SPENDWARE_CUSTOMER_KEY.

curl "$SPENDWARE_BASE_URL/api/v1/usage/log-with-key" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "SPENDWARE_CUSTOMER_KEY",
    "service_provider_name": "openai",
    "service_name": "gpt-4o",
    "number_requests": 1,
    "input_tokens": 120,
    "output_tokens": 45,
    "cached_tokens": 30,
    "feature": "chat",
    "idempotency_key": "usage:event-001"
  }'
from spendware_provider_sdk.management import SpendwareManagementClient

client = SpendwareManagementClient(base_url="SPENDWARE_BASE_URL", api_key="SPENDWARE_CUSTOMER_KEY")
response = client.usage.log_with_key(
    {
        "key": "SPENDWARE_CUSTOMER_KEY",
        "service_provider_name": "openai",
        "service_name": "gpt-4o",
        "number_requests": 1,
        "input_tokens": 120,
        "output_tokens": 45,
        "cached_tokens": 30,
        "feature": "chat",
        "idempotency_key": "usage:event-001",
    }
)
print(response)
client.close()
import { SpendwareManagementClient } from "@spendware/provider-adapters-sdk/management";

const client = new SpendwareManagementClient({
  baseUrl: "SPENDWARE_BASE_URL",
  apiKey: "SPENDWARE_CUSTOMER_KEY",
});

const response = await client.usage.logWithKey({
  key: "SPENDWARE_CUSTOMER_KEY",
  service_provider_name: "openai",
  service_name: "gpt-4o",
  number_requests: 1,
  input_tokens: 120,
  output_tokens: 45,
  cached_tokens: 30,
  feature: "chat",
  idempotency_key: "usage:event-001",
});
console.log(response);

Log Through A Provider Adapter

Provider adapters are the recommended SDK path when your application already uses a supported AI provider client. The adapter runs preflight, calls the provider, extracts usage from the provider response, and logs usage to Spendware when you call shutdown() or when the async logging queue flushes.

# Provider adapters are SDK-only. This raw request shows the equivalent
# usage log that the adapter sends after extracting provider usage.
curl "$SPENDWARE_BASE_URL/api/v1/usage/log-with-key" \
  -H "Content-Type: application/json" \
  -d '{
    "key": "SPENDWARE_CUSTOMER_KEY",
    "service_provider_name": "openai",
    "service_name": "gpt-4o-mini",
    "number_requests": 1,
    "input_tokens": 120,
    "output_tokens": 45,
    "cached_tokens": 0,
    "feature": "quickstart"
  }'
import os

from openai import OpenAI
from spendware_provider_sdk import create_spendware_config, wrap_openai_client
from spendware_provider_sdk.core.adapter_types import AdapterCommonOptions
from spendware_provider_sdk.core.types import KeyAuthConfig

openai_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
config = create_spendware_config(
    spendware_base_url=os.environ["SPENDWARE_BASE_URL"],
    auth=KeyAuthConfig(customer_key=os.environ["SPENDWARE_CUSTOMER_KEY"]),
)
wrapped = wrap_openai_client(openai_client, AdapterCommonOptions(config=config))

response = wrapped.client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Write one sentence about usage metering."}],
    spendware={"runtime_context": {"feature": "quickstart"}},
)
print(response.choices[0].message.content)
wrapped.shutdown()
import OpenAI from "openai";
import { createSpendwareConfig, wrapOpenAIClient } from "@spendware/provider-adapters-sdk";

const openaiClient = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const config = createSpendwareConfig({
  spendwareBaseUrl: process.env.SPENDWARE_BASE_URL ?? "https://api.spendware.dev",
  auth: { mode: "customer_key", customerKey: process.env.SPENDWARE_CUSTOMER_KEY },
});
const wrapped = wrapOpenAIClient(openaiClient, { config });

const response = await wrapped.client.chat.completions.create(
  {
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: "Write one sentence about usage metering." }],
  },
  { spendware: { runtimeContext: { feature: "quickstart" } } },
);
console.log(response.choices[0]?.message?.content);
await wrapped.shutdown();

Verify

The log response should include status, and may include event_id, cost_computed, currency, and action. Then query usage with your management key:

curl "$SPENDWARE_BASE_URL/api/v1/usage/query-usage?include_statistics=true" \
  -H "Authorization: Bearer $SPENDWARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"customer_ids\":[\"$CUSTOMER_ID\"]}"
client = SpendwareManagementClient(base_url="SPENDWARE_BASE_URL", api_key="SPENDWARE_API_KEY")
usage = client.usage.query({"customer_ids": ["CUSTOMER_ID"]}, include_statistics=True)
print(usage)
client.close()
const client = new SpendwareManagementClient({ baseUrl: "SPENDWARE_BASE_URL", apiKey: "SPENDWARE_API_KEY" });
const usage = await client.usage.query({ customer_ids: ["CUSTOMER_ID"] }, { includeStatistics: true });
console.log(usage);

Next Steps

If a newly created runtime key is lost before it is stored, create a replacement or rotate the key. Spendware intentionally cannot reveal it again.