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_KEYfor management calls. - A customer key with
preflight:readandusage:write. UseSPENDWARE_CUSTOMER_KEYfor runtime usage. - Optional provider credentials such as
OPENAI_API_KEYwhen you want the provider-adapter path. - Python package
spendware-provider-sdkor npm package@spendware/provider-adapters-sdkif 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¶
- Use provider adapters to wrap actual AI provider clients.
- Learn preflight and usage logging for production runtime behavior.
- Use partial usage logging for user-key imports where project, customer, or provider records are finalized later.
- Add webhooks for event-driven integration.
- Review authentication, errors, and idempotency.
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.