What is relayfor.si?

Launch a token

Your server prepares the launch, your user's own wallet signs it last. relayfor.si never holds its keys or money.

A launch creates a pump.fun token with its fee route locked in the same transaction. The token's creator is the wallet that signs it and pays for it: your user, or you. relayfor.si builds the transaction, checks it by simulation and signs it with the new mint, but never holds the creator's keys or money.

A launch, step by step
  1. 01PrepareYour serverPOST /launches/prepare
  2. 02SignThe creator's walletThe wallet signs last
  3. 03SendYou, or relayfor.siPOST /launches/{id}/submit
  4. 04ConfirmSolanalaunch.confirmed
Statespreparedsentconfirmedfailedexpired
relayfor.si never holds the creator's keys or money: the transaction is built, checked by simulation and signed by the new mint on our side, and the creator's wallet signs it last. A prepared launch that is never sent in time becomes expired: prepare it again.

What a launch costs

The creator's wallet pays, all inside the launch transaction:

PartAboutWhat for
pump.fun's accounts0.03 SOLThe token, its bonding curve and its fee route
relayfor.si's fee0.02 SOLOur launch fee
The token's vault0.0009 SOLOpens the vault the AI's share is paid to
An opening buyOptionalThe creator buys first, up to 100 SOL

Each route wallet that holds nothing yet is opened the same way, for about 0.0009 SOL: pump.fun can't pay an empty wallet less than that.

Step by step

Prepare

Your server calls POST /launches/prepare with the creator's wallet and the token's details. The image is a base64 data URI, up to 2 MB.

const launch = await fetch("https://relayfor.si/api/project/v1/launches/prepare", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RELAYFOR_SECRET_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    creator: userWallet,
    name: "Nova",
    symbol: "NOVA",
    description: "The agent that reads Solana for you.",
    image: imageDataUri,
  }),
}).then((response) => response.json());

The answer is the launch in state prepared, with its mint, its route and the transaction to sign. Ask for "transaction_version": 0 when the creator's wallet can't sign version 1 (see the next step).

Sign in the browser

The creator's wallet signs last. Every Solana wallet in the browser (Phantom, Solflare, Backpack and others) offers the Wallet Standard's solana:signTransaction, which takes the transaction's bytes as they are:

npm i @wallet-standard/app @wallet-standard/features @solana/wallet-standard-features
import { SolanaSignTransaction, type SolanaSignTransactionFeature } from "@solana/wallet-standard-features";
import { getWallets } from "@wallet-standard/app";
import { StandardConnect, type StandardConnectFeature } from "@wallet-standard/features";

// The Solana wallets in this browser that can connect and sign.
const wallets = getWallets()
  .get()
  .filter((wallet) => SolanaSignTransaction in wallet.features && StandardConnect in wallet.features);

// Connect the one your user picks, and read its address.
const wallet = wallets[0]!;
const connect = (wallet.features as StandardConnectFeature)[StandardConnect];
const { accounts } = await connect.connect();
const account = accounts[0]!;
const signing = (wallet.features as SolanaSignTransactionFeature)[SolanaSignTransaction];

// A wallet that can't sign version 1 transactions yet gets a version 0 launch.
const transactionVersion = signing.supportedTransactionVersions.includes(1) ? 1 : 0;

// Your server prepares the launch for this wallet (step 1)...
const launch = await prepareOnYourServer({ creator: account.address, transactionVersion });

// ...the wallet signs it...
const [signed] = await signing.signTransaction({
  account,
  transaction: Uint8Array.from(atob(launch.transaction.base64), (char) => char.charCodeAt(0)),
  chain: "solana:mainnet",
});

// ...and your server sends it (step 3).
await submitOnYourServer(launch.id, btoa(String.fromCharCode(...signed!.signedTransaction)));

prepareOnYourServer and submitOnYourServer are your own server's routes: the secret key never reaches the browser.

Send

Post the signed transaction to POST /launches/{id}/submit and relayfor.si sends it, or send it to Solana yourself. Do it within about a minute of preparing: the transaction expires with its blockhash.

await fetch(`https://relayfor.si/api/project/v1/launches/${launchId}/submit`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RELAYFOR_SECRET_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ transaction: signedBase64 }),
});

Confirm

Poll GET /launches/{id} until state is confirmed, or listen for the launch.confirmed webhook. The token then has a credit account and a proof page.

StateMeans
preparedBuilt and waiting for the creator's wallet
sentSent to Solana, not confirmed yet
confirmedOn chain with its route. The token is live
failedIt did not land: error says why
expiredNever sent in time: prepare it again

The token's route

The route comes from your fee strategy. Three fields shape it:

  • preset: the strategy to use (fps_...). Your default when left out.
  • split: the creator's pick of the AI's and the creator's shares, in basis points of your part, within the strategy's creator_choice. The strategy's own split when left out.
  • creator_fees_to: where the creator's share goes. The creator's own wallet when left out.
{ "split": { "ai_share": 7000, "creator_share": 1000 } }

Here the AI takes 70% of your part and the creator 10%, and Standard's wallet keeps its 20%. A launch is refused before anything is signed when a route wallet can't be paid, such as a program's address or relayfor.si's own. The answer's route lists every shareholder with its roles, in basis points of the whole fee, relayfor.si's 20% among them.

Quote tokens

A token trades against SOL unless you name a quote_mint: USDC, or a token on pump.fun's quote-control list. It then trades, pays its creator fees and pays its route in that token. relayfor.si credits USDC at $1, and sells any other quote token for USDC before crediting it.

Its opening buy is opening_buy_amount, in the quote token's base units: 5000000 is 5 USDC, paid from the creator's own account for it. Every amount in the API is base units, as on chain. A Token-2022 quote with a scaled UI amount (an xStock) or interest reads differently in wallets: turn a typed amount into base units with Token-2022's UiAmountToAmount.

Older wallets

Launches are version 1 transactions, Solana's newer format. A wallet that can't sign version 1 yet says so in supportedTransactionVersions; ask for "transaction_version": 0 for it: a version 0 transaction that loads shared accounts from relayfor.si's lookup table. A version 0 launch carries no opening buy.

Without a server

A project with no developer launches from the dashboard's Launch page. It builds the same launch with your strategy and route preview, and signs with the Solana wallet connected in your browser.

On this page