Intermediate · LESSON 11 OF 12
11. Share safe Solana transactions with Actions and Blinks
By the end, you can design and audit a solana action api and blink transaction flow without confusing public input, previews, wallet approval, and finalized execution.
Look up Solana terms in the glossary
Optional reading history is saved only in this browser. It is not a scored checkpoint or exam result.
In this lesson9 field notes · practice · sources · checkpoint
Read a text version of this diagram
A compatible client performs OPTIONS and GET against a public HTTPS Action endpoint, then displays the action choices. After the person chooses, the client POSTs caller input such as a public key to the endpoint. The server validates all input against canonical application state and returns a transaction proposal. The client decodes and inspects the exact transaction, including required signers, programs, resolved accounts, fee payer, amounts, and freshness. The learner reviews and approves in a wallet. Only then is the transaction submitted and its exact signature reconciled with finalized cluster state before one idempotent fulfillment. Dashed arrows represent untrusted data or proposals; they do not prove identity, approval, submission, or success.
- REQUEST + OPTIONS / GET
- VALIDATE INPUT + BUILD
- REVIEW SIGNERS + COST
- REJECT OR APPROVE IN WALLET
Separate the Action API from the Blink client
A Solana Action is a publicly reachable API that describes an available wallet action and, after an explicit request, returns a signable transaction or message. A Blink is a client that discovers that API and builds an interface around it. A URL is only a way to locate a request; it does not contain a trusted transaction or grant permission. The wallet still mediates the final signature. Keep this distinction clear in diagrams, logs, and user copy: endpoint metadata is a proposal, a Blink is a presentation client, and the signed transaction is the user-authorized instruction set. A supporting wallet or social client can still choose which Actions it displays.
Trace the GET to POST lifecycle
A client first sends GET to learn the title, icon, description, labels, available links, and whether an action is currently disabled. After the person chooses an action, the client sends POST with the account public key and any declared input. The POST response carries a base64 serialized transaction, an optional plain-language message, and optionally a next-action link. The wallet reviews and signs; the client then submits or otherwise executes the approved action and tracks its result. A successful GET proves only that a server responded. A POST account field is caller-supplied input, not a proof that the key signed, owns anything, or consented to a business operation. Treat each response and input as untrusted until independently checked.
Implement the protocol surface deliberately
Support the specification’s OPTIONS preflight and JSON GET and POST shapes. Return preflight methods that satisfy the documented Actions requirements, including GET, POST, PUT, and OPTIONS, without confusing an allowed method with a business endpoint you do not implement. Publish an actions.json file at the website root if ordinary website URLs should map to API endpoints, and test the exact path rules rather than assuming a wildcard has the desired scope. The Actions reference specifies Cross-Origin headers for endpoint calls and actions.json; CORS permits browser requests but does not authenticate clients, prevent non-browser callers, or protect a transaction. Since these endpoints are public, avoid cookie-based assumptions and do not place secrets or private account data in response metadata. Return a useful protocol-shaped error when the action is unavailable; expose disabled state when a client can still present the reason safely.
Build against validated intent, not arbitrary input
Parse the supplied account as a canonical public key, bound every number and string, reject malformed or unexpected parameters, and derive the intended program accounts from trusted application state. Never trust a label, UI field, referer header, or public-key string to prove identity or authorization. If the action depends on a quote, order, proposal, or limited resource, fetch the canonical record server-side, check its owner and state, and bind its identifier, amount, cluster, and expiry into the transaction. Put an expiry or freshness rule on reusable requests, and define idempotency so repeated POSTs cannot fulfill the same order twice.
Review partially signed transactions correctly
An Action response can be unsigned or partially signed. The current specification tells clients not to rely on a fee payer or recent blockhash supplied in an unsigned transaction: set them from the requesting account and a fresh blockhash before signing. If the transaction is partially signed, changing either field invalidates prior signatures. Instead verify every existing signature and reject malformed signatures. The client should sign only with the requested account, only when its signature is required; reject a transaction that requires an unexpected signer. On both paths, decode and inspect programs, account keys, writable accounts, transfer amounts, token mint and program, address-table loaded keys when applicable, and the actual designated fee payer. Simulate when the exact transaction supports it, then show network fee and separate account funding before opening the wallet. A signature is approval of those bytes, not proof that they succeeded.
Keep the user in control through confirmation
Show the action domain and a plain-language description, then let the wallet present the transaction details. Explain which wallet pays the network fee; if the fee payer is the learner, the learner pays it, even when the Action itself is free to use. Account creation, rent-exempt storage, protocol fees, and transferred value can debit different accounts and must be described separately. A sponsor is possible only when the transaction and all required signatures are designed for sponsorship; do not imply that an Action automatically makes a transaction sponsored. After submission, reconcile the exact signature against the intended cluster, finality, error, and resulting account state. A timeout is unknown until checked; never fulfill twice or claim success from the POST response alone.
Treat links and previews as a security boundary
Action links can be opened in many contexts, so provide an ordinary website fallback for clients that do not support Blinks and never imply every wallet or social platform will unfurl the link. Encode Action URLs according to the scheme, especially when they contain query parameters. Keep sensitive values out of shareable URLs and previews because links can be copied, logged, indexed, or shown to bystanders. Make the destination domain visible, protect APIs from request floods, set bounded response sizes and timeouts, avoid reflecting arbitrary input into markup, and separate public response data from authenticated account records. Registration in a third-party registry is not universal trust or a replacement for local transaction review.
Test the API and the bytes independently
Use specification types and the official Actions references for the exact request and response contract, then validate the resulting serialized transaction with Solana libraries. Test OPTIONS from an unrelated origin, GET metadata and unavailable states, each accepted POST input, malformed keys, huge values, replayed orders, stale quotes, unsupported clusters, and structured 4xx/5xx responses. For transaction construction, decode both legacy and supported versioned forms; verify signer requirements and signatures; reject unexpected instructions, programs, destinations, authorities, or fee payers; and test address lookup table resolution when used. A successful local simulation is not a wallet signature, submission, finalized state, production-provider guarantee, or security audit. Use the official Blink Inspector as one protocol debugging aid, not as a security certification.
Ship with explicit operational evidence
Pin the Actions SDK and transaction libraries together with their lockfile, keep RPC credentials and signing keys server-side, and deploy HTTPS endpoints with bounded rate limits and monitored error classes. Record safe action IDs, cluster, transaction signature, and final outcome; do not log wallet messages, email, or private business data without a clear need and retention rule. Rehearse degraded RPC, expired blockhashes, duplicate callbacks, RPC timeouts after submission, and rollback before production. Publish an Actions mapping only after CORS, fallback behavior, endpoint ownership, rate limits, response schemas, and wallet previews have been checked on the deployed domain.
A detail worth keeping.
Specification conformance and application security are separate review tracks. A response can be perfectly shaped JSON and still propose a dangerous destination, an unexpected fee payer, or a replayable business operation. Test schema, origin, and CORS behavior separately from transaction semantics and server-side fulfillment logic.
Work through an example
The Actions specification distinguishes unsigned from partially signed transactions because an existing signature commits to the serialized message. For an unsigned response, the client fills the requested signer account and a fresh blockhash according to the specification. For a partially signed response, verify existing signatures and preserve the message. Do not “fix” bad message fields after a signer has approved them; reject and request a new transaction.
{
"title": "Course token vote",
"icon": "https://example.invalid/icon.png",
"description": "Review a proposed vote before signing.",
"label": "Choose vote",
"links": {
"actions": [
{
"label": "Approve",
"href": "/api/actions/vote?choice=yes",
"type": "post",
"parameters": []
},
{
"label": "Reject",
"href": "/api/actions/vote?choice=no",
"type": "post",
"parameters": []
}
]
}
}
Given a fictional token-voting Action, draw its OPTIONS → GET → POST → wallet review → submit → finalized verification flow. Annotate the domain, caller-controlled inputs, canonical server state, programs, resolved accounts, required signers, fee payer, amount, quote expiry, idempotency key, and retry decision. Mark every fact that still needs proof after an HTTP 200 response.
GUIDED PRACTICE · INTERMEDIATE
Do the work, one step at a time.
Use fictional addresses and disposable test data. Do not enter a recovery phrase or private key. Most exercises work on paper or with a local test; only a lesson that clearly says “read-only Devnet” makes a public network request.
- Read the sample GET metadata and identify the visible domain, available action labels, and any missing fallback or disabled state. Treat the server response as untrusted input.
- Inspect the declared POST fields. List which values come from the caller and specify the server-side order or proposal record that must be re-read before building a transaction.
- For a partially signed response, decode the transaction and list the existing signatures, required signers, fee payer, recent blockhash, programs, and all resolved writable accounts. Reject any signer beyond the requested wallet.
- Compare an unsigned response with the same signed sample. State which fields the client must populate for the unsigned case and which fields it must preserve for the partially signed case.
- Write a pre-wallet review card that identifies the network, destination program, token mint, raw amount, network fee payer, separate storage funding, quote expiry, and user-facing action.
- Simulate the first POST timing out after submission. Define how the server reconciles the exact signature, prevents duplicate fulfillment, reports pending/unavailable states, and authorizes a fresh transaction only when appropriate.
Before you move on, check your reasoning
- R
- i
- s
- k
- r
- e
- v
- i
- e
- w
- i
- s
- e
- a
- s
- i
- e
- r
- w
- h
- e
- n
- e
- a
- c
- h
- l
- a
- y
- e
- r
- h
- a
- s
- i
- t
- s
- o
- w
- n
- e
- v
- i
- d
- e
- n
- c
- e
- r
- e
- c
- o
- r
- d
- :
- t
- h
- e
- d
- o
- m
- a
- i
- n
- a
- n
- d
- G
- E
- T
- r
- e
- s
- p
- o
- n
- s
- e
- ,
- v
- a
- l
- i
- d
- a
- t
- e
- d
- s
- e
- r
- v
- e
- r
- -
- s
- i
- d
- e
- b
- u
- s
- i
- n
- e
- s
- s
- s
- t
- a
- t
- e
- ,
- t
- h
- e
- s
- e
- r
- i
- a
- l
- i
- z
- e
- d
- t
- r
- a
- n
- s
- a
- c
- t
- i
- o
- n
- a
- n
- d
- r
- e
- s
- o
- l
- v
- e
- d
- k
- e
- y
- s
- ,
- t
- h
- e
- w
- a
- l
- l
- e
- t
- d
- e
- c
- i
- s
- i
- o
- n
- ,
- t
- h
- e
- c
- l
- u
- s
- t
- e
- r
- -
- s
- c
- o
- p
- e
- d
- s
- i
- g
- n
- a
- t
- u
- r
- e
- o
- b
- s
- e
- r
- v
- a
- t
- i
- o
- n
- ,
- a
- n
- d
- t
- h
- e
- i
- d
- e
- m
- p
- o
- t
- e
- n
- t
- f
- u
- l
- f
- i
- l
- l
- m
- e
- n
- t
- r
- e
- s
- u
- l
- t
- .
- I
- f
- a
- n
- y
- l
- a
- y
- e
- r
- i
- s
- u
- n
- a
- v
- a
- i
- l
- a
- b
- l
- e
- ,
- m
- a
- r
- k
- t
- h
- a
- t
- s
- p
- e
- c
- i
- f
- i
- c
- p
- r
- o
- o
- f
- a
- s
- u
- n
- k
- n
- o
- w
- n
- i
- n
- s
- t
- e
- a
- d
- o
- f
- b
- o
- r
- r
- o
- w
- i
- n
- g
- c
- o
- n
- f
- i
- d
- e
- n
- c
- e
- f
- r
- o
- m
- a
- n
- e
- i
- g
- h
- b
- o
- r
- i
- n
- g
- l
- a
- y
- e
- r
- .
OPEN SELF-CHECK · UNGRADED
Pause and test your reasoning
Practice 1 of 3
A Blink client displays a transaction proposal returned by a public Action API. What does that display prove?
Reveal the answer and explanation
One best answer: Only that the client rendered a proposal; the wallet must review and authorize the exact transaction.
Presentation is not authentication, authorization, or chain confirmation. Confirm the matching API and transaction behavior against the official Actions protocol reference before building a client.
Sources: Solana Actions and Blinks
Practice 2 of 3
What does the wallet address in an Actions POST body establish by itself?
Reveal the answer and explanation
One best answer: The caller supplied a public key; it is not proof of key control or consent.
Caller-supplied data needs independent validation and cannot establish consent. Confirm the matching API and transaction behavior against the official Actions protocol reference before building a client.
Sources: Solana Actions and Blinks
Practice 3 of 3
The first transaction POST timed out after submission. What is the safe next step?
Reveal the answer and explanation
One best answer: Reconcile the exact signature and resulting state on the intended cluster before retrying.
A timeout creates uncertainty; exact signature and state reconciliation determine recovery. Confirm the matching API and transaction behavior against the official Actions protocol reference before building a client.
Sources: Solana Actions and Blinks
This practice is not scored, saved, or part of a certificate. Account-based lesson checkpoints and level exams remain separate.
Never enter a recovery phrase or private key into a learning site or lesson exercise.
READ THE SOURCE
Go deeper in the official docs
Reviewed on 2026-10-06. Protocol documentation can change; check the linked page’s current version as you use this material.
YOUR PROGRESS, SAVED
Ready for the checkpoint?
Make a free account to save your progress and unlock the quizzes.
Create free account