TypeScript SDK
The relayfor.si SDK for your server and your users' wallets. Launches, tokens, credit, keys, webhooks and the RFS Router, typed end to end.
relayfor.si is the official SDK for the management API and the RFS Router. It runs on Node.js 22+, Bun, Deno, Cloudflare Workers and Vercel Functions, with one small dependency.
npm i relayfor.siSet up
Make a secret key on the dashboard's Keys page and keep it on your server:
export RELAYFOR_SECRET_KEY="rf_sk_..."import { RelayForSI } from "relayfor.si";
// Reads RELAYFOR_SECRET_KEY.
const relayForSI = new RelayForSI();The client refuses to run in a browser, where the key would reach every visitor. Its parts for the browser live in relayfor.si/solana.
Launch a token
Your server prepares the launch, the creator's wallet signs it in the browser, and your server sends it:
const launch = await relayForSI.launches.prepare(
{
creator: wallet, // signs last and pays
name: "Acme",
symbol: "ACME",
image: await relayForSI.files.dataUri(file),
transaction_version: version, // from the browser, below
},
{ idempotencyKey: `launch:${draftId}` },
);
// Later, with the signed transaction from the browser:
await relayForSI.launches.submit(launch.id, { transaction: signed });
// "confirmed", "failed" or "expired".
const { state } = await relayForSI.launches.wait(launch.id);files.dataUri() checks the image (PNG, JPEG, GIF or WebP, up to 2 MB) before it leaves your server. signWithWallet refuses a transaction the wallet changed while signing, which the API would reject. Bring a token works the same with relayForSI.tokens.import({ mint, creator }).
Tokens, credit and keys
// Every token, page by page.
for await (const token of relayForSI.tokens.all()) {
console.log(token.mint, token.preset?.name);
}
// The token's credit account, and a router key for its agent.
const account = await relayForSI.accounts.get(mint);
const key = await relayForSI.keys.create({
account: account.id,
name: "agent-1",
limit: { usd: "5", reset: "daily" },
});key.key (rf_ai_...) is shown once, as on the dashboard. The resources follow the API reference: launches, tokens, presets, accounts (with ledger and purchases), keys, usage, statements and webhooks, with the API's own field names.
Webhooks
import { verifyWebhook } from "relayfor.si/webhooks";
export async function POST(request: Request): Promise<Response> {
const event = await verifyWebhook({
body: await request.text(), // the raw body
header: request.headers.get("relayfor-signature"),
secret: process.env.RELAYFOR_WEBHOOK_SECRET!, // whsec_...
});
if (event.type === "launch.confirmed") {
// event.data.launch
}
return new Response(null, { status: 204 });
}During a rotation, pass both secrets: secret: [current, previous]. See Webhooks.
The RFS Router
// Reads RELAYFOR_ROUTER_KEY (rf_ai_...).
const { spendable_usd } = await relayForSI.ai.balance();
const { data, usage } = await relayForSI.ai.images.generate({
model: "openai/gpt-image-2",
prompt: "A red lighthouse at dusk, flat illustration",
});ai.models() and ai.imageModels() list every model with its price, and ai.images.stream() yields partial images as they form. For chat, use the OpenAI or Anthropic SDK with relayForSI.ai.baseURL, or the Vercel AI SDK provider.
Errors and retries
import { isAPIError } from "relayfor.si";
try {
await relayForSI.launches.prepare(params);
} catch (error) {
if (isAPIError(error) && error.code === "insufficient_funds") {
// error.status, error.param, error.requestId
} else {
throw error;
}
}error.codeis the API's stable error code; quoteerror.requestIdto support.- Network errors, timeouts,
429and5xxare retried twice, waiting asRetry-Afterasks. - Every
POSTcarries anIdempotency-Key, so a retry never acts twice. Pass your own when your job may run again:{ idempotencyKey: "launch:42" }. - Image generation is charged, so it is retried only after refusals that cost nothing.