Security
How tokens work, how embeds authenticate, and the "never in URL" rules.
Core principles
- Never put secrets in URLs. URLs leak via browser history, logs, analytics, referrers, screenshots, and support tickets.
- Embed tokens (mtok_...) are for customer-facing embeds and API calls in embed mode.
- Owner keys (mk_...) are for management only (creating/revoking tokens, settings). Never use mk_... in embeds.
Golden rule: Embed token is passed only via HTTP header or postMessage. Not in query params.
Token handling
- Raw token value is shown only once on creation. Storage is hash-only on the server.
- Rotate tokens regularly. Revoke immediately if it appears in logs, support screenshots, or public repos.
- Do not hardcode tokens in frontend bundles. Prefer server-side injection or runtime configuration in the host app.
// OK: Tokens are NEVER passed in the URL. // OK: Embed API calls must send mtok via header: x-microapp-embed-token: mtok_... // NO: Never do this: Do not pass tokens in the URL query string. Query-string tokens are not accepted.
Embed flow (iframe)
The iframe URL contains only the tool slug (no token). The host page sends the token via postMessage after the iframe signals READY.
Iframe HTML
<iframe id="microapp" src="https://YOUR_MICROAPP_HOST/embed/YOUR_SLUG" style="width:100%;height:720px;border:0;border-radius:16px;overflow:hidden" loading="lazy" title="Microapp embed" ></iframe>
Host postMessage script
// Host-side: send token via postMessage after the iframe says READY
const iframe = document.getElementById("microapp");
window.addEventListener("message", (ev) => {
if (!iframe || ev.source !== iframe.contentWindow) return;
const data = ev.data || {};
if (data.type === "MICROAPP_EMBED_READY") {
iframe.contentWindow.postMessage(
{
type: "MICROAPP_EMBED_INIT",
slug: "YOUR_SLUG",
token: "mtok_...",
config: { locale: "en-US" }
},
ev.origin
);
}
});Origin checks: Always validate ev.origin and only respond to messages from
your expected embed origin.
Widget flow
The widget SDK loads an iframe and performs an auth handshake via postMessage internally. The token is never placed in the iframe URL.
<!-- Widget (SDK mount): token is NEVER in the URL -->
<script src="https://YOUR_MICROAPP_HOST/microapp-widget.js"></script>
<script>
(function(){
function boot(){
if (!window.MicroappWidget) return setTimeout(boot, 50);
window.MicroappWidget.mount({
baseUrl: "https://YOUR_MICROAPP_HOST",
toolSlug: "YOUR_SLUG",
mtok: "mtok_...",
mode: "floating",
anchor: "bottom-right",
offsetX: 16,
offsetY: 16
});
}
boot();
})();
</script> If you paste a real token into snippets, treat that snippet as secret material.
Embed API authentication
In embed mode, call endpoints with embed=1 and pass x-microapp-embed-token header.
Schema
curl -s "https://YOUR_MICROAPP_HOST/api/tools/YOUR_SLUG/schema?embed=1" \ -H "accept: application/json" \ -H "x-microapp-embed-token: mtok_..."
Run
curl -s "https://YOUR_MICROAPP_HOST/api/tools/YOUR_SLUG/run?embed=1" \
-H "content-type: application/json" \
-H "accept: application/json" \
-H "x-microapp-embed-token: mtok_..." \
--data '{"input":{"q":"hello"}}'Hard rule: Query-string tokens are not accepted. If you see examples with query tokens, treat them as
outdated.
Auth precedence (important)
- When embed=1 is used, the server accepts only embed tokens via header (mtok_...).
- If an Authorization: Bearer ... header is present under embed mode and it is not an embed token, the request is rejected (no fallback).
- Outside embed mode, Authorization has precedence for owner/API calls (mk_/ak_).
This strict precedence prevents accidental privilege escalation and avoids ambiguous auth paths.
Public runs
Public runs are optional and controlled by the tool owner. If enabled, anyone can run without a token. Public runs still consume the owner quotas.
LLM tools remain BYOK-gated even if public runs are enabled.
BYOK for LLM tools
- If a tool requires AI and the owner has no active AI key configured, runs fail with byok_required.
- byok_required is side-effect-free: it should not create run logs or consume quotas.
Rate limits & quotas
- Short-window rate limit applies per tool to reduce abuse.
- Daily quota window is based on UTC calendar day (resets at 00:00 UTC).
- When an LLM provider fails, the run is logged as failure and quota is refunded (no net usage increase).
Troubleshooting
- invalid_token: token revoked/invalid, or sent via wrong channel.
- missing_auth: embed endpoints called without token header.
- byok_required: AI tool needs owner BYOK key.
- If embed is blocked: check CSP frame-ancestors and iframe origin rules.