Solana Blinks Integration Guide: Step by Step.

Okay, so you wanna get into Solana Blinks? Awesome choice. Blinks are basically magic links that let people do on chain stuff-like donating SOL or swapping tokens-right from Twitter, Discord, or wherever. No dApps, no extensions. Just click, wallet pops up, sign, done. In my experience, it's changed how I share crypto actions with friends. Super shareable.

Why does this matter? Imagine posting a link on X that says "Donate 0.1 SOL to my tip jar" and boom, people can do it without leaving the app. That's Blinks. And today? We're building one step by step. A simple SOL transfer blink. You'll have it running in like 30 minutes if you're quick.

First off, what even are Blinks?

Short version: Solana Actions are APIs you build. Blinks are the clients (like on X or Phantom) that turn those APIs into clickable buttons. You make a URL with an action param pointing to your API. Client fetches metadata (GET), shows a pretty UI, user clicks, signs via POST. Transactions cost like ~0.000005 SOL in fees on mainnet, peanuts.

The thing is, it's all URL based. Post solana action:https://your site.com/donate?amount=0.1 anywhere. Boom. And it works on devnet for testing, no real money lost.

Quick ways Blinks detect your action

MethodExampleWhen to use
Explicitsolana action:https://yoursite.com/actionFor max compatibility, but only Blink clients see it
Known URLsRegister at Dialect, maps normal URLs to actionsBest for social sharing, gets previews
Query paramhttps://yoursite.com?action=https://api.yoursite.com/actionFallback for non Blink spots

Setting up your dev environment. Easy peasy.

Look, I usually start with Next.js 'cause the official guide does it that way. Grab a new project:

  1. npx create next app@latest my blink --typescript --tailwind --eslint --app. Say yes to everything.
  2. cd into it: cd my blink
  3. Install the goodies: npm install @solana/web3.js @solana/actions
  4. Start dev server: npm run dev. Hits localhost:3000.

Now? Create your action file. Make a folder src/app/api/actions/transfer sol/ and inside it, route.ts. That's your API endpoint. All the magic lives here.

Building the GET endpoint - the UI brain

This one's crucial. GET returns JSON that Blinks use to render buttons, labels, icons. Miss it? No UI shows. And don't forget OPTIONS for CORS, or it won't render anywhere.

Copy this into your route.ts. Swap YOURSOLANAWALLETADDRESS with a devnet wallet you control. Use Phantom to make one if needed.

import { ActionGetResponse, ActionPostRequest, ActionPostResponse, ACTIONSCORSHEADERS, BLOCKCHAINIDS,
} from "@solana/actions"; import { Connection, PublicKey, LAMPORTSPERSOL, SystemProgram, TransactionMessage, VersionedTransaction,
} from "@solana/web3.js"; const blockchain = BLOCKCHAIN_IDS.devnet;
const connection = new Connection("https://api.devnet.solana.com");
const donationWallet = "YOURSOLANAWALLETADDRESS"; const headers = { ..ACTIONSCORS_HEADERS, "x blockchain ids": blockchain, "x action version": "2.4",
}; export const OPTIONS = async () => { return new Response(null, { headers });
}; export const GET = async (req: Request) => { const response: ActionGetResponse = { type: "action", icon: ${new URL("/icon.jpg", req.url).toString()}, // Add your own icon label: "Donate SOL", title: "Quick SOL Tip", description: "Send some SOL my way on devnet. Testing Blinks!", links: { actions: [ { type: "transaction", label: "0.01 SOL", href: /api/actions/transfer sol?amount=0.01, }, { type: "transaction", label: "0.05 SOL", href: /api/actions/transfer sol?amount=0.05, }, { type: "transaction", label: "Custom Amount", href: /api/actions/transfer sol?amount={amount}, parameters: [{ name: "amount", label: "SOL Amount", type: "number", }], }, ], }, }; return new Response(JSON.stringify(response), { status: 200, headers });
};

Test it. Hit http://localhost:3000/api/actions/transfer sol in browser. You should see JSON. Pretty much instant feedback.

Now the POST - where transactions happen

This fires when user clicks. Grabs amount from URL, payer from request body, builds tx, serializes to base64. Boom.

export const POST = async (req: Request) => { try { const url = new URL(req.url); const amount = Number(url.searchParams.get("amount")); const request: ActionPostRequest = await req.json(); const payer = new PublicKey(request.account); const receiver = new PublicKey(donationWallet); const transaction = await prepareTransaction(connection, payer, receiver, amount); const response: ActionPostResponse = { type: "transaction", transaction: Buffer.from(transaction.serialize()).toString("base64"), }; return Response.json(response, { status: 200, headers }); } catch (error) { console.error("Error:", error); return new Response(JSON.stringify({ error: "Oops, try again" }), { status: 500, headers }); }
}; const prepareTransaction = async (connection: Connection, payer: PublicKey, receiver: PublicKey, amount: number) => { const instruction = SystemProgram.transfer({ fromPubkey: payer, toPubkey: receiver, lamports: amount * LAMPORTSPERSOL, }); const { blockhash } = await connection.getLatestBlockhash(); const message = new TransactionMessage({ payerKey: payer, recentBlockhash: blockhash, instructions: [instruction], }).compileToV0Message(); return new VersionedTransaction(message);
};

Add that below your GET. Full file now? Restart dev server. Test POST with curl or Postman if you're nerdy.

Testing locally. Does it work?

Grab your full URL: http://localhost:3000/api/actions/transfer sol. To make it a Blink link: https://solana action:http://localhost:3000/api/actions/transfer sol. Ngrok it for public testing: ngrok http 3000, use the ngrok URL.

  • Open in Phantom browser extension. Should show buttons.
  • Click 0.01 SOL. Wallet asks to sign. Approve on devnet.
  • Check explorer: solscan.io for devnet txs. Funds move!

Common gotcha? Wrong wallet address. Or forgetting OPTIONS - page just blanks. Happened to me first time. Double check headers.

Deploying it. Don't skip this.

Local's cute, but shareable needs hosting. Vercel is free and easy for Next.js.

  1. Push to GitHub.
  2. vercel.com, import repo, deploy. Boom, live URL like https://my blink.vercel.app.
  3. Update to mainnet: Change BLOCKCHAIN_IDS.mainnet, connection to "https://api.mainnet beta.solana.com" or your RPC (QuickNode free tier rocks).
  4. Fees? Transfer is ~0.000005 SOL priority fee + rent, tiny.

Alternative? Fleek Functions if you hate Next.js. Super serverless. Install CLI, make index.js with similar logic (check their guide for exact code), deploy. Gets you a .fleek.co URL fast.

Registering your Blink - social media magic

Honestly, this is. Unregistered Blinks don't unfurl on X. Go to dial.to/register or Dialect form. Paste your action URL. They verify, approve, now your links preview with buttons everywhere. Takes a day or two sometimes.

Pro tip: Add an icon (512x512 PNG). Upload to public folder as /icon.png. Makes it pop.

Leveling up: Custom inputs and multi steps

Your basic donate works. But what about swaps? Or forms?

For inputs, like custom amount - you already have it in the example. Blinks show a number field. User types 0.42, hits your POST with ?amount=0.42.

Multi step? Chain links. In GET response, make actions point to other endpoints. Like step1: approve, step2: swap. Each POST returns next action JSON. Wildly powerful.

In my experience, token transfers next. Grab @solana/spl token, make transferChecked instruction. Amount in UI lamports? Nah, show SOL decimals.

Token transfer snippet

Swap SystemProgram for:

import { getAssociatedTokenAddress, createTransferInstruction } from '@solana/spl token';
// In prepareTransaction:
const sourceATA = await getAssociatedTokenAddress(mint, payer);
const destATA = await getAssociatedTokenAddress(mint, receiver);
const instruction = createTransferInstruction(sourceATA, destATA, payer, amount * 1e9); // USDC decimals

Troubleshooting - 'cause stuff breaks

Button gray? Check console for CORS errors. Add OPTIONS.

Tx fails signature? Use VersionedTransaction, not legacy. Devnet blockhash expires fast - getLatestBlockhash('finalized').

No UI on X? Register first. Or use solana action: prefix.

Fees too high? Set computeUnitLimit lower in tx message. But for transfers, default's fine at 0.000005 SOL.

  • Connection drops? Use paid RPC like Helius (0.0001 SOL/req cheap).
  • Custom chains? BLOCKCHAIN_IDS supports Solana only rn.
  • Errors in POST? Log req.body.account - must be valid Pubkey.

Real world ideas I love

Tip jars. NFT mints. Vote with tokens (burn or send to pool). Raffles: pay entry, get ticket ATA.

Posted a blink for my project's airdrop claim. 500 claims in a day via Twitter. No website needed. That's the power.

What's next for you? Tweak the amounts, add validation (min 0.001 SOL?), deploy to mainnet. Share your first blink with me - hit up Discord or whatever. You've got this.