Ship an agent with a budget,
in three lines.
@kyvernlabs/sdk is the official client for Kyvern vaults. It talks to the same API that powers the /app 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
Mint an agent key
kv_live_… key is shown once — paste it into your env.Set the env vars
1export KYVERN_AGENT_KEY=kv_live_...2# optional: route earnings to a KAST-funded card3export MY_KAST_ADDRESS=...
Pay (5 lines)
1import { Vault, KastDestination } from "@kyvernlabs/sdk";23const vault = new Vault({ agentKey: process.env.KYVERN_AGENT_KEY! });4const myKast = KastDestination.fromAddress(process.env.MY_KAST_ADDRESS!);5const res = await vault.pay({ ...myKast, amount: 1.50, memo: "weekly yield share" });6if (res.decision !== "allowed") throw new Error(res.reason);
That's a real on-chain USDC transfer to the user's KAST deposit address — five lines, decided by the chain. The same policy that runs here also runs on every spend the agent makes.
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.checkAllowance()
Run the same off-chain policy check that pay() runs, but without making a payment. Returns the chain's verdict ahead of time so an agent can decide whether to fire the underlying paid call.
1await vault.checkAllowance({2merchant: string, // URL or host, normalized server-side3amount: number, // USD4memo?: string, // required if vault.requireMemo === true5}): Promise<{ decision: "allowed" | "blocked", reason?: string, code?: string }>
Use this when you're wrapping pay.sh, x402, or any 402-paywalled rail — the vault decides BEFORE the rail's wallet prompt fires.
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.
Wrap pay.sh with Kyvern in 4 lines
pay.sh is the Solana Foundation's payment layer for HTTP agents. It handles 402/x402/MPP challenges and asks the local wallet to sign. Their docs say “Real payments still require local user authorization.” Kyvern is what closes that gap — the chain takes the place of the wallet approval prompt so an agent can run autonomously without compromising safety.
1# 1. Install both tools.2brew install pay # macOS3# or4npm install -g @solana/pay # Linux/Windows5npm install @kyvernlabs/sdk67# 2. Scaffold a Kyvern-protected agent that pays pay.sh APIs.8npx create-kyvern-agent my-agent910# 3. The scaffolded agent calls pay.sh through your Kyvern vault.11# Every call passes through your on-chain policy. Drains, rogue12# endpoints, over-cap purchases — all refused before pay.sh sees them.13cd my-agent && npm start
The architectural moment
Before invoking pay.sh, ask the vault first:
1const allowance = await vault.checkAllowance({2merchant: "api.pay.sh",3amount: 0.001,4});5if (allowance.decision !== "allowed") {6// Kyvern refused — pay.sh is never invoked, no wallet prompt fires.7return console.warn("blocked:", allowance.reason);8}910// Kyvern allowed — pay.sh executes the 402-paywalled call.11const result = execSync(12`pay --sandbox curl https://pay.sh/<service-url>`,13{ encoding: "utf-8" },14);
Kyvern is compatible with pay.sh and any HTTP-402 payment rail. Not partnered with pay.sh. The composability is the integration.
Sending earnings to a KAST-funded card
Every KAST user has a Solana USDC deposit address. Send USDC there → it tops up their card → they spend at 150M+ merchants worldwide. Kyvern wraps that flow in a vault budget so an agent can route a share of accrued earnings to your card automatically.
Find your address
Allowlist as MY_KAST
kast.xyz to your vault's allowlist + persists the address.)Spend in five lines
1import { Vault, KastDestination } from "@kyvernlabs/sdk";23const vault = new Vault({ agentKey: process.env.KYVERN_AGENT_KEY! });4const myKast = KastDestination.fromAddress(process.env.MY_KAST_ADDRESS!);5const res = await vault.pay({ ...myKast, amount: 1.50, memo: "weekly yield share" });6if (res.decision !== "allowed") throw new Error(res.reason);
Don't have a KAST card? Get one →
Kyvern is compatible with KAST deposit rails. Not affiliated with KAST. We don't verify the address belongs to a KAST account — the user owns the address either way.
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
What this is, and what this isn't
What this is
- Financial safety infrastructure for autonomous agents on Solana — a smart safe, an on-chain policy program, an SDK, and a scaffolder.
- An on-chain policy program at
PpmZ…MSqcdeployed to devnet, enforcing per-tx caps, daily/weekly caps, merchant allowlists, memo requirements, velocity caps, and a kill switch. - Atlas, our reference agent, has been running on Solana devnet continuously since April 20, 2026 — every block and every settle is a real on-chain transaction you can verify on Explorer.
What this isn't
- A hardware device, an AI experience, a fully autonomous trading bot, mainnet-deployed, or a financial advisor. Mainnet audit in progress.
- A KAST partner. We route on-chain to a public Solana USDC deposit address that any KAST user owns. The integration speaks via the working flow + the affiliate link.
- A pay.sh partner or wrapper SDK. Kyvern is the policy layer above the rails — pay.sh executes the paid HTTP call; Kyvern decides which calls the agent is allowed to make. Both products' value compounds.
- Atlas's decisions are scripted by design. The moat is the financial control layer, not the intelligence — even a deliberately minimal agent demonstrates the thesis. LLM- driven Atlas is post-Frontier.
