Skip to content

HTTP API reference

The Luau SDK wraps the game API, so you only need this page to call Rolink from another language, to understand what the linking page does, or to debug.

  • Base URL. Every path on this page is relative to https://api.rolink.tech. The SDK uses it by default.

  • HTTPS and JSON. Request bodies are JSON. Every response is JSON, except the linking page (HTML) and the redirects of the sign-in flow.

  • Amounts are decimal strings in token units, such as "1.5". Raw base units are also strings, such as "1500000000".

  • Times such as linkedAt are Unix timestamps in milliseconds.

  • Errors share one shape, with a stable code and a human-readable message:

    { "error": { "code": "wallet_not_linked", "message": "Player 42 has not linked a wallet" } }

    Every code is listed in Error codes.

These errors can come from any route:

CodeHTTPWhen
invalid_json400A route that reads a body got one that is not valid JSON, or no body at all.
invalid_request400The body does not match the route’s schema. The message lists each problem as path: problem, separated by ; .
not_found404No such route.
internal500An unexpected error, such as an RPC failure during a read.

An invalid_request example:

{
"error": {
"code": "invalid_request",
"message": "amount: Invalid input: expected string, received number; idempotencyKey: Too small: expected string to have >=1 characters"
}
}

Routes under /v1 are for your game servers. Each request must carry a project API key in the x-api-key header:

x-api-key: rlk_live_<43 characters>

Create keys in the dashboard, under your project’s API keys. A key belongs to one project, and every /v1 route reads and writes only that project’s data: its links, its transactions, its managed wallet and its cluster. Keys are checked before anything else on every /v1 route.

CodeHTTPWhen
unauthorized401The x-api-key header is missing, malformed, revoked, or belongs to a deleted project.

A revoked key stops working within about 30 seconds.

Every /v1 request with a valid key counts toward the project’s beta quotas, whatever its answer. Responses carry no rate-limit headers; the dashboard’s Overview shows the project’s usage.

CodeHTTPWhen
rate_limited429More than 600 requests in the current minute for this project.
daily_quota_exceeded429The project used its 100,000 requests for the current UTC day.

Transactions have their own daily quota, daily_tx_quota_exceeded. All quotas are listed in Limits and quotas.

MethodPathAuthPurpose
GET/healthnoneService status
GET/v1/projectAPI keyThe key’s project
GET/v1/players/:userId/walletAPI keyOne player’s wallet
POST/v1/players/walletsAPI keyMany players’ wallets
GET/v1/wallets/:address/balanceAPI keySOL balance
GET/v1/wallets/:address/tokensAPI keySPL and Token-2022 balances
GET/v1/wallets/:address/assetsAPI keyNFTs the wallet owns
GET/v1/signatures/:signatureAPI keyTransaction signature status
POST/v1/tx/transfer-solAPI keySend SOL
POST/v1/tx/transfer-tokenAPI keySend a token
POST/v1/tx/mint-tokenAPI keyMint a token
GET/v1/tx/:idempotencyKeyAPI keyTransaction request status
GET/link/:slugnoneA project’s linking page
GET/api/link/:slug/mesession cookieCurrent player and link
POST/api/link/:slug/challengesame origin + sessionGet a message to sign
POST/api/link/:slug/verifysame origin + sessionProve wallet ownership and link
POST/api/link/:slug/unlinksame origin + sessionRemove the link
GET/auth/roblox/startnoneStart a player’s Sign in with Roblox
GET/auth/roblox/callbacksign-in cookieFinish a player’s Sign in with Roblox
POST/auth/wallet/challengesame originGet the dashboard sign-in message
POST/auth/wallet/verifysame origin + sign-in cookieSign a developer in to the dashboard
POST/auth/logoutsame originEnd the session

The dashboard, at https://api.rolink.tech/dashboard, is a web app for signed-in developers. Its pages are not part of the API.

GET /health

No authentication, and not counted toward any quota. Reports that the service is up.

200
{ "ok": true, "wallets": "turnkey" }
FieldMeaning
oktrue when the API answers.
walletsThe provider that holds managed wallets: turnkey, or none while managed wallets are off. With none, creating a project wallet answers wallets_unavailable and transactions are unavailable. Everything else works.
GET /v1/project
x-api-key: <key>

Returns the project the key belongs to.

200
{
"id": "prj_3fk9Qx2LmP0aB7cD",
"name": "My Game",
"slug": "my-game",
"cluster": "devnet",
"walletAddress": "8Kip13Ac8mYrW6Qq62ERSEFbpu5PVSA9U4xScEk1hpRT",
"linkUrl": "https://api.rolink.tech/link/my-game",
"events": true
}
FieldMeaning
idThe project’s ID.
nameThe name set in the dashboard.
slugSet from the name when the project is created. It doesn’t change when you rename the project.
clusterdevnet or mainnet, chosen when the project is created. Every read and transaction uses it.
walletAddressThe managed wallet’s address, or null until it is created in the dashboard.
linkUrlThe project’s hosted linking page.
eventstrue when the project has both a universe ID and an Open Cloud API key, so live events are published.
GET /v1/players/42/wallet
x-api-key: <key>

Returns the wallet a player linked to this project. address and linkedAt are null when there is none. Answered from Rolink’s database, not the chain.

200
{ "userId": 42, "address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin", "linkedAt": 1791429403583 }
CodeHTTPWhen
invalid_user_id400:userId is not a positive integer.
POST /v1/players/wallets
x-api-key: <key>
Content-Type: application/json
{ "userIds": [42, 43] }

userIds is an array of up to 200 positive integers. The response has one key per requested id, as a string, set to the address or null. The whole lookup counts as one request.

200
{ "wallets": { "42": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin", "43": null } }
CodeHTTPWhen
invalid_json400Missing or malformed body.
invalid_request400userIds is missing, has more than 200 entries, or contains something other than a positive integer.
GET /v1/wallets/9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin/balance
x-api-key: <key>

Read on the project’s cluster at confirmed commitment, and cached for about 10 seconds.

200
{ "address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin", "lamports": "1500000000", "sol": "1.5" }
CodeHTTPWhen
invalid_address400:address is not a Solana address.
GET /v1/wallets/9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin/tokens?mint=4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU
x-api-key: <key>

Returns one entry per mint, with the amounts of all the wallet’s token accounts for that mint added together. Token and Token-2022 accounts are both included. mint is optional:

  • without it, every mint the wallet has a token account for is listed
  • with it, the list always has exactly one entry, with "0" amounts if the wallet holds none

Read at confirmed commitment and cached for about 10 seconds.

200
{
"address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
"tokens": [
{ "mint": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", "amount": "2500000", "decimals": 6, "uiAmount": "2.5" }
]
}
CodeHTTPWhen
invalid_address400:address or mint is not a Solana address.
invalid_mint400mint does not exist on the project’s cluster or is not a token mint.
GET /v1/wallets/9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin/assets?collection=<collection address>
x-api-key: <key>

Lists the NFTs the wallet owns. With collection, only those in that collection.

  • With a DAS endpoint for the project’s cluster, Rolink reads them through the DAS API: getAssetsByOwner, or searchAssets grouped by the collection. DAS also sees compressed NFTs and Metaplex Core assets.
  • Without one, Rolink reads classic NFTs straight from the chain: Metaplex Token Metadata NFTs, programmable NFTs and editions whose supply is 1. image is then null, because it lives in the off-chain metadata.

Either way, collection is set only for a verified collection, so a mint can’t claim to belong to yours. Only the first 1,000 assets are read. Cached for about 10 seconds.

200
{
"address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
"assets": [
{
"id": "<asset address>",
"name": "Founder #12",
"collection": "<collection address>",
"image": "https://example.com/founder-12.png",
"interface": "V1_NFT"
}
]
}

name, collection, image and interface are null when the asset’s metadata does not have them.

CodeHTTPWhen
invalid_address400:address or collection is not a Solana address.
GET /v1/signatures/5z99PpyTfnCYHDAbyVoMFuHD3gWQFis2izi9NSJKEbijXWUQQnuWxcnAKUFxAhSJcJMExR9MfN69R1PhsvGiQYeu
x-api-key: <key>

Looks up any transaction signature on the project’s cluster, searching the RPC node’s transaction history. Not cached.

200
{
"signature": "5z99PpyTfnCYHDAbyVoMFuHD3gWQFis2izi9NSJKEbijXWUQQnuWxcnAKUFxAhSJcJMExR9MfN69R1PhsvGiQYeu",
"status": "finalized",
"slot": 312345678,
"error": null
}
FieldValues
statusprocessed, confirmed, finalized, failed, or unknown when the node has no record of the signature
slotThe slot it landed in, or null when unknown
errorThe on-chain error as a JSON string when failed, such as "{\"InstructionError\":[1,{\"Custom\":1}]}", otherwise null
CodeHTTPWhen
invalid_signature400:signature is not a base58 transaction signature.

The three transaction routes sign and send with the project’s managed wallet. The wallet must exist, and each request must fit the transaction policy set in the dashboard. The guide is Send transactions.

FieldTypeRules
toobjectExactly one of { "userId": 42 } (a positive integer, resolved to the wallet the player linked to this project) or { "address": "<base58>" }. No other keys.
mintstringToken routes only. A mint listed under Allowed mints in the policy.
amountstringA decimal in token units matching ^\d+(\.\d+)?$, such as "1.5". Greater than zero, with no more decimals than the asset has (9 for SOL).
idempotencyKeystring1 to 128 characters. Unique per intended payment within the project.

Every transaction route, and the status route, answers 200 with a TxResult:

200
{
"idempotencyKey": "quest:42:intro",
"kind": "transfer-token",
"status": "confirmed",
"signature": "5z99PpyTfnCYHDAbyVoMFuHD3gWQFis2izi9NSJKEbijXWUQQnuWxcnAKUFxAhSJcJMExR9MfN69R1PhsvGiQYeu",
"recipient": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
"error": null
}
FieldMeaning
kindtransfer-sol, transfer-token or mint-token
statusconfirmed: it landed. failed: nothing reached the recipient, see error. submitted: sent, not yet confirmed. pending: another request with this key is being prepared.
signatureSet once the managed wallet signed the transaction, otherwise null.
recipientThe wallet address the request resolved to.
errorWhy it failed, otherwise null.

The request waits up to 15 seconds for confirmation. If it takes longer, the answer is submitted and Rolink keeps checking the signature every 1.5 seconds in the background, with a sweep every 30 seconds for anything still submitted. The final result arrives as a tx event and through GET /v1/tx/:idempotencyKey.

A failed status is an answer, not an error. For example, when the network rejects the transaction in simulation (the managed wallet is short of funds, or is not the mint authority), the route answers 200 with "status": "failed" and the simulation error in error. The hosted API reports it in a coded form: Solana error #-32002; Decode this error by running followed by an npx @solana/errors decode command that turns it back into the readable message, Transaction simulation failed.

200
{
"idempotencyKey": "tournament:7:42",
"kind": "transfer-sol",
"status": "failed",
"signature": "4huotLhmwu1MzxAgG1z8fhctptGdAsG3teAo1QPcxQkddj3jiGmxuK4WQydFK9j8gYUCDWNaSkMQePLdAGBbpEcf",
"recipient": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
"error": "Solana error #-32002; Decode this error by running `npx @solana/errors decode -- -32002 '<details>'`"
}

A transaction is reported failed with Transaction expired before it was confirmed only when that is proven: the finalized chain is more than 32 blocks past the blockhash’s last valid height, and a node that has seen that finalized slot still has no record of the signature.

Rolink stores every key a project uses, permanently, so these rules always hold:

  • Same key, same body, earlier attempt pending, submitted or confirmed: Rolink returns that attempt and sends nothing new.
  • Same key, same body, earlier attempt failed: Rolink sends again, because a failed attempt moved no funds.
  • Same key, different body: 409 idempotency_conflict. The body is compared as sent, so { "userId": 42 } and { "address": "<the same wallet>" } differ, and so do "1" and "1.0".

A request refused before step 14 of the order of checks never reserves its key. Fix the request and send it again with the same key. A 502 at step 14 is different: the attempt is recorded as failed with that body, so retry it with the same body and key.

A transaction request is checked in this order. The first failure is returned.

StepCodeHTTP
1. API keyunauthorized401
2. Request quotarate_limited, daily_quota_exceeded429
3. Body is JSONinvalid_json400
4. Body matches the schemainvalid_request400
5. mint is an address (token routes)invalid_address400
6. The managed wallet existswallet_not_configured409
7. The key is new, or was used for the same bodyidempotency_conflict409
8. to resolves to a walletwallet_not_linked (404), invalid_address (400)
9. The asset is allowedsol_transfers_disabled (SOL), mint_not_allowed (tokens)403
10. The mint exists on the cluster (token routes)invalid_mint400
11. The amount is valid and within the per-transaction limitinvalid_amount (400), amount_over_limit (403), invalid_limit_config (422)
12. The daily transaction quotadaily_tx_quota_exceeded429
13. Recipient, player and budget limitsrecipient_rate_limited, player_rate_limited, hourly_budget_exceeded (429), invalid_limit_config (422) for a budget the asset’s decimals cannot represent
14. The transaction can be built and signedrpc_error, wallet_unavailable502

When step 7 finds an earlier attempt that is not failed, Rolink returns it right there. Steps 8 to 13 have no side effects, and the key is reserved only after step 13, in the same step as the limit checks.

POST /v1/tx/transfer-sol
x-api-key: <key>
Content-Type: application/json
{ "to": { "userId": 42 }, "amount": "0.05", "idempotencyKey": "tournament:7:42" }

Sends SOL from the managed wallet with a System Program transfer. Disabled until the policy has a Max SOL per transfer. Answers a TxResult with "kind": "transfer-sol".

POST /v1/tx/transfer-token
x-api-key: <key>
Content-Type: application/json
{
"to": { "address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin" },
"mint": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
"amount": "25",
"idempotencyKey": "quest:42:intro"
}

Sends a token from the managed wallet’s associated token account with a checked transfer, under the mint’s own program (Token or Token-2022). The recipient’s associated token account is created first if needed, paid for by the managed wallet. Answers a TxResult with "kind": "transfer-token".

POST /v1/tx/mint-token
x-api-key: <key>
Content-Type: application/json
{ "to": { "userId": 42 }, "mint": "<mint address>", "amount": "1", "idempotencyKey": "badge:42:founder" }

Mints new tokens to the recipient’s associated token account, creating it if needed. The managed wallet must be the mint authority. If it is not, the network rejects the transaction and the answer is "status": "failed". Answers a TxResult with "kind": "mint-token".

GET /v1/tx/quest%3A42%3Aintro
x-api-key: <key>

Returns the stored TxResult for a key. URL-encode the key. It answers right away. If the request is still submitted, Rolink resumes checking its confirmation in the background. It works whether or not the managed wallet exists.

CodeHTTPWhen
not_found404The project has no request with this key.

Each project has a hosted linking page at /link/<slug>, where players sign in with Roblox through the project’s own OAuth app and prove they own a wallet. These routes serve it. They’re documented so you can understand and debug the flow. Game servers never call them.

  • Per project. The :slug in each path is the project’s slug. Every link created here belongs to that project only.
  • Session. Signing in sets a player session cookie, __Host-rolink_session, for one hour. It is HttpOnly, Secure, SameSite=Lax and Path=/, and signed with HMAC-SHA256. It belongs to the project whose linking page the player signed in on. On another project’s routes it counts as no session, and a new sign-in replaces it.
  • Same origin. Every POST route requires an Origin header equal to the API’s origin, checked first. A cross-site page cannot forge these requests.
CodeHTTPWhen
bad_origin403A POST without a matching Origin header.
not_signed_in401A route that needs a session got no valid, unexpired session cookie for this project.
project_not_found404No active project has this slug.
GET /link/my-game

Serves the project’s linking page as HTML. For an unknown slug, or a deleted project, it answers 404 with a short HTML page: “No game uses this link.” The page is sent with these headers, so it cannot be framed:

Content-Security-Policy: frame-ancestors 'none'; base-uri 'none'; form-action 'self'
X-Frame-Options: DENY
Referrer-Policy: same-origin
Cache-Control: no-store

Until the project has a Roblox OAuth app saved (a client ID and a client secret), the page disables Continue with Roblox and shows “This game hasn’t set up Roblox sign-in yet.”

When sign-in fails, the callback redirects back to this page with ?error=<code>, and the page shows a matching message. See Sign-in redirect errors.

GET /api/link/my-game/me

Returns the signed-in player and their link in this project. Without a valid session for this project, both are null.

200
{
"user": { "id": 42, "name": "builderman", "displayName": "Builderman", "avatar": "https://tr.rbxcdn.com/..." },
"link": { "address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin", "linkedAt": 1791429403583 }
}
200, signed out
{ "user": null, "link": null }

name is the Roblox username, displayName the display name and avatar the profile picture URL from Roblox, or null. link is null when the player has not linked a wallet to this project.

POST /api/link/my-game/challenge
Origin: <API origin>
Cookie: __Host-rolink_session=<session>
Content-Type: application/json
{ "address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin" }

Returns a single-use nonce and the message the wallet must sign. The challenge expires after 10 minutes.

200
{
"nonce": "f9f0a3be5b153047d5aaec41d21e7b5d",
"message": "api.rolink.tech wants you to sign in with your Solana account:\n9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin\n\nLink this wallet to Roblox account @builderman (42) in My Game. Signing is free and does not send a transaction.\n\nURI: https://api.rolink.tech/link/my-game\nVersion: 1\nChain ID: devnet\nNonce: f9f0a3be5b153047d5aaec41d21e7b5d\nIssued At: 2026-10-08T03:16:43.594Z\nExpiration Time: 2026-10-08T03:26:43.594Z"
}

The message follows the Sign-In With Solana layout, so wallets that support it can warn when the domain does not match the site asking for the signature:

api.rolink.tech wants you to sign in with your Solana account:
9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin
Link this wallet to Roblox account @builderman (42) in My Game. Signing is free and does not send a transaction.
URI: https://api.rolink.tech/link/my-game
Version: 1
Chain ID: devnet
Nonce: f9f0a3be5b153047d5aaec41d21e7b5d
Issued At: 2026-10-08T03:16:43.594Z
Expiration Time: 2026-10-08T03:26:43.594Z

The first line is the API’s host, My Game is the project’s name, URI is the project’s linking page and Chain ID is the project’s cluster.

CodeHTTPWhen
bad_origin403Missing or foreign Origin.
project_not_found404Unknown slug.
not_signed_in401No valid session.
invalid_json, invalid_request400The body is not { "address": string }.
invalid_address400address is not a Solana address.
POST /api/link/my-game/verify
Origin: <API origin>
Cookie: __Host-rolink_session=<session>
Content-Type: application/json
{ "nonce": "f9f0a3be5b153047d5aaec41d21e7b5d", "signature": "<base64>" }

signature is the base64-encoded 64-byte ed25519 signature of message, encoded as UTF-8. Rolink consumes the nonce, checks the signature against the challenge’s address, and links the wallet to the signed-in player in this project.

200
{ "link": { "address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin", "linkedAt": 1791429403583 } }

Within a project, a wallet belongs to one Roblox account at a time, and a player has one wallet at a time. Linking replaces the player’s previous wallet and takes the wallet away from any other account that had it in this project. Afterwards Rolink publishes:

  • wallet_unlinked for the other account, if the wallet was linked to one
  • wallet_linked for this player
CodeHTTPWhen
bad_origin403Missing or foreign Origin.
project_not_found404Unknown slug.
not_signed_in401No valid session.
invalid_json, invalid_request400The body is not { "nonce": string, "signature": string }.
challenge_expired400The nonce is unknown, already used, older than 10 minutes, issued for another project, or issued to another player. Request a new challenge.
invalid_signature400The signature does not decode to 64 bytes (Malformed signature), or does not match the wallet (The signature does not match this wallet).
POST /api/link/my-game/unlink
Origin: <API origin>
Cookie: __Host-rolink_session=<session>

Removes the signed-in player’s link in this project and publishes wallet_unlinked if there was one. Answers the same way when there was nothing to remove.

200
{ "ok": true }
CodeHTTPWhen
bad_origin403Missing or foreign Origin.
project_not_found404Unknown slug.
not_signed_in401No valid session.

There are two sign-ins, and Rolink has no Roblox app of its own:

  • Players sign in with Roblox on a project’s linking page, through that project’s own Roblox OAuth app. Its client ID and secret are saved in the project’s settings.
  • Developers sign in to the dashboard by signing a message with a Solana wallet.
GET /auth/roblox/start?next=/link/my-game

Starts Sign in with Roblox (OAuth 2.0 with PKCE) for the linking page in next, through that project’s Roblox OAuth app. The route stores a random state, the PKCE verifier, next and the project in a signed cookie valid for 10 minutes, then redirects (302) to Roblox’s authorization page with the project’s client ID, the redirect URI https://api.rolink.tech/auth/roblox/callback, the scopes openid profile and an S256 code challenge.

Instead of an error body, it redirects in these cases, without going to Roblox:

CaseRedirects to
next is missing, or not a linking page or dashboard path on this site/dashboard
next is a dashboard path: the dashboard uses wallet sign-innext
No active project has the slug in nextnext, which answers “No game uses this link.”
The project has no Roblox OAuth app savednext?error=oauth_not_configured
GET /auth/roblox/callback?code=<code>&state=<state>

Roblox redirects here. It’s the redirect URL every project registers on its Roblox OAuth app. Rolink deletes the sign-in cookie, checks state, exchanges code for a token with the project’s client ID and secret and the PKCE verifier, reads the user’s identity, then revokes the Roblox token, since it only needed the identity. It starts a player session for that project (one hour) and redirects to the linking page.

On failure it redirects to the linking page with ?error=<code> instead of answering an error body. Without a valid sign-in cookie it can’t tell which page that is, and redirects to /dashboard?error=oauth_state_mismatch. The codes are listed in Sign-in redirect errors.

POST /auth/wallet/challenge
Origin: <API origin>
Content-Type: application/json
{ "address": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin", "next": "/dashboard" }

address is the wallet signing in. next, optional, is the dashboard page to land on afterwards; anything that isn’t a dashboard path becomes /dashboard. Rolink answers with the message the wallet must sign, and keeps the address, the message and next in a signed HttpOnly cookie, __Host-rolink_signin, valid for 10 minutes. Nothing is written to the database.

200
{
"message": "api.rolink.tech wants you to sign in with your Solana account:\n9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin\n\nSign in to the Rolink dashboard. Signing is free and does not send a transaction.\n\nURI: https://api.rolink.tech/dashboard\nVersion: 1\nChain ID: mainnet\nNonce: 0b6c2f4e9a1d3e5f7a8b9c0d1e2f3a4b\nIssued At: 2026-10-08T03:16:43.594Z\nExpiration Time: 2026-10-08T03:26:43.594Z"
}

The message follows the Sign-In With Solana layout:

api.rolink.tech wants you to sign in with your Solana account:
9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin
Sign in to the Rolink dashboard. Signing is free and does not send a transaction.
URI: https://api.rolink.tech/dashboard
Version: 1
Chain ID: mainnet
Nonce: 0b6c2f4e9a1d3e5f7a8b9c0d1e2f3a4b
Issued At: 2026-10-08T03:16:43.594Z
Expiration Time: 2026-10-08T03:26:43.594Z
CodeHTTPWhen
bad_origin403Missing or foreign Origin.
invalid_json, invalid_request400The body is not { "address": string, "next"?: string }.
invalid_address400address is not a Solana address.
POST /auth/wallet/verify
Origin: <API origin>
Cookie: __Host-rolink_signin=<pending sign-in>
Content-Type: application/json
{ "signature": "<base64>" }

signature is the base64-encoded 64-byte ed25519 signature of message, encoded as UTF-8. Rolink deletes the sign-in cookie, verifies the signature against the address it was issued for, and signs the developer in: the wallet address is the account, created on its first sign-in. It sets the developer session cookie, __Host-rolink_dev, for 12 hours, and answers with the page to open.

200
{ "next": "/dashboard" }
CodeHTTPWhen
bad_origin403Missing or foreign Origin.
challenge_expired400No valid sign-in cookie: it is missing, older than 10 minutes, or was already used. Request a new challenge.
invalid_json, invalid_request400The body is not { "signature": string }.
invalid_signature400The signature does not decode to 64 bytes (Malformed signature), or does not match the wallet (The signature does not match this wallet).
POST /auth/logout
Origin: <API origin>

Clears the player and developer sessions. Needs the same-origin header but not a session. Answers { "ok": true }, or redirects to / when the request accepts HTML.

200
{ "ok": true }
CodeHTTPWhen
bad_origin403Missing or foreign Origin.