Snippet
The embed widget is an iframe that loads the tracking page view from https://walletsleuth.app/embed/tracking/{trackingPageId}?key={publicApiKey}. The dashboard generates the exact snippet for you, but the mechanics are simple enough to write by hand.
The snippet
<iframe
src="https://walletsleuth.app/embed/tracking/YOUR_PAGE_ID?key=ws_pub_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
width="100%"
height="600"
frameborder="0"
allow="clipboard-write"
style="border-radius: 12px;"
></iframe>
What each part does
| Part | Required | Notes |
|---|---|---|
src path /embed/tracking/{id} | yes | id is the tracking page ID, visible in the dashboard URL. |
?key=ws_pub_... | yes | Public API key in the query string. The iframe route rejects missing keys, revoked keys, and secret keys. |
width="100%" | recommended | Fills the parent container. The widget has an internal min-width but is fully responsive. |
height="600" | required | The iframe has a fixed height — the widget does not currently auto-resize or post a contentHeight message to the parent. Pick a height that comfortably fits your wallet table; 600px is a good starting point. |
frameborder="0" | recommended | Removes the default iframe border for a seamless look. |
allow="clipboard-write" | recommended | Lets visitors copy wallet addresses from the table directly. |
style="border-radius: 12px;" | optional | Purely cosmetic. |
Why ?key= is in the URL, not a header
Iframes can't set request headers on their initial document load. The key has to travel in a way the browser will send automatically — which leaves the URL. The server resolves the key, validates it against the allowed origins of the parent page (via Content-Security-Policy: frame-ancestors), and then renders the shell.
Because the key is a public key with an origin allowlist, exposing it in HTML is not a leak — it only works from the origins you've allowlisted. Treat it like a publishable Stripe key.
Sizing notes
- Height is fixed. The widget doesn't postMessage its content height. Give it a value generous enough for the wallet table without internal scrolling, or accept the internal scroll (the table is scrollable by design).
- Width works best at
100%. The layout is designed to adapt down to roughly 320px on mobile. - No lazy-loading recommended. The iframe is server-rendered and is fast;
loading="lazy"works but isn't necessary.
CSP on the parent page
The iframe itself sends Content-Security-Policy: frame-ancestors <your-allowed-origins>, which means the browser will refuse to render it inside an origin not on the key's allowlist. This is the authoritative check.
Your parent page does not need to mention walletsleuth.app in its own CSP. If your parent page sets a strict CSP that forbids framing altogether, you need to relax it enough to allow child iframes. A minimal compatible policy for the parent looks like:
Content-Security-Policy: frame-src https://walletsleuth.app;
Multiple widgets on one page
You can embed widgets for multiple tracking pages on the same parent page. Each iframe is isolated: separate API key, separate session cookie, separate rate-limit buckets. There's no cross-iframe communication.
What the iframe actually renders
The iframe loads a compact view of the tracking page:
- A header with
tokenNameand$tokenSymbol(plus optionalpublicTitleandpublicCommentsset by the owner). - A scrollable wallet table showing address, tags, and balance. Wallets marked "hide from public" by the owner are excluded.
- A collapsible Submit a wallet form for visitors to add their own wallet (requires a Solana signature — see SIWS signing).
- A "Your submissions" section for the current visitor session.
Related
- Authentication — how public keys and the origin allowlist work.
- SIWS signing — the signature flow behind "Submit a wallet".
- Customization — what's owner-configurable.
- GET /tracking/{id} — build your own UI off the same data.