SIWS signing
When a visitor submits a wallet through the widget, they must sign a Sign-In-With-Solana (SIWS) message proving they own the key. This page explains the flow, the message format, and why the widget has both an in-frame signing path and a popup fallback.
The flow
- The widget calls
POST /api/embed/siws/noncewith the tracking page ID and the wallet address the visitor intends to prove. See POST /siws/nonce. - The server returns a
nonceIdand the fullmessageto sign. - The visitor's wallet signs
messagebytes (UTF-8). The widget encodes the resulting signature as base64. - The widget calls
POST /api/embed/submissionswith the signature, nonce ID, nickname, and wallet address. See POST /submissions. - The server verifies the signature against the rebuilt message, runs policy checks, and writes the submission.
The message format
The server builds the message deterministically so callers never need to construct it themselves. The server returns the full message; treat it as opaque and sign it byte-for-byte.
The last line of the message is a human-readable disclaimer shown inside wallet UIs: "This signature does not authorize any transaction."
The server rebuilds the exact message when verifying the signature; any discrepancy (different line endings, altered whitespace) fails with 401 invalid_signature. Don't reconstruct the message on the client.
Why there's a popup fallback
When the widget is inside an iframe on a third-party site, some wallet extensions refuse to pop a signing prompt. Phantom in particular does not reliably expose signMessage to sandboxed iframes. To work around this, the widget offers two signing paths:
In-frame signing (default)
The visitor clicks "Connect wallet" inside the iframe (via @solana/wallet-adapter). If the adapter surfaces a working signMessage, the widget signs directly and submits.
Popup signing (fallback)
The visitor clicks "Can't sign in-frame? Use popup" (or enters a wallet address manually). The widget opens a signing page at the Wallet Sleuth origin in a new window.
The popup page runs at the Wallet Sleuth origin, which is a regular top-level browsing context — wallet extensions treat it like any other dApp and sign freely. After the visitor signs in the popup, the signature is posted back to the iframe and the submission completes.
When does the popup path trigger? Automatically when the visitor chooses "Enter address manually" (no wallet-adapter connection available), and manually via the "Use popup" link under the form. If you're building your own UI off the API, implement either path yourself — the server doesn't care which was used.
What visitors actually sign
Nothing. The final line of the message makes this explicit: "This signature does not authorize any transaction." It's a challenge-response proof of private-key ownership. No tokens move, no on-chain transactions are produced, and the signature is not replayable against any other system because it includes a Wallet Sleuth-scoped nonce.
Related
- POST /siws/nonce
- POST /submissions
- Concepts → Verification policy — what happens after a valid signature lands.
- Concepts → Trust ladder — auto-approval after repeated valid submissions.