What is relayfor.si?

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.si

npm · GitHub · Changelog

Set 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.code is the API's stable error code; quote error.requestId to support.
  • Network errors, timeouts, 429 and 5xx are retried twice, waiting as Retry-After asks.
  • Every POST carries an Idempotency-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.

On this page