Guide
App security
A mini app runs untrusted web content inside SprinTelex, so the trust model is explicit: the platform guarantees the container, the developer guarantees their own server. This page lists both sides.
Origin binding
The viewer locks navigation to your app's verified domain and its subdomains, over HTTPS only. Any navigation anywhere else is blocked. Links your app opens with SprinTelex.openLink leave the viewer entirely and open in the system browser, where the user sees the real address bar.
The bridge itself is only callable from your registered origin, and only over HTTPS. Plain HTTP app URLs are refused at registration, and the viewer never loads them.
Client chrome you cannot fake
The viewer always renders its own header above your page: your app's name and its verified domain, plus which bot opened it. That chrome is drawn by the client, never by your HTML, so a phishing page inside the viewer cannot impersonate SprinTelex UI or hide which site the user is really on.
initData: never trust the client
Everything in window.SprinTelex is client data. The signature is what makes it trustworthy, and only your server can check it, because only your server holds the app_secret. Validate on every request that acts on a user, not just at session creation: recompute the HMAC chain, compare in constant time, and enforce the auth_date freshness window (30 minutes normally, 24 hours absolute maximum). initData carries no nonce, so that TTL is your only replay defense.
See the Building a mini app guide for the exact algorithm, the worked test vector, and copy-paste validators in Python and Node.js.
app_secret vs bot token
Your bot has two separate secrets because they protect two different things:
| Bot token | app_secret | |
|---|---|---|
| Proves | Your server may act as the bot (Bot API) | A user really opened your app (initData) |
| Lives on | Your backend only | Your backend only (never in page JS) |
| If leaked | Attacker controls the bot: rotate it | Attacker can forge app sessions: regenerate it |
The separation matters because your web server needs the app_secret to validate sessions, and web servers get compromised more often than locked-down bot backends. A leaked app_secret lets an attacker forge app sessions, but it grants zero Bot API access: they cannot send messages as your bot or read its updates. Regenerating the secret instantly invalidates every previously signed payload.
The 4KB sendData limit
sendData payloads are capped at 4KB, enforced on the client (which shows a "Too much data" alert) and again on the server (400 data must be at most 4KB). Keep app-to-bot messages small by design: send choices and references, not documents. If you need to move bulk data, have your server fetch it directly using the validated session.
Hardened container
The viewer runs your page with file access disabled, geolocation disabled, media capture gated behind user action, third-party and shared cookies disabled, and no generic script gateway: each bridge member is a single-purpose handler whose parameters are validated as attacker input. Your page gets its own app-scoped storage and nothing else.
What the platform guarantees vs what you must do
| Platform guarantees | You must do |
|---|---|
| Navigation locked to your verified domain, HTTPS only | Keep the verified domain yours; re-verify after moving |
| App name plus domain always visible in client chrome | Never draw a fake header or hide where the user is |
| initData signed with your per-bot app_secret | Validate the signature server side on every sensitive request |
| sendData authenticated as the opener, capped at 4KB | Treat the payload as user input; validate it like any other |
| Secret shown once, regenerable, never re-readable | Store it as an env var; rotate it if it ever leaks |
| Suspended or deleted bots and apps cannot launch | Handle launch failures gracefully in your page |