Valta Docs

Valta Cap

Available now, via POST /api/v1/cap/allow and valta.cap.* in the SDK. V1 is cooperative, not enforced — see How enforcement actually works before you rely on this.

Valta Cap answers one question: is this agent allowed to spend another $X? That's it. It does not cap an OpenAI project, it does not touch your provider's billing dashboard, and it does not replace OpenAI or Anthropic's own spend controls. It's a per-agent yes/no gate you call from your own code, backed by a real per-run/day/month ledger, real freeze support, and the same hash-chained audit trail the rest of Valta uses.

Dashboard: not its own sidebar page — go to Agents, open the specific agent, and scroll to the Cap section of that agent's settings page (/dashboard/agents/<agentId>). Enable it, set the three limits, and you'll find a copy-paste snippet there with this exact agent's id already filled in (OpenAI or Anthropic — pick a tab).

The model — read this before you build

Two keys, both stay in your own environment, never in the Valta dashboard:

bash
OPENAI_API_KEY=sk-proj-...     # or ANTHROPIC_API_KEY=sk-ant-...
VALTA_API_KEY=vlt_live_...
VALTA_AGENT_ID=ag_...

The flow:

your code → valta.cap.allow() → denied? stop, don't call the provider → approved? your code → OpenAI/Anthropic (your key, their bill)

A "Valta agent" in Cap mode is just a policy bucket — a name, three limits, a freeze flag, a ledger, and an audit trail. Valta never talks to OpenAI or Anthropic on your behalf.

Say this plainly, because it trips people up:

  • Creating a Cap and never calling allow() does nothing to OpenAI. No limit exists on OpenAI's side because of Cap — OpenAI has never heard of Valta.
  • Cursor, ChatGPT, and Claude.ai usage is not covered unless the code or MCP server actually calling the provider also calls allow() first. Wrapping an IDE's own chat is out of scope for v1.
  • Valta never stores your provider key. There's no field for it anywhere — not in the dashboard, not in the API, not in the SDK's types.
  • A hosted proxy is planned, not available. Until it ships, a skipped allow() call is invisible to Valta. Keep the product's yellow warning in mind — it's accurate, not overcautious.

Cap vs. Spending Policies — which one applies to you

Spending Policies (/dashboard/spending-policies)Cap (this page)
For an agent that...has a funded Valta wallethas no wallet at all
Who holds the moneyValta doesNobody — your own provider key, outside Valta
EnforcementReal. Valta is the one debiting the wallet, so it can simply refuse to move money that would break a limit.Cooperative. Your code has to call allow() and honor the answer.
FieldsmaxPerTransaction, dailyLimit, weeklyLimit, monthlyLimit, requireApprovalAbove, categories, domainsperRunLimit, dailyLimit, monthlyLimit only

Cap mode and wallet mode are two modes on the same underlying policy per agent — see Spending Policies if this agent should actually hold Valta-custodied money instead.

Working example

TypeScript

ts
import OpenAI from "openai";
import { ValtaClient } from "valta-sdk";

const valta = new ValtaClient({ apiKey: process.env.VALTA_API_KEY! });
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

const gate = await valta.cap.allow({
  agent: process.env.VALTA_AGENT_ID!,
  runId: crypto.randomUUID(),
  estimatedUsd: 0.05,
  merchant: "openai",
  model: "gpt-4.1",
  purpose: "support-reply",
});

if (!gate.approved) {
  console.error(gate.reason, gate.remaining);
  process.exit(1);
}

const completion = await openai.chat.completions.create({
  model: "gpt-4.1",
  messages: [{ role: "user", content: "Hello" }],
});

// v1 trues the ledger up from estimatedUsd using whatever cost estimate you
// derive yourself from completion.usage — Valta doesn't compute a rate table
// for you. report() only ever adjusts the ledger DOWN, never up.
await valta.cap.report({
  allowId: gate.id,
  actualUsd: 0.043, // your own estimate from completion.usage
});

Same shape for Anthropic — swap the client and the env var:

ts
import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
// ...same valta.cap.allow()/report() calls, merchant: "anthropic"

Python

The Python SDK's cap resource matches this shape but currently lives on a separate, unmerged branch — not yet part of a pip install valta-python-sdk release. This example shows the intended interface; confirm it's live before relying on it in production.

python
import os
import uuid
from openai import OpenAI
from valta import ValtaClient

valta = ValtaClient(api_key=os.environ["VALTA_API_KEY"])
openai = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

gate = valta.cap.allow(
    agent=os.environ["VALTA_AGENT_ID"],
    run_id=str(uuid.uuid4()),
    estimated_usd=0.05,
    merchant="openai",
    model="gpt-4.1",
)

if not gate.approved:
    raise RuntimeError(f"Cap denied: {gate.reason}")

completion = openai.chat.completions.create(
    model="gpt-4.1",
    messages=[{"role": "user", "content": "Hello"}],
)

valta.cap.report(allow_id=gate.id, actual_usd=0.043)

Setup

  1. Create or open a Valta agent.
  2. On that agent's Cap section, enable Cap and set per-run / per-day / per-month limits (leave a field blank for no limit).
  3. Create a Valta API key from API Keys in the dashboard.
  4. Keep your provider key (OPENAI_API_KEY/ANTHROPIC_API_KEY) in your own host environment — never paste it into Valta.
  5. Call allow() before every paid provider request; stop if it denies.
  6. Freeze the agent from its dashboard page any time to deny the next allow() call without touching OpenAI or Anthropic at all.

How enforcement actually works

Cap v1 checks run in your own code path. Nothing at the network level stops your code from skipping allow() and calling the provider directly — Valta only sees a call if you ask it first. This is the same trust model as any client-side rate limiter: honest by default, not tamper-proof. A hosted proxy that enforces this even when the SDK call is skipped is planned, not built.

API reference

Auth: the same API key every other /api/v1/* route uses — x-api-key: vlt_live_... (an Authorization: Bearer header is also accepted).

POST /api/v1/cap/allow

json
// Request
{ "agent": "ag_123", "runId": "run_1", "estimatedUsd": 0.05, "merchant": "openai", "model": "gpt-4.1", "purpose": "support-reply" }

estimatedUsd must be greater than 0 — a zero or negative value is a 400.

json
// Response — approved
{ "approved": true, "id": "allow_9f2a...", "remaining": { "run": 0.95, "day": 4.20, "month": 89.50 }, "remainingPlanUsd": 1949.55 }

// Response — denied (agent-level limit)
{ "approved": false, "reason": "daily_limit", "id": "allow_9f2a...", "remaining": { "run": null, "day": 0, "month": 89.50 } }

// Response — denied (your Valta plan's own monthly Cap budget, not a per-agent limit)
{ "approved": false, "reason": "plan_limit", "id": "allow_9f2a...", "remaining": { "run": 0.95, "day": 4.20, "month": 89.50 }, "remainingPlanUsd": 0 }

remainingPlanUsd is your account's plan-wide Cap budget (all agents combined) left for this calendar month, not this one agent's own remaining limit — see Billing below.

POST /api/v1/cap/report

json
// Request
{ "allowId": "allow_9f2a...", "actualUsd": 0.043 }

Adjusts the ledger down if actualUsd is less than what allow() counted — never up, even if the real cost came in higher (see the worked example above for why). 404 if allowId doesn't match a real, un-reported allow decision on your account.

GET /api/v1/cap/usage?agent=&runId=

Returns limits, frozen, capEnabled, spend for the current run (if runId passed) / day / month, and a computed remaining for each — plus your account's plan-wide Cap billing picture: plan, capAgentsUsed, capAgentsMax, trackedUsdMonth, trackedUsdLimit, resetAt (ISO timestamp of the next calendar-month reset). The plan fields are account-wide, not specific to the agent you passed.

POST /api/v1/cap/settings

json
// Request
{ "agent": "ag_123", "capEnabled": true, "perRunLimit": 0.10, "dailyLimit": 1.00, "monthlyLimit": 20.00 }

The API equivalent of the dashboard's Enable Cap + Save limits. Omit a limit (or send null) for no limit.

Deny reasons

ReasonMeaning
frozenThe agent is frozen — freeze it from its dashboard page to deny the next allow() immediately.
cap_disabledCap isn't enabled for this agent (no policy, or Cap was never turned on).
per_run_limitThis single run's spend would exceed the per-run limit.
daily_limitToday's cumulative spend would exceed the daily limit.
monthly_limitThis month's cumulative spend would exceed the monthly limit.
plan_agent_limitYour Valta plan's Cap agent limit is already used by other agents this month, and this agent hasn't used its seat yet.
plan_limitYour Valta plan's Cap USD budget for this calendar month is used up. See Billing below.

Billing

Leak is free. valta-leak — the static-analysis CLI (npx valta-leak) — always is, no account, no metered charge, regardless of anything below.

Cap is a Valta subscription, not a per-call charge. Your Valta plan sets two Cap-specific numbers: how many distinct agents may use Cap in a calendar month (capAgentsMax), and how many tracked USD those agents' allow() calls may sum to in that month (capTrackedUsdMonth). Free is 1 agent / $50 a month; Builder ($29/mo) is 10 agents / $2,000 a month; Startup ($99/mo) is 50 agents / $10,000 a month. Enterprise has no Cap-specific cap.

OpenAI (or Anthropic, or any other provider) bills tokens separately — always. Cap's plan limit is Valta's own bucket for how many dollars of tracked, allowed spend your account gets to check per month; it is never a charge on top of your provider bill, and Cap never sees or touches that bill.

plan_limit means the Valta plan bucket is empty, not that OpenAI is down. If allow() returns plan_limit, your provider account is fine — your Valta plan's monthly Cap budget is what's exhausted. Upgrade from the agent's Cap card in the dashboard, or via POST /api/subscriptions/checkout, to raise it; the new limit applies immediately.

FAQ

Do I put my OpenAI key in Valta? No. OPENAI_API_KEY/ANTHROPIC_API_KEY stays in your own environment. Only VALTA_API_KEY is sent to Valta.

Will this change my OpenAI spend limit in their dashboard? No. Cap has no connection to your OpenAI or Anthropic account settings at all.

I set a cap and still got billed. Why? Your code called the provider without calling allow() first, or called it after a deny. Cap can't see or block a call it wasn't asked about.

Does Cap wrap Cursor? No. Only the process that actually calls allow() is covered — an IDE's own chat isn't unless you've wired it to call Valta yourself.

How is this different from OpenAI's hard spend limits? OpenAI's limits cap the whole org or project, monthly. Cap limits one specific agent, per run/day/month, and can freeze that one agent without touching anything else.

When do I need a Valta wallet, card, or USDC? Not for Cap. Those only matter when an agent should actually hold a Valta-custodied balance and move money through Valta — see Spending Policies.

Next steps