How it works
Rolink is a hosted service. The only piece that runs on your side is the Luau SDK inside your game servers. The linking pages, the game API, the dashboard, the project wallets and the event publisher all run on Rolink.
Player's browser Roblox game servers /link/<slug> + wallet Luau SDK (server only) | | ^ | Sign in with Roblox | HTTPS | SubscribeAsync | + signed message | x-api-key | v v | +----------------------------------------------+ | | Rolink Cloud | | | linking pages, /v1 game API, dashboard | | | database: projects, links, tx records | | +----------------------------------------------+ | | | | | | JSON-RPC | sign | publish | v v v | Solana RPC Turnkey Open Cloud | (+ DAS API) (project wallets) MessagingService -----+ (your universe)Components
Section titled “Components”| Piece | Runs on | What it does |
|---|---|---|
| Luau SDK | Your Roblox game servers | Wraps the game API, caches wallet lookups, and turns MessagingService messages into signals. It refuses to run on the client. |
| Game API | Rolink, under /v1 | Answers your servers. Each request carries a project API key in x-api-key, and everything it reads or writes belongs to that key’s project. |
| Linking page | Rolink, at /link/<project-slug> | Signs the player in with Roblox through your game’s own OAuth app, finds their wallets, and asks them to sign the link message. One page per project. |
| Dashboard | Rolink, at /dashboard | Where you sign in with a Solana wallet, create projects and keys, save your game’s Roblox OAuth app, set the transaction policy, and enter your Open Cloud settings. |
| Managed wallets | Turnkey | One Solana wallet per project. Turnkey generates the key and keeps it in its secure enclaves. Rolink asks it for signatures. |
| Solana RPC | Rolink’s RPC providers | Balances, token accounts, mints, signatures and transaction submission, on the cluster your project uses. NFT reads use the DAS API. |
| Open Cloud MessagingService | Roblox | Delivers events to every live server in your experience, using the universe ID and Open Cloud key you enter in the dashboard. |
Why a hosted service
Section titled “Why a hosted service”Some of this can’t be done safely from inside Roblox:
- There are no wallets in Roblox. A game can’t talk to a browser wallet, so players link from a web page instead.
- Game servers have no safe place for a signing key. Roblox Secrets can be sent in requests but never read, so you can’t sign with them. Rolink signs through Turnkey and enforces your limits before it asks.
- HttpService has a quota. Roblox allows about 500 requests per minute per server. Rolink caches chain reads and answers wallet lookups from its own database.
- Signing players in needs a web server. Sign in with Roblox sends the browser back to a callback URL, and a game can’t serve one. Rolink hosts the callback for every game, and signs players in through each game’s own OAuth app.
Projects keep games apart
Section titled “Projects keep games apart”A project is one Roblox experience. Everything Rolink keeps is attached to a project:
- API keys. A key resolves to exactly one project. Every
/v1request is answered with that project’s data and nothing else. - Wallet links. A player who links a wallet in one game hasn’t linked it in another. Each project has its own linking page and its own links.
- Player sign-in. Each project signs players in through its own Roblox OAuth app, and a player session only counts on the linking page of the game it was opened for.
- The managed wallet and its policy. Each project has its own wallet, SOL and token limits, hourly budgets and transaction history.
- Quotas. Requests and transactions are counted per project.
- Events. Each project publishes to its own universe ID, with its own Open Cloud key and topic.
- The cluster. Each project is on devnet or mainnet, chosen when you create it.
Only the developer who created a project can see or change it in the dashboard.
Linking a wallet
Section titled “Linking a wallet”- The player opens your project’s linking page in a browser.
- Sign in with Roblox. Rolink runs the OAuth 2.0 authorization code flow with PKCE, with the
openidandprofilescopes, through your game’s own OAuth app. It reads the player’s user ID, username, display name and avatar, revokes the Roblox access token right away, and sets a signed,HttpOnlysession cookie for your game that lasts one hour. - Pick a wallet. The page lists wallets that implement the Wallet Standard with
standard:connectandsolana:signMessage. If there are none, it falls back to an older injected provider. - Get a challenge. Rolink answers with a message in Sign-In With Solana format. It names the Rolink domain, your linking page, the wallet, the Roblox account, your game and its cluster, with a single-use nonce that expires after 10 minutes.
- Sign. The wallet signs the message. Signing is free and can’t move funds. Rolink checks the signature against the wallet’s public key.
- Store and announce. Rolink saves the link for your project and publishes
wallet_linked. If the wallet was linked to another account in the same project, that link moves, and the other account getswallet_unlinked.
Link wallets shows the exact message and the rules around it.
Reading chain data
Section titled “Reading chain data”- The SDK calls the game API, for example
GET /v1/wallets/:address/tokens?mint=…, with thex-api-keyheader. - Rolink serves the answer from its cache or asks a Solana RPC on your project’s cluster. Token balances cover both the Token and Token-2022 programs. NFT and collection reads use the DAS API.
- The SDK returns amounts as decimal strings, such as
"12.5".
Wallet lookups (GetWallet, GetWallets) never touch the chain. Rolink answers them from its database.
Sending a transaction
Section titled “Sending a transaction”- The SDK posts a request, for example
TransferToken({ to, mint, amount, idempotencyKey }). - Same key seen before? If the body matches and the earlier attempt didn’t fail, Rolink returns that result and sends nothing new. A different body with the same key gets
409 idempotency_conflict. - Check the policy and the quota. The project must have a managed wallet. The asset must be allowed and the amount within its per-transaction limit. Then Rolink checks the hourly limits per recipient wallet and per player, the asset’s hourly budget, and the project’s beta transaction quota. You set the policy in the dashboard.
- Reserve the key. Rolink records the request as
pendingin the same step as the limit checks, so two requests can’t both slip under a limit. - Sign and send. Rolink builds the transaction with the project wallet as fee payer, asks Turnkey to sign it, and verifies the signature before it trusts it. If the recipient has no token account for the mint yet, the transaction creates one. Rolink saves the signature before it sends, so an interrupted send can still be reconciled.
- Wait, then keep tracking. The request waits up to 15 seconds for confirmation and answers
confirmed,failedorsubmitted. Asubmittedrequest keeps being tracked, and its final result arrives as atxevent or throughGetTransaction.
A transaction is reported as expired only when the finalized chain is past its blockhash and an up-to-date node has no record of it. A failed request moved no funds, so retrying it under the same key is safe. Send transactions covers the policy and the statuses.
Live events
Section titled “Live events”- Something happens in your project: a wallet is linked or unlinked, a transaction reaches a final status, or a watched address has a new transaction.
- Rolink publishes a small JSON message (
{ "v": 1, "type": … }) to your project’s topic in your universe through Open Cloud, with your Open Cloud key. It retries rate limits and server errors a few times, and trims long transaction errors to fit MessagingService’s 1 kB message limit. - Each game server’s SDK is subscribed to the same topic. It decodes the message and fires
WalletLinked,WalletUnlinked,TransactionUpdatedorChainActivity.
The four event types are wallet_linked, wallet_unlinked, tx and watch. Their fields are listed in the Events reference. Without a universe ID and an Open Cloud key, nothing is published and everything else still works.
Watching addresses
Section titled “Watching addresses”Rolink polls each address you add in the dashboard every 5 seconds. The first poll only records where history ends, so old transactions are never replayed. After that, every new transaction is published as a watch event, oldest first, and the SDK fires ChainActivity. See Watch addresses.
What Rolink stores
Section titled “What Rolink stores”Rolink keeps public data, hashes and encrypted credentials. It never holds a private key in its database.
| Data | Holds |
|---|---|
| Developers | The wallet address you sign in to the dashboard with, and when you first and last signed in. |
| Projects | Name, slug, cluster, universe ID, event topic, your Roblox OAuth app’s client ID, transaction policy, watched addresses, and the managed wallet’s Turnkey ID and public address. |
| Open Cloud keys | Encrypted with AES-256-GCM. Decrypted only to publish your events or send a test event. |
| Roblox OAuth client secrets | Encrypted with AES-256-GCM. Decrypted only to sign players in on your linking page. |
| API keys | A name, the first 16 characters, and a SHA-256 hash. The full key is shown once and never stored. |
| Wallet links | Per project: the player’s Roblox user ID and username, the wallet address and the link time. |
| Link challenges | Pending link messages and their nonces. Each is single-use and expires after 10 minutes. |
| Transactions | One record per idempotency key: kind, recipient, asset, amount in base units, signature, status, error and timestamps. |
| Watch cursors | The last signature seen on each watched address. |
| Usage | Requests and transactions per project per UTC day. |
What it doesn’t store:
- Players’ private keys. Rolink never sees them. A player only sends a public address and a signature.
- Project wallet keys. They’re generated and kept inside Turnkey. Rolink stores the wallet’s ID and address.
- Roblox tokens. The access token is revoked as soon as Rolink has read the user ID.
- Sessions and pending sign-ins. They live in signed cookies, not in the database.
Caching
Section titled “Caching”Two caches keep traffic down. Neither one ever holds a private key.
| Cache | Where | What | How long |
|---|---|---|---|
| Chain reads | Rolink | SOL balances, token balances, assets | Up to 10 seconds |
| Wallet lookups | Each game server | GetWallet and GetWallets answers, including “no wallet” | walletCacheSeconds, 60 seconds by default |
Rolink also merges identical reads that arrive at the same time into one RPC call. Signature status and transaction routes are never cached.
On game servers, wallet_linked and wallet_unlinked events update the wallet cache as soon as they arrive. A lookup that started before an event can’t overwrite the newer answer. GetWallets fetches up to 200 users per request.
Events are hints
Section titled “Events are hints”MessagingService delivers on a best-effort basis. A message can be lost, and a server that starts later never sees messages published before it subscribed. Use events to react quickly, and read the API when correctness matters:
- Wallets. Call
GetWalletwhen a player joins rather than waiting forWalletLinked. - Payments. Treat
TransactionUpdatedas a nudge, and confirm withGetTransaction(idempotencyKey)before you grant anything that depends on the result. - Linking. Rolink never makes the player wait on Open Cloud. If publishing fails, the link is saved anyway.
