Iframe Quickstart
This is the fastest way to try the Developer API without building a backend first.
In iframe mode, your app talks to the Chastify parent via postMessage, and Chastify calls server APIs on your behalf.
What Chastify Passes To Your Iframe
On open, Chastify puts a JSON payload in location.hash.
Important fields:
bridge.nonce: request signing value for bridge messages.bridge.parentOrigin: required target origin forpostMessage.sessionId: stable extension session id for this lock.lockId: active lock id.ui: theme values from parent page.- Optional:
homeActionSlug,homeAction,intent,regularActionsSummary,mainToken.
Quick Start With The Bridge Library (Recommended)
Use helpers from apps/extension/src/lib/ChastifyBridge.ts:
parseHashPayload()ChastifyBridgeClientstartAutoResizeToParent(...)themeVars(...)
Minimal React bootstrap
import { useEffect, useMemo, useState } from "react";
import {
parseHashPayload,
ChastifyBridgeClient,
startAutoResizeToParent,
themeVars,
} from "../lib/ChastifyBridge";
const payload = parseHashPayload();
if (!payload?.bridge?.nonce || !payload?.bridge?.parentOrigin) {
throw new Error("Missing bridge payload in iframe hash");
}
export function App() {
const [client, setClient] = useState<ChastifyBridgeClient | null>(null);
const [session, setSession] = useState<any>(null);
const [stateDoc, setStateDoc] = useState<any>(null);
const vars = useMemo(() => themeVars(payload.ui ?? null), []);
useEffect(() => {
const c = new ChastifyBridgeClient({
nonce: payload.bridge!.nonce,
targetOrigin: payload.bridge!.parentOrigin,
});
setClient(c);
return () => c.destroy();
}, []);
useEffect(() => {
if (!client) return;
(async () => {
const s = await client.request("session.get", {});
setSession(s);
const st = await client.request("state.get", {});
setStateDoc(st);
})().catch(console.error);
}, [client]);
useEffect(() => {
return startAutoResizeToParent({
nonce: payload.bridge!.nonce,
targetOrigin: payload.bridge!.parentOrigin,
extraPx: 12,
});
}, []);
return (
<div style={{ background: vars.pageBg, color: vars.text, minHeight: "100%" }}>
<h2>Extension Quickstart</h2>
<pre>{JSON.stringify(session, null, 2)}</pre>
<pre>{JSON.stringify(stateDoc, null, 2)}</pre>
</div>
);
}
Raw postMessage Example (No Helper Class)
If you do not use ChastifyBridgeClient, send the full request envelope manually:
const req = {
type: "chastify:ext:req",
v: 1,
id: crypto.randomUUID(),
nonce,
action: "session.get",
payload: {},
};
window.parent.postMessage(req, parentOrigin);
Listen for matching responses:
window.addEventListener("message", (event) => {
if (event.origin !== parentOrigin) return;
const msg = event.data;
if (!msg || msg.type !== "chastify:ext:resp" || msg.v !== 1) return;
if (msg.id !== req.id) return;
if (msg.ok) console.log("Bridge success:", msg.data);
else console.error("Bridge error:", msg.error);
});
Test Flow You Should Run First
session.getstate.getmetadata.getregularActions.get
This confirms bridge auth, safe reads, and request/response handling. State writes must be performed by your backend with Developer API credentials.
File Uploads From Iframe
If your extension needs images or generated media, use the bridge file helpers. Chastify uploads to managed R2 storage, returns a stable file id, and gives you a short-lived signed URL for rendering.
const capabilities = await client.filesCapabilities();
if (capabilities.enabled) {
const uploaded = await client.filesUpload({
file,
filename: file.name,
purpose: "puzzle-image",
});
// Send uploaded.file.id to your backend if it should be stored in session state.
const refreshed = await client.filesGet(uploaded.file.id);
image.src = refreshed.file.signedUrl;
}
Store uploaded.file.id, not the signed URL. Signed URLs expire and should be refreshed with files.get / client.filesGet(...) when the iframe loads again.
Theme Support (ui + themeVars)
Chastify passes a ui object in the iframe hash.
themeVars(ui) converts it into usable design tokens:
pageBgtextpanelbordermutedinputBg
Use these values as your extension’s base styling so your iframe matches host theme.
Live theme updates
The parent can send chastify:ext:ui events while your iframe is open.
If you want live theme sync, listen for that event and update local theme state.
For detailed light/dark theme handling, contrast examples, and Tailwind patterns, see Iframe Theming.
Auto Resize Support
Use startAutoResizeToParent(...) so the parent can resize the iframe to your content.
Why this matters:
- Prevents scroll traps inside the iframe.
- Keeps setup modals and extension pages sized correctly.
- Works well for dynamic sections that expand/collapse.
Common mistakes
- Wrong
targetOrigin(must matchbridge.parentOrigin). - Missing or stale
nonce. - Not calling
destroy()on unmount forChastifyBridgeClient. - Sending unsupported actions without checking
session.getcapabilities first.
Reference Files
apps/extension/src/lib/ChastifyBridge.tsapps/extension/src/pages/MainPage.tsxapps/extension/src/pages/SetupPage.tsx