Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.relayer.fi/llms.txt

Use this file to discover all available pages before exploring further.

@relayerfi/agent-sdk is the runtime SDK your agent loads. It signs every API call with HMAC-SHA256, enforces 3-layer budgets, polls the kill switch, batches telemetry, and orchestrates x402 payments + human approvals — all behind a small public surface.
New to the Agent Kit? Start with Getting Started for the full journey from workspace signup to running agent.

Package

PackageVersionPurpose
@relayerfi/agent-sdk1.0.0RelayerSDK facade + HMAC, budget, kill switch, x402, approvals, LLM wrappers, Mastra hook
Install:
pnpm add @relayerfi/agent-sdk
# Optional, for Mastra integration:
pnpm add @mastra/core
Requirements: Node.js ≥ 22.13.0, ESM, TypeScript ≥ 5.0 for typings.

Entry points

// Core SDK
import { RelayerSDK, x402fetch, wrapAnthropic, wrapOpenAI, wrapGoogle } from "@relayerfi/agent-sdk";

// Errors
import {
  RelayerError,
  RelayerApiError,
  BudgetExhaustedError,
  KillSwitchActiveError,
  ApiUnreachableError,
  X402PaymentError,
  ApprovalTimeoutError,
  ApprovalRejectedError,
} from "@relayerfi/agent-sdk";

// Mastra integration (optional peer dep on @mastra/core)
import { relayerMastraHook } from "@relayerfi/agent-sdk/mastra";

RelayerSDK

The main facade. Construct once per agent process and reuse.

Constructor

new RelayerSDK(config: RelayerSDKConfig)
agentId
string
required
The agent UUID, issued by POST /v1/agents/confirm-policies at provisioning time. Typically: process.env.RELAYER_AGENT_ID.
secret
string
required
The agent’s HMAC secret, also issued by POST /v1/agents/confirm-policiesshown once, never retrievable. Typically: process.env.RELAYER_AGENT_SECRET.
apiUrl
string
required
Base URL of the Relayer API. https://api.relayer.fi for production, https://testnet.relayer.fi for sandbox. Typically: process.env.RELAYER_API_URL.
retries
number
default:"3"
Number of retry attempts on HTTP failure. Retries 429 and 5xx. Non-429 4xx errors never retry (caller bug, not transient).
retryBaseMs
number
default:"1000"
Base backoff in ms. Exponential with jitter, capped at 10s.
timeoutMs
number
default:"30000"
Per-request timeout in ms (via AbortSignal.timeout).
killSwitchPollInterval
number
default:"30000"
How often the kill-switch poller hits GET /v1/agents/{id}/status. Lower = faster reaction to a kill, higher = less API noise.
eventFlushInterval
number
default:"10000"
How often the event batcher flushes to POST /v1/agents/events/batch. Flushes earlier if the buffer reaches eventBatchSize or if a payment event arrives (immediate).
eventBatchSize
number
default:"50"
Max events per flush. The batcher splices the whole buffer when flushing.
approvalPollInterval
number
default:"5000"
How often x402fetch polls GET /v1/signing/approvals/{id} while waiting for human approval.
approvalTimeout
number
default:"300000"
Max time to wait for an approval before throwing ApprovalTimeoutError. Default 5 minutes.
autoShutdown
boolean
default:"true"
Register SIGTERM / beforeExit handlers that call shutdown() and flush pending events. Disable if you manage shutdown manually.
logger
Logger
Optional logger with info(msg, data?), warn(msg, data?), error(msg, data?). Undefined by default — no logs are printed.

Minimal init

import { RelayerSDK } from "@relayerfi/agent-sdk";

export const sdk = new RelayerSDK({
  agentId: process.env.RELAYER_AGENT_ID!,
  secret: process.env.RELAYER_AGENT_SECRET!,
  apiUrl: process.env.RELAYER_API_URL!,
});
The constructor:
  1. Validates the 3 required fields (throws synchronously if missing)
  2. Builds an HttpClient with HMAC-SHA256 signing
  3. Starts the kill-switch poller (immediate first call, then interval)
  4. Starts the event batcher’s interval flush
  5. Registers shutdown handlers (unless autoShutdown: false)

Methods

checkBudget(): Promise<void>

Checks budget layers 1+2 only (infra + tokens). Use for telemetry-only operations that should still proceed if only payments are exhausted.
await sdk.checkBudget(); // throws BudgetExhaustedError if infra OR tokens exhausted
Implementation: hits GET /v1/agents/{id}/budget, throws BudgetExhaustedError([...failed]) with the array of exhausted layers. Layer 3 (payments) is not checked here.

checkPaymentBudget(): Promise<void>

Checks all 3 layers plus the kill switch. Use before any USDC outflow.
await sdk.checkPaymentBudget();
// throws KillSwitchActiveError if kill switch is active
// throws BudgetExhaustedError(["infra"|"tokens"|"payments"]) if any layer is empty
Order: kill switch check first, then budget query. The kill switch is isBlocked if agent.killSwitch === true or the API is unreachable (fail-safe).

isKillSwitchActive: boolean (getter)

true if the kill switch is currently active OR the API was unreachable on the last poll.
if (sdk.isKillSwitchActive) {
  // Skip payment operations; safe to continue with infra-only work.
}

emitEvent(event: RelayerEvent): void

Adds an event to the batch buffer. Returns immediately. The batch flushes:
  • On the configured interval (default 10s)
  • When buffer length reaches eventBatchSize (default 50)
  • Immediately if event.type === "payment"
sdk.emitEvent({
  type: "api_call",
  timestamp: new Date().toISOString(),
  agentId: sdk.agentId,
  data: { endpoint: "https://example.com/api", cost_usd: "0.001" },
});
Event types: payment, token_usage, llm_call, api_call, budget_check, kill_switch, error. Failure handling inside the batcher:
  • 429 and 5xx from event batch endpoint → re-enqueue with 60s backoff
  • 4xx (non-429) → drop the batch (permanent failure, caller bug)
  • Network error / timeout → re-enqueue with 60s backoff

createUserWallet(input): Promise<{ wallet_id, addresses }>

Asks the API to stamp a CREATE_WALLET activity using the agent’s own P-256 key. Requires the agent’s policies to include Allow: Agent — Create Wallets.
const { wallet_id, addresses } = await sdk.createUserWallet({
  walletName: "Operations wallet",
  // optional — defaults to Solana ED25519
  accounts: [{ curve: "ed25519", pathFormat: "...", path: "m/44'/501'/0'/0'", addressFormat: "solana" }],
});
Calls POST /v1/agents/wallets. The wallet is owned by the agent itself, not by the agent’s parent workspace.

buildAuthHeaders(method, path, body?): Record<string, string>

Returns the 4 headers the API expects for HMAC-authenticated requests. Useful if you bypass the SDK’s HTTP client (e.g. WebSocket auth):
const headers = sdk.buildAuthHeaders("POST", "/v1/agents/self-bookkeeping", { foo: "bar" });
// {
//   "x-agent-id":          "agt_abc123",
//   "x-agent-auth":        "ad3f...",        // hex HMAC-SHA256
//   "x-request-timestamp": "1715772600",
//   "X-SDK-Version":       "@relayerfi/agent-sdk@1.0.0"
// }
Payload: ${method}${path}${timestamp}${sha256(body)}. The signature is HMAC-SHA256 over that string with the agent secret. Empty body → sha256(""). The server validates the timestamp window (±60s).

http: HttpClient (getter)

Direct access to the underlying HTTP client. Use for endpoints the SDK doesn’t expose as first-class methods:
const data = await sdk.http.post("/v1/agents/" + sdk.agentId + "/sign-transaction", {
  unsignedTx: base64Tx,
});
HttpClient exposes get<T>(path), post<T>(path, body), agentId, apiUrl, and buildAuthHeaders(...).

agentId: string and apiUrl: string (getters)

Convenience accessors:
sdk.agentId;  // "agt_abc123"
sdk.apiUrl;   // "https://api.relayer.fi"

budget (object)

Tiny accessor for the cached budget snapshot. The cache is populated automatically when API responses include budget_status (server-pushed updates).
sdk.budget.get();      // BudgetStatus | null
// sdk.budget.refresh()  // not implemented yet — throws NotImplementedError

shutdown(): Promise<void>

Stops the kill-switch poll, stops the approval handler, flushes pending events, stops the event batcher. Idempotent. Called automatically on SIGTERM and beforeExit if autoShutdown is true (default).
await sdk.shutdown();

x402fetch

A fetch-compatible wrapper that ties together: budget check, HMAC auth, x402 detection, payment, approval, and retry.
import { x402fetch } from "@relayerfi/agent-sdk";

const response = await x402fetch(sdk, "https://paid-api.example.com/v1/run", {
  method: "POST",
  body: JSON.stringify({ input: "..." }),
});
const data = await response.json();

What it does, step by step

  1. Budget checksdk.checkPaymentBudget(). Throws BudgetExhaustedError or KillSwitchActiveError before any network call.
  2. Auth headers — builds HMAC headers for the external URL (path-only, since the host is external).
  3. First request — sends with the original body and headers.
  4. 402 handling — if the server returns 402 Payment Required, extracts the paymentRequirement from the body, calls POST /v1/agents/{id}/x402-pay, gets a paymentHeader, retries the request with that header. Emits a payment event on success.
  5. 202 handling — if the server returns 202 Accepted with an approvalId, suspends and polls GET /v1/signing/approvals/{id} every 5s. On approval, retries the original request. On rejection, throws ApprovalRejectedError. On timeout, throws ApprovalTimeoutError.
  6. Everything else — passes through unchanged.

When to use

  • Calling any third-party API that’s x402-gated
  • Any operation where you want budget + approval + payment in one call
If you only need HMAC auth without payment handling, use sdk.http.post(...) directly.

LLM Wrappers

ES Proxy wrappers that intercept LLM provider calls and emit llm_call events with token counts. Zero overhead until the first LLM call — the proxy is lazy.

wrapAnthropic<T>(client: T, sdk: RelayerSDK): T

import Anthropic from "@anthropic-ai/sdk";
import { wrapAnthropic } from "@relayerfi/agent-sdk";

const raw = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const anthropic = wrapAnthropic(raw, sdk);

// Use exactly like the original — emits llm_call events automatically
const response = await anthropic.messages.create({
  model: "claude-opus-4-5",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Hello" }],
});
What gets tracked: provider: "anthropic", model, tokens_input, tokens_output extracted from response.usage.

wrapOpenAI<T>(client: T, sdk: RelayerSDK): T

Same pattern. Intercepts client.chat.completions.create() and extracts response.usage.{prompt_tokens, completion_tokens}.

wrapGoogle<T>(client: T, sdk: RelayerSDK): T

Same pattern. Intercepts the Gemini SDK’s generation calls.

Type safety

The wrappers return the same type as the input client. So wrapAnthropic(new Anthropic(...)) returns an Anthropic instance from your IDE’s perspective. No type juggling.

Failure mode

Tracking errors are swallowed silently — if event emission fails, the LLM call still succeeds. Logged via the SDK logger if one is configured.

Mastra Integration

For agents built with Mastra. The hook plugs into agent.generate({ onStepFinish }).

relayerMastraHook(sdk: RelayerSDK)

import { Agent } from "@mastra/core/agent";
import { relayerMastraHook } from "@relayerfi/agent-sdk/mastra";
import { sdk } from "./sdk";

const agent = new Agent({ /* your config */ });
const hook = relayerMastraHook(sdk);

await agent.generate("Generate a financial summary", {
  onStepFinish: hook.onStepFinish,
});
Returns { onStepFinish: (step) => void }. Reads step.usage (Mastra’s standardized usage object) and emits llm_call events with provider and model parsed from step.modelId (format: "provider/model", e.g. "anthropic/claude-opus-4-5"). Tracking errors are swallowed — broken telemetry never breaks the agent.

Errors

All SDK errors extend RelayerError. Use instanceof for dispatch.
import {
  RelayerError,         // base class
  RelayerApiError,      // any non-2xx response with statusCode + endpoint + body
  BudgetExhaustedError, // failedLayers: ("infra" | "tokens" | "payments")[]
  KillSwitchActiveError,// payment operations blocked because kill switch is active
  ApiUnreachableError,  // payment operations blocked because API is unreachable (fail-safe)
  X402PaymentError,     // x402 payment failed after retries
  ApprovalTimeoutError, // approval not resolved within `approvalTimeout`
  ApprovalRejectedError,// approval was actively rejected by a human approver
} from "@relayerfi/agent-sdk";

Dispatch example

try {
  await x402fetch(sdk, url, init);
} catch (err) {
  if (err instanceof BudgetExhaustedError) {
    console.error(`Budget out on: ${err.failedLayers.join(", ")}`);
    return;
  }
  if (err instanceof KillSwitchActiveError) {
    console.warn("Agent is killed — stopping");
    process.exit(0);
  }
  if (err instanceof ApprovalRejectedError) {
    console.error(`Approval ${err.approvalId} was rejected`);
    return;
  }
  if (err instanceof ApprovalTimeoutError) {
    console.warn(`Approval ${err.approvalId} timed out — try smaller amount`);
    return;
  }
  if (err instanceof RelayerApiError) {
    console.error(`API ${err.statusCode} on ${err.endpoint}:`, err.body);
    return;
  }
  throw err;
}

Fail-safe vs fail-open

  • Payment operations (anything that moves USDC) — fail-safe: if anything is wrong (budget out, kill switch, API unreachable), the operation is blocked.
  • Infrastructure operations (telemetry, status polls, non-payment API calls) — fail-open: if the API is unreachable, the agent continues. Telemetry buffers; reconnects retry automatically.
This guarantee is the spine of the Agent Kit: a misbehaving LLM in your agent code cannot bypass payment budgets, even if it has full control of the runtime.

Events

The batcher posts events to POST /v1/agents/events/batch. Server fills in timestamp and agentId server-side; the SDK maps to a minimal shape before send.
TypeWhen emittedAuto-emitted byManual?
paymentAfter a successful x402 paymentx402fetchYes
llm_callAfter an LLM call returnswrapAnthropic / wrapOpenAI / wrapGoogle / relayerMastraHookYes
token_usageAlias usable for explicit token accountingCallerYes
api_callAny non-LLM external HTTP call worth meteringCallerYes
budget_checkDiagnostic for budget queriesCallerYes
kill_switchDiagnostic for kill switch state changesCallerYes
errorCaller-chosen error checkpointsCallerYes
Manual emit:
sdk.emitEvent({
  type: "api_call",
  timestamp: new Date().toISOString(),
  agentId: sdk.agentId,
  data: { endpoint: "https://example.com/lookup", cost_usd: "0.0008" },
});

Configuration cheat sheet

DefaultKnobWhen to change
retries: 3HTTP retry attemptsLower for hot loops (avoid amplification on outages); higher for batch jobs
retryBaseMs: 1_000Base backoffRaise to spread retries across longer windows
timeoutMs: 30_000Per-request timeoutLower for snappy UX; raise for large payloads
killSwitchPollInterval: 30_000Status poll intervalLower for faster kill reaction (more API noise); raise for quieter agents
eventFlushInterval: 10_000Telemetry flush cadenceRaise if your telemetry is non-critical; lower if you want faster dashboards
eventBatchSize: 50Max events per flushRaise for very chatty agents to reduce flush count
approvalPollInterval: 5_000Approval queue pollRaise if approvals are rare; never lower below 5s (API rate limit)
approvalTimeout: 300_000Approval wait timeoutMatch the human approver’s response SLA (CFO availability)
autoShutdown: trueRegister SIGTERM handlersDisable if you manage shutdown manually

Production checklist

1

Secret in a secret manager

RELAYER_AGENT_SECRET lives in your platform’s secret store. Not in .env committed to git. Not in environment variables logged by your runtime.
2

Rotate on schedule

Run POST /v1/agents/{id}/rotate quarterly (or after any suspected leak). The old secret is revoked instantly; push the new one to your secret store.
3

Configure a logger

Pass a logger to RelayerSDK so kill-switch state changes, retries, and event-batch failures are surfaced. The SDK is silent by default.
4

Wire wallet balance to alerts

Poll GET /v1/agents/{id}/wallet-balance and alert when it drops below a refill threshold. Top up via POST /v1/agents/{id}/fund/prepare + /fund/confirm.
5

Test the kill switch

From the dashboard, kill the agent once in staging. Verify your runtime gracefully stops payment operations within ~30s and that infra operations continue.
6

Set a sensible approval threshold

approvalThresholdUSDC should be high enough to not interrupt routine work but low enough that one bad LLM decision is caught. 1010–50 is a reasonable range for many use cases.

See also

Getting Started

The full path from signup to running agent.

Flow Guide

Agent lifecycle, funding, budget, and approvals — focused on the API endpoints.

API Reference

Every endpoint with interactive playground and cURL / Node.js / Python samples.

Authentication

The three auth modes, including the HMAC payload spec the SDK implements.