Ship an agent with a budget
in three lines.
@kyvernlabs/sdk is the official client for KyvernLabs vaults. It talks to the same API that powers the dashboard — install, paste, ship.
Install
1npm install @kyvernlabs/sdk2# or3pnpm add @kyvernlabs/sdk4# or5yarn add @kyvernlabs/sdk
Node 18+ or any runtime with a global fetch. The package has zero runtime dependencies.
Quickstart
Create a vault
kv_live_… agent key — copy it somewhere safe.Set the env var
1export KYVERNLABS_AGENT_KEY=kv_live_...2export VAULT_ID=vlt_...
Pay a merchant
1import { Vault } from "@kyvernlabs/sdk";23const vault = new Vault({4agentKey: process.env.KYVERNLABS_AGENT_KEY!,5});67const res = await vault.pay({8merchant: "api.openai.com",9recipientPubkey: "5eyKt4yXtD9Wz8gPWs9fEUv9AQCoTFv9o6xAiBm1Kjv6",10amount: 0.12,11memo: "forecast lookup",12});1314if (res.decision === "blocked") {15console.log("refused:", res.code, res.reason);16} else {17console.log("settled:", res.tx.signature);18}
vault.pay()
Ask the vault to pay a merchant. Policy is enforced server-side — every call is budgeted, rate-limited, and matched against the allowlist before Squads co-signs.
1await vault.pay({2merchant: string, // URL or host, normalized server-side3recipientPubkey: string, // Solana pubkey of the USDC recipient4amount: number, // USD (USDC mint, 6 decimals)5memo?: string, // required if vault.requireMemo === true6}): Promise<PayResult>
Returns { decision: "allowed" | "blocked", … }. Pass throwOnBlocked: true to the constructor if you'd rather catch KyvernPaymentBlocked.
vault.status()
Snapshot of the vault — budget utilization, velocity, and the most recent payments. This is what the owner dashboard uses on every poll.
1const s = await vault.status({ vaultId: "vlt_abc", limit: 20 });23console.log(`${s.budget.spentToday} / ${s.budget.dailyLimitUsd} USD today`);4console.log(`${s.velocity.callsInWindow} calls in last ${s.velocity.velocityWindow}`);5for (const p of s.payments) {6console.log(p.createdAt, p.merchant, p.amountUsd, p.status, p.reason ?? "");7}
Kill switch
Pause blocks every future payment instantly. Existing policies stay intact — resume any time. Pause/resume require the ownerWallet at construction time.
1const owner = new Vault({2agentKey: process.env.KYVERNLABS_AGENT_KEY!,3ownerWallet: "5eyKt4yXtD9Wz8gPWs9fEUv9AQCoTFv9o6xAiBm1Kjv6",4});56await owner.pause({ vaultId: "vlt_abc" }); // agent is dead to the world7await owner.resume({ vaultId: "vlt_abc" }); // back online
Errors & decisions
Every pay() resolves with a decision. Blocks are not exceptions by default — they're data. The code is stable across versions so you can branch on it.
| Code | When |
|---|---|
| vault_paused | Owner hit the kill switch. |
| amount_exceeds_per_tx | A single payment exceeds the per-transaction ceiling. |
| amount_exceeds_daily | Today's spend plus this payment exceeds the 24h budget. |
| amount_exceeds_weekly | This week's spend plus this payment exceeds the 7d ceiling. |
| merchant_not_allowed | Merchant isn't in the vault's allowlist. |
| velocity_cap | Too many calls inside the velocity window. |
| missing_memo | Vault requires a memo and the payment didn't include one. |
| invalid_amount | Non-positive or non-finite amount. |
| invalid_merchant | Merchant string couldn't be parsed as a host or URL. |
Auth failures throw KyvernAuthError (401). Network or 5xx errors throw KyvernError with the HTTP status attached.
REST API
The SDK is a thin client. Here's the raw API for polyglot teams.
1curl -X POST https://kyvernlabs.com/api/vault/pay \2-H "Authorization: Bearer kv_live_..." \3-H "Content-Type: application/json" \4-d '{5"merchant": "api.openai.com",6"recipientPubkey": "5eyKt4yXtD9Wz8gPWs9fEUv9AQCoTFv9o6xAiBm1Kjv6",7"amountUsd": 0.12,8"memo": "forecast lookup"9}'
200 = settled · 402 = policy block · 401 = bad key · 400 = validation · 502 = Squads failure
