@relayerfi/agent-sdk
v1.0.0
Published
Relayer SDK for AI agents — budget enforcement, x402 payments, Mastra hook
Downloads
16
Maintainers
Readme
@relayerfi/agent-sdk
Budget-enforced SDK for AI agents -- cost visibility, 3-layer budget control, and programmable approval.
Install
npm install @relayerfi/agent-sdk
# or
pnpm add @relayerfi/agent-sdkPeer dependency (optional): @mastra/core ^1.24.0 -- only needed if you use the Mastra tool wrappers from @relayerfi/agent-sdk/mastra.
Quick Start
import { RelayerSDK } from "@relayerfi/agent-sdk";
const sdk = new RelayerSDK({
agentId: "your-agent-id",
secret: "your-secret",
apiUrl: "https://api.relayer.fi",
});
// Check budget before an expensive operation
await sdk.checkBudget();
// Clean up when done
await sdk.shutdown();Core Concepts
3-Layer Budget Model
Relayer enforces budgets across three independent layers:
| Layer | What it controls | Method |
|-------|-----------------|--------|
| Infra (Layer 1) | Infrastructure spend (compute, storage) | checkBudget() |
| Tokens (Layer 2) | LLM token consumption | checkBudget() |
| Payments (Layer 3) | On-chain payments via x402 | checkPaymentBudget() |
checkBudget()checks Layers 1+2. Non-payment operations fail open when only the payments layer is exhausted.checkPaymentBudget()checks all 3 layers AND the kill switch. Use this before any payment-capable operation.
Kill Switch
The CFO can activate a kill switch at any time. When active, all payment operations are blocked immediately with KillSwitchActiveError. The SDK polls for kill switch state automatically.
if (sdk.isKillSwitchActive) {
console.log("Payments are blocked by kill switch");
}Event Batching
The SDK batches telemetry events and flushes them to relayer-api periodically. Events are auto-flushed on shutdown.
sdk.emitEvent({
type: "api_call",
timestamp: new Date().toISOString(),
agentId: "your-agent-id",
data: { endpoint: "/api/data", latencyMs: 230 },
});API Reference
RelayerSDK
The main SDK class. Manages HTTP transport, budget checks, kill switch polling, event batching, x402 payments, and approval flows.
const sdk = new RelayerSDK(config: RelayerSDKConfig);sdk.checkBudget(): Promise<void>
Checks Layers 1+2 (infra + tokens). Throws BudgetExhaustedError if either layer is exhausted. Does NOT check the payments layer or kill switch.
sdk.checkPaymentBudget(): Promise<void>
Checks all 3 budget layers. Also checks the kill switch -- throws KillSwitchActiveError if active. Use before any operation that may trigger a payment.
sdk.emitEvent(event: RelayerEvent): void
Queues a telemetry event for batched delivery to relayer-api. Events are flushed automatically based on eventFlushInterval and eventBatchSize.
sdk.isKillSwitchActive: boolean
Returns true if the kill switch is currently active. Getter -- no network call, reads cached state from background polling.
sdk.shutdown(): Promise<void>
Stops kill switch polling, flushes pending events, and cleans up timers. Always call this before your process exits.
sdk.http: HttpClient
Direct access to the underlying HTTP client for custom API calls:
const data = await sdk.http.get<MyType>("/v1/custom/endpoint");
const result = await sdk.http.post<MyType>("/v1/custom/endpoint", { body: "data" });x402fetch
Universal fetch wrapper that integrates budget checks, x402 payment handling, and approval flows transparently.
import { RelayerSDK, x402fetch } from "@relayerfi/agent-sdk";
const sdk = new RelayerSDK({ /* config */ });
// Use like regular fetch -- x402 payments are handled automatically
const response = await x402fetch(sdk, "https://api.example.com/paid-endpoint", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query: "data" }),
});Flow:
- Checks payment budget (all 3 layers + kill switch)
- Makes the original HTTP request
- If server returns
402with x402 payment requirement, delegates payment to relayer-api and retries - If server returns
202with approval ID, polls until approved then retries - All other responses pass through unchanged
Error Classes
import {
RelayerError,
RelayerApiError,
BudgetExhaustedError,
KillSwitchActiveError,
ApiUnreachableError,
X402PaymentError,
ApprovalTimeoutError,
ApprovalRejectedError,
} from "@relayerfi/agent-sdk";x402 Payments
The x402 protocol enables pay-per-request APIs. When a server returns HTTP 402 with a payment requirement, the SDK handles payment automatically via relayer-api.
import { RelayerSDK, x402fetch } from "@relayerfi/agent-sdk";
const sdk = new RelayerSDK({
agentId: "my-agent",
secret: "my-secret",
apiUrl: "https://api.relayer.fi",
});
// This API charges per request via x402
const response = await x402fetch(sdk, "https://paid-api.example.com/data");
const data = await response.json();If the request returns 402, the SDK:
- Extracts the
X402PaymentRequirement(amount, currency, network, recipient) - Sends the requirement to relayer-api for payment execution
- Retries the original request with the payment header
- Emits a
paymentevent for cost tracking
If checkPaymentBudget() fails, the request never happens -- budget enforcement cannot be bypassed.
LLM Token Tracking
Wrap your LLM client to automatically track token usage without changing your existing code.
Anthropic
import Anthropic from "@anthropic-ai/sdk";
import { RelayerSDK, wrapAnthropic } from "@relayerfi/agent-sdk";
const sdk = new RelayerSDK({ /* config */ });
const anthropic = wrapAnthropic(new Anthropic(), sdk);
// Use normally -- token usage is tracked automatically
const message = await anthropic.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello" }],
});OpenAI
import OpenAI from "openai";
import { RelayerSDK, wrapOpenAI } from "@relayerfi/agent-sdk";
const sdk = new RelayerSDK({ /* config */ });
const openai = wrapOpenAI(new OpenAI(), sdk);
const completion = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello" }],
});import { GoogleGenerativeAI } from "@google/generative-ai";
import { RelayerSDK, wrapGoogle } from "@relayerfi/agent-sdk";
const sdk = new RelayerSDK({ /* config */ });
const google = wrapGoogle(new GoogleGenerativeAI("api-key"), sdk);
const model = google.getGenerativeModel({ model: "gemini-pro" });
const result = await model.generateContent("Hello");All wrappers use ES Proxy -- zero runtime overhead when no LLM call is made. The original client type is preserved, so TypeScript types and IDE completions work as before.
Mastra Integration
Import from the /mastra entry point to get pre-built Mastra tools and the token tracking hook.
import { relayerMastraHook } from "@relayerfi/agent-sdk/mastra";
import { RelayerSDK } from "@relayerfi/agent-sdk";
const sdk = new RelayerSDK({ /* config */ });
const hook = relayerMastraHook(sdk);
// Pass to agent.generate() for automatic token tracking
const result = await agent.generate("Analyze spending for Q1", {
onStepFinish: hook.onStepFinish,
});The hook captures promptTokens and completionTokens from each Mastra step and emits them as token_usage events to relayer-api.
Configuration
All RelayerSDKConfig options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| agentId | string | required | Agent identifier registered with relayer-api |
| secret | string | required | Agent secret for X-Agent-Auth header |
| apiUrl | string | required | Base URL for relayer-api |
| retries | number | 3 | Max retry attempts for failed HTTP requests |
| retryBaseMs | number | 1000 | Base delay (ms) for exponential backoff |
| timeoutMs | number | 30000 | HTTP request timeout (ms) |
| killSwitchPollInterval | number | 30000 | How often (ms) to poll kill switch status |
| eventFlushInterval | number | 10000 | How often (ms) to flush batched events |
| eventBatchSize | number | 50 | Max events per batch before auto-flush |
| approvalPollInterval | number | 5000 | How often (ms) to poll approval status |
| approvalTimeout | number | 300000 | Max time (ms) to wait for approval (5 min) |
| logger | Logger | undefined | Custom logger implementing info, warn, error |
| autoShutdown | boolean | true | Register SIGTERM/beforeExit handlers automatically |
Error Handling
| Error | When thrown | How to handle |
|-------|-----------|---------------|
| BudgetExhaustedError | checkBudget() or checkPaymentBudget() when a layer is exhausted | Stop the operation. The .failedLayers property lists which layers failed. |
| KillSwitchActiveError | checkPaymentBudget() when kill switch is on | Block all payment operations. Retry after CFO deactivates the switch. |
| ApiUnreachableError | HTTP client exhausts all retries against relayer-api | Payment operations are blocked (fail-safe). Layers 1+2 fail open. |
| RelayerApiError | relayer-api returns a non-2xx response | Check .statusCode and .endpoint for details. |
| X402PaymentError | x402 payment flow fails after retries | The payment requirement is in .paymentRequirement. |
| ApprovalTimeoutError | Approval not resolved within approvalTimeout | The approval ID is in .approvalId. Ask the CFO to approve. |
| ApprovalRejectedError | CFO explicitly rejects the approval | The operation was denied. Do not retry. |
All errors extend RelayerError, which extends Error. You can catch RelayerError to handle all SDK errors:
import { RelayerError, BudgetExhaustedError } from "@relayerfi/agent-sdk";
try {
await sdk.checkPaymentBudget();
} catch (error) {
if (error instanceof BudgetExhaustedError) {
console.log("Exhausted layers:", error.failedLayers);
} else if (error instanceof RelayerError) {
console.log("SDK error:", error.message);
}
}Example
See examples/bi-agent/ for a complete reference implementation — a financial intelligence agent with daily reports, hourly alerts, and full SDK integration including HMAC auth, token tracking, and cron scheduling.
License
MIT
