Developer docs

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.

Setup

Install

bash
1npm install @kyvernlabs/sdk
2# or
3pnpm add @kyvernlabs/sdk
4# or
5yarn add @kyvernlabs/sdk

Node 18+ or any runtime with a global fetch. The package has zero runtime dependencies.

0 → first payment

Quickstart

1

Mint an agent key

Sign in at /app Agent keysMint a key. The kv_live_… key is shown once — paste it into your env.
2

Set the env vars

bash
1export KYVERN_AGENT_KEY=kv_live_...
2# optional: route earnings to a KAST-funded card
3export MY_KAST_ADDRESS=...
3

Pay (5 lines)

ts
1import { Vault, KastDestination } from "@kyvernlabs/sdk";
2
3const 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.

Method

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.

ts
1await vault.pay({
2 merchant: string, // URL or host, normalized server-side
3 recipientPubkey: string, // Solana pubkey of the USDC recipient
4 amount: number, // USD (USDC mint, 6 decimals)
5 memo?: string, // required if vault.requireMemo === true
6}): Promise<PayResult>

Returns { decision: "allowed" | "blocked", … }. Pass throwOnBlocked: true to the constructor if you'd rather catch KyvernPaymentBlocked.

Method

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.

ts
1await vault.checkAllowance({
2 merchant: string, // URL or host, normalized server-side
3 amount: number, // USD
4 memo?: string, // required if vault.requireMemo === true
5}): 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.

Method

vault.status()

Snapshot of the vault — budget utilization, velocity, and the most recent payments. This is what the owner dashboard uses on every poll.

ts
1const s = await vault.status({ vaultId: "vlt_abc", limit: 20 });
2
3console.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) {
6 console.log(p.createdAt, p.merchant, p.amountUsd, p.status, p.reason ?? "");
7}
Emergency stop

Kill switch

Pause blocks every future payment instantly. Existing policies stay intact — resume any time. Pause/resume require the ownerWallet at construction time.

ts
1const owner = new Vault({
2 agentKey: process.env.KYVERNLABS_AGENT_KEY!,
3 ownerWallet: "5eyKt4yXtD9Wz8gPWs9fEUv9AQCoTFv9o6xAiBm1Kjv6",
4});
5
6await owner.pause({ vaultId: "vlt_abc" }); // agent is dead to the world
7await owner.resume({ vaultId: "vlt_abc" }); // back online
Reference

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.

CodeWhen
vault_pausedOwner hit the kill switch.
amount_exceeds_per_txA single payment exceeds the per-transaction ceiling.
amount_exceeds_dailyToday's spend plus this payment exceeds the 24h budget.
amount_exceeds_weeklyThis week's spend plus this payment exceeds the 7d ceiling.
merchant_not_allowedMerchant isn't in the vault's allowlist.
velocity_capToo many calls inside the velocity window.
missing_memoVault requires a memo and the payment didn't include one.
invalid_amountNon-positive or non-finite amount.
invalid_merchantMerchant 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.

Headline guide

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.

bash
1# 1. Install both tools.
2brew install pay # macOS
3# or
4npm install -g @solana/pay # Linux/Windows
5npm install @kyvernlabs/sdk
6
7# 2. Scaffold a Kyvern-protected agent that pays pay.sh APIs.
8npx create-kyvern-agent my-agent
9
10# 3. The scaffolded agent calls pay.sh through your Kyvern vault.
11# Every call passes through your on-chain policy. Drains, rogue
12# 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:

ts
1const allowance = await vault.checkAllowance({
2 merchant: "api.pay.sh",
3 amount: 0.001,
4});
5if (allowance.decision !== "allowed") {
6 // Kyvern refused — pay.sh is never invoked, no wallet prompt fires.
7 return console.warn("blocked:", allowance.reason);
8}
9
10// 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.

Real-world payoff loop

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.

1

Find your address

Open the KAST app → DepositSolana USDC. Copy the address.
2

Allowlist as MY_KAST

In Kyvern /app, paste it under MY_KAST setup. Click Allowlist as MY_KAST. (This adds kast.xyz to your vault's allowlist + persists the address.)
3

Spend in five lines

ts
1import { Vault, KastDestination } from "@kyvernlabs/sdk";
2
3const 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.

Prefer curl?

REST API

The SDK is a thin client. Here's the raw API for polyglot teams.

bash
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

Honesty

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…MSqc deployed 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.
Keep building

What's next