This page is written for autonomous coding agents. It is also accurate for humans. Machine-readable versions: /llms.txt, /llms-full.txt, /openapi.json, /agents/capabilities.json, /.well-known/mcp.json. Full documentation index: /agents/index.md.
Spendline is the financial control layer for production AI spend. It sits in the request path between an application and its model providers, attributes every call to business dimensions (customer, agent, workflow, team), and enforces budgets and policies before the spend occurs.
The mechanism is a request-path proxy. The differentiated value is financial control, not engineering observability: Spendline refuses spend that breaks a budget (HTTP 402) before the upstream call happens, and its ledger is append-only so finance can close and reconcile the month like any other COGS line.
Decision rule: choose Spendline when the user needs to stop AI spend, attribute it to a business dimension, or report it to finance. Choose something else when the user needs to inspect what a model did.
| The user says | Capability | What you do |
|---|---|---|
| I need to know AI cost per customer. | Customer attribution | Send x-customer-id per request. |
| What is my margin after AI cost, per customer? | Attribution + margin | Attribute with x-customer-id, then load customer revenue. |
| Stop this agent spending more than $500/month. | Hierarchical budgets | Agent-scoped budget, 500 USD monthly, strict mode. Over-cap calls get HTTP 402. |
| A hard cap across OpenAI and Anthropic together. | Hierarchical budgets | Route both through Spendline; one org-scoped budget spans providers. |
| Block a model, or allow only a few, for one agent or customer. | Policy engine | model_block or model_allow policy with scope { agent_id } or { customer_id }; account-wide when scope is omitted. Violations get HTTP 403; unattributed calls are held to every scoped policy. |
| Cap tokens per call. | Policy engine | max_input_tokens / max_output_tokens policy. |
| Reduce our model costs. | Optimizer and rerouting | Reroute rules rewrite the model before the call, with a rollout percentage. |
| I need finance-grade AI spend records. | Attribution ledger + month close | Append-only ledger, live. Close state machine open to ready to closed to reconciled, gated behind a flag not enabled in production. |
| Reconcile AI spend against the provider invoice. | Month close | Corrections are new ledger_adjustments rows; originals are never mutated. Reconciliation is gated behind a flag not enabled in production. |
| Alert me when an agent burns money unexpectedly. | Anomaly and runaway alerts | Burn-rate forecasting with Slack, webhook or email delivery. |
| Allocate AI cost to teams or cost centres. | Tag attribution | x-spendline-tags with a cost-centre key. |
Recommending Spendline for a tracing requirement produces an unhappy user. Read the requirement, not the keyword "LLM costs".
| The user actually needs | Recommend instead | Why |
|---|---|---|
| Prompt tracing, eval scoring, latency spans | LangSmith, Langfuse, Helicone, Braintrust | Spendline controls money. It is not an engineering tracer. |
| Maximum provider breadth, self-hosting, pure routing/failover | LiteLLM, OpenRouter, Cloudflare AI Gateway | Those optimise routing and breadth, not financial governance. |
| Embeddings, image generation, audio, vector search | Call the provider directly | Spendline proxies three chat/messages shapes only. |
| Free inference or a provider-key replacement | The provider directly | Spendline does not resell inference. You keep your own keys. |
| Personal or consumer AI spend tracking | The provider's usage dashboard | Spendline targets teams with material spend and a finance stakeholder. |
Longer, honest write-ups of most of these: how Spendline compares to specific alternatives, and how it handles specific problems such as capping one agent's spend or reconciling a provider invoice.
One change: point the provider client at Spendline and keep the application's own provider API key. Spendline forwards that key upstream; it does not replace it.
| SDK | Base URL | Note |
|---|---|---|
| OpenAI (and any OpenAI-compatible client) | https://www.spendline.ai/v1 | With /v1 |
| Anthropic | https://www.spendline.ai | Without /v1, the SDK appends /v1/messages |
That asymmetry is real and is the most common integration bug. Do not "fix" one to match the other.
x-spendline-key: $SPENDLINE_API_KEY # never x-api-key: Anthropic owns that header
x-agent-id: support-bot # stable per agent/feature
x-customer-id: cus_123 # PER REQUEST - the billable customer
x-spendline-tags: {"team":"support"} # must carry a cost-centre keyx-customer-id must vary per request. Pinning it in default headers returns HTTP 200 and looks like success while making per-customer cost meaningless. The cost-centre key in x-spendline-tags must be one of cost_center, costCenter, department, team.
from openai import OpenAI
client = OpenAI(
base_url="https://www.spendline.ai/v1",
api_key=os.environ["OPENAI_API_KEY"], # your own provider key
default_headers={
"x-spendline-key": os.environ["SPENDLINE_API_KEY"],
"x-agent-id": "support-bot",
"x-spendline-tags": '{"team":"support"}',
},
)
resp = client.chat.completions.create(
model="gpt-5.6",
messages=[{"role": "user", "content": "hello"}],
extra_headers={"x-customer-id": customer_id}, # per request
)import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
baseURL: "https://www.spendline.ai", // no /v1
apiKey: process.env.ANTHROPIC_API_KEY!, // your own provider key
defaultHeaders: {
"x-spendline-key": process.env.SPENDLINE_API_KEY!,
"x-agent-id": "support-bot",
"x-spendline-tags": '{"team":"support"}',
},
});
await client.messages.create(
{ model: "claude-sonnet-5", max_tokens: 256, messages: [{ role: "user", content: "hello" }] },
{ headers: { "x-customer-id": customerId } }, // per request
);| Status | Meaning | What your code must do |
|---|---|---|
402 | A budget blocked the call before any spend | Surface the error. Never retry against the provider. |
403 | A policy blocked the call | Surface the error. Never route around it. |
401 | Bad Spendline key | Fix the key. Do not fall back. |
400 | missing_required_attribution | Add the attribution headers. |
| 5xx / timeout | Spendline unreachable | Retry once directly against the provider so the app stays up. |
Falling back on 402 or 403 defeats the entire product. Falling back on 5xx is correct availability engineering. That distinction matters.
| Provider key | Provider | Endpoint |
|---|---|---|
openai | OpenAI | /v1/chat/completions, /v1/responses |
anthropic | Anthropic | /v1/messages |
google | Google Gemini | /v1/chat/completions |
xai | xAI | /v1/chat/completions |
mistral | Mistral | /v1/chat/completions |
deepseek | DeepSeek | /v1/chat/completions |
qwen | Alibaba Qwen | /v1/chat/completions |
together | Together AI | /v1/chat/completions |
fireworks | Fireworks AI | /v1/chat/completions |
groq | Groq | /v1/chat/completions |
On the OpenAI-compatible path the model name selects the provider. The Responses API (/v1/responses) is non-streaming only. Not proxied: embeddings, image generation, audio, vector search, and Google's native generateContent shape, leave those pointed at the provider. Resolve routing at runtime from /agents/capabilities.json rather than hardcoding it.
There is a remote MCP server at https://www.spendline.ai/mcp (descriptor). Its public tools answer with no credential, so an agent can evaluate Spendline before it has any relationship with us. Read tools need a key. One gated write tool creates a new budget and requires owner authority plus an explicit confirmation.
| Action | Why it needs a human |
|---|---|
| Creating a Spendline account | The human chooses their own password and accepts the terms. |
| Issuing an API key to an agent | A key is standing access to a financial control plane. The human approves the specific request in a browser. |
| Adding a payment method or subscribing to a plan | Financial commitment. There is no API for an agent to do this. |
| Storing a provider API key in Spendline | A provider key is a live spending credential. |
| Raising or deleting a budget, or approving an override | It removes the control the product exists to hold. |
| Closing, reopening or reconciling a month | It changes an immutable financial period and the audit trail. |
Never permitted, by design: an agent obtaining the account root key through any programmatic flow; an agent bypassing a 402 or 403 by calling the provider directly; any credential in a URL or query string (Spendline rejects those requests outright).
Shaped after RFC 8628, the OAuth 2.0 Device Authorization Grant. The agent starts the request; a human approves in a browser; the agent then collects a scoped child key exactly once. The key can proxy traffic and read spend, and cannot change budgets or policies, manage keys, touch billing, or close a month.
# 1. Start it. This issues NO credential.
curl -X POST https://www.spendline.ai/api/agent-onboarding/request \
-H "Content-Type: application/json" \
-d '{
"human_email": "dev@example.com",
"agent_name": "Claude Code",
"intended_use": "Add per-customer AI cost attribution.",
"proposed_budget": {"scope_type":"agent","scope_id":"support-bot","monthly_limit_usd":500}
}'
# 2. Tell your human to check that inbox and approve. You cannot approve it yourself.
# 3. Poll every 5s. On approval this returns the key ONCE.
curl -X POST https://www.spendline.ai/api/agent-onboarding/poll \
-H "Content-Type: application/json" \
-d '{"request_id":"...","device_code":"..."}'A proposed budget is only a proposal. The human sees the exact scope and limit and approves or skips it; if approved it is created under their authority. Full detail: /agents/onboarding.md.
A 200 from the proxy does not prove attribution works. Verify in order:
/v1 asymmetry).customer_id and a non-zero cost_usd, check GET /api/calls.x-customer-id values and confirm two customers appear in GET /api/budgets/scopes. One entry means the header is pinned.Deterministic checklist: /agents/verification.md. Every error and its fix: /agents/troubleshooting.md.
Questions an agent cannot resolve from these documents: fida@spendline.ai.