Blinks and Solana Actions
A Solana Action is an HTTP API. GET returns metadata (title, icon, buttons), and POST receives the user's account and returns a serialized transaction for the wallet to sign. A Blink is a link that clients unfurl into that interactive card. Servers must send the CORS headers the spec requires and should serve an actions.json at the domain root.
Reference: Solana docs: Actions and Blinks · Dialect Blinks docs
Real code, line by line
4 excerpts from different projects. Each caption links the exact lines at the fork's pinned commit. The notes below each excerpt explain the lines that matter.
app/api/actions/tokens/[uniqueid]/route.ts L1–26 @ 08bfbac · MIT · originalimport { ObjectId } from 'mongodb';import { NextRequest, NextResponse } from 'next/server';import clientPromise from '@/lib/mongodb';import { ActionGetResponse, ACTIONS_CORS_HEADERS, ActionPostRequest, createPostResponse, ActionPostResponse,} from "@solana/actions";import { AddressLookupTableAccount, clusterApiUrl, Connection, LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction,} from "@solana/web3.js";import { TOKEN_PROGRAM_ID } from "@solana/spl-token";import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction } from "@/lib/constant"; const PUMP_PROGRAM_ID = new PublicKey("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P");- Line 5
ActionGetResponse,: The metadata a Blink client renders (title, icon, buttons). - Line 8
createPostResponse,: Returns the serialized transaction the wallet will sign.
candyblinks/app/blinks/mint/[handle]/route.ts L1–25 @ 88bf53d · Apache-2.0 · originalimport { ACTIONS_CORS_HEADERS, ActionGetResponse, ActionPostRequest, ActionPostResponse, createPostResponse,} from "@solana/actions";import { mintTransaction, getBlink } from "./utils";import client from "@/app/lib/blinksightsClient"; export const GET = async ( req: Request, { params }: { params: { handle: string } }) => { const handle = params.handle; try { console.log("fetching blink"); console.log(getBlink); const blinksData = await getBlink(handle); if (blinksData.length === 0) { console.error("No blinks found for the given handle"); throw new Error("No blinks found for the given handle"); }- Line 3
ActionGetResponse,: The metadata a Blink client renders (title, icon, buttons). - Line 5
ActionPostResponse,: Returns the serialized transaction the wallet will sign.
src/lambda/services/ActionService.ts L11–36 @ 54f4b3c · Apache-2.0 · original signTransaction,} from '@metaplex-foundation/umi'import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'import { toWeb3JsTransaction } from '@metaplex-foundation/umi-web3js-adapters'import { ActionPostResponse, createPostResponse } from '@solana/actions'import { ActionGetResponse } from '@solana/actions-spec'import { InternalServerError } from 'http-errors'import { FileService } from './FileService'import { KeyService } from './KeyService'import { getMandatoryEnv } from '../../utils/getMandatoryEnv'import { CreateAssetRequest } from '../types/CreateAssetRequest' export class ActionService { private readonly logger = new Logger({ serviceName: 'ActionService' }) private readonly keyService: KeyService private readonly fileService: FileService private readonly solanaRpcNode = getMandatoryEnv('SOLANA_RPC_NODE') constructor(keyService: KeyService, fileService: FileService) { this.keyService = keyService this.fileService = fileService } getOneOfActionDefinition = (): ActionGetResponse => ({ description: 'Create a NFT', title: 'NFT creation',- Line 15
import { ActionPostResponse, createPostResponse } from '@solana/actions': Returns the serialized transaction the wallet will sign. - Line 16
import { ActionGetResponse } from '@solana/actions-spec': The metadata a Blink client renders (title, icon, buttons).
packages/backend/src/routers/blinks.ts L1–26 @ 4199f5b · MIT · originalimport { createPostResponse } from '@solana/actions';import { Action } from '@solana/actions-spec';import express, { Request, Response } from 'express';import { findAccessTokenByCode, findGuildById, getGrandfatheredPriceForUser, getUserGrandfatheredPricesForGuild, saveRolePurchase,} from '../database/database';import { RolePurchase } from '../database/entities/role-purchase';import { decryptText } from '../services/encrypt';import env from '../services/env';import { discordApi, sendDiscordLogMessage } from '../services/oauth';import { generateSendTransaction, isTxConfirmed } from '../services/transaction'; export const blinksRouter = express.Router(); const BASE_URL = env.APP_BASE_URL; const generalAction = { title: 'Use Blinkord', label: 'Go to blinkord.com', type: 'action', links: { actions: [- Line 1
import { createPostResponse } from '@solana/actions';: Returns the serialized transaction the wallet will sign.
Go further
- getblink.fun: explore the full project
- Candy-Blinks: explore the full project
- mintr-backend: explore the full project
- blinkord: explore the full project
- Next lesson: AI agents on Solana
Related lessons
Topics: Payments · Learning path · Glossary