Skip to content

Embed sessions

An embed session is the credential the widget runs on. It is minted server-side with your partner key, lives for a limited time, and can only touch one workspace.

POST /v1/embed-sessions

Field Type Default Notes
workspace string required The workspace the widget will act on
surfaces string[] all four Which of composer, calendar, inbox, accounts the session may open. all shows only the allowed ones
expires_in number 3600 Seconds until the token stops working. Maximum 86400 (24 hours)
theme object dark, purple { mode?: "dark" or "light", accent?: "#6d5efc", radius?: "12px" }. See Theming
locale string en en or nl
Terminal window
curl -X POST https://social.missless.tel/v1/embed-sessions \
-H "Authorization: Bearer $MISSLESS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workspace": "ws7k2m9p4q1r8t3",
"surfaces": ["composer", "calendar", "inbox", "accounts"],
"expires_in": 3600,
"theme": { "mode": "dark", "accent": "#6d5efc", "radius": "12px" },
"locale": "en"
}'

Response 201:

{
"token": "mle_5c1e9a7b3d2f8e4a6c0b9d1f7e3a5c2b",
"expires_at": "2026-08-25T10:30:00Z",
"urls": {
"all": "https://social.missless.tel/embed/all?session=mle_5c1e9a7b3d2f8e4a6c0b9d1f7e3a5c2b",
"composer": "https://social.missless.tel/embed/composer?session=mle_5c1e9a7b3d2f8e4a6c0b9d1f7e3a5c2b",
"calendar": "https://social.missless.tel/embed/calendar?session=mle_5c1e9a7b3d2f8e4a6c0b9d1f7e3a5c2b",
"inbox": "https://social.missless.tel/embed/inbox?session=mle_5c1e9a7b3d2f8e4a6c0b9d1f7e3a5c2b",
"accounts": "https://social.missless.tel/embed/accounts?session=mle_5c1e9a7b3d2f8e4a6c0b9d1f7e3a5c2b"
}
}

urls are ready to use as iframe src. token is what the loader takes in data-session.

The call needs your partner key, which must never reach a browser. The pattern is a small authenticated route in your backend:

app/api/social/embed-session/route.ts
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
const user = await requireUser(req); // your auth
const workspace = await findWorkspaceFor(user.id); // by external_id, see the Next.js guide
if (!workspace) return NextResponse.json({ error: 'no workspace' }, { status: 404 });
const res = await fetch('https://social.missless.tel/v1/embed-sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MISSLESS_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
workspace: workspace.id,
surfaces: ['composer', 'calendar', 'inbox', 'accounts'],
theme: { mode: user.prefersDark ? 'dark' : 'light', accent: '#6d5efc', radius: '12px' },
locale: user.locale === 'nl' ? 'nl' : 'en',
}),
});
const session = await res.json();
return NextResponse.json({ token: session.token, urls: session.urls, expires_at: session.expires_at });
}

Only mint a session for a workspace the signed-in user owns. The session is that user’s power over the workspace, so the check is yours to make.

Sessions expire after expires_in seconds and cannot be extended. When the token expires the widget shows a “session expired, reload” notice and stops making calls. Two ways to handle it:

  • Mint per page load with the default hour. Most sessions end long before that.
  • For long-lived screens, mint a new session and swap the iframe src (or re-render the loader element) shortly before expires_at.

Keep expires_in short. A leaked token is worth at most that many seconds of access to one workspace.

surfaces is a permission, not a layout hint. A session minted with ["inbox"] cannot open the composer even if someone edits the iframe URL. Mint the narrowest session the screen needs: an inbox page gets ["inbox"], a settings page gets ["accounts"].