Sessions
Short-lived session tokens for browser-side calls. Your server mints a token scoped to a single end-user, the browser uses it to talk to PromptFloe directly without exposing your API key.
#Why sessions?
Live API keys (pf_live_...) must never reach the browser — anyone who reads them can run up your bill. Sessions solve this with a two-step flow:
- Your server proxies an "issue session" call using your API key.
- The server returns a short-lived session token to the browser.
- The browser uses the session token directly with the SDK.
Sessions are scoped to a single endUserId, expire in 60 minutes, and inherit a subset of your key's scopes.
#Server: mint a session
Use your live API key on the server
// app/api/promptfloe/session/route.ts
import { PromptFloe } from '@promptfloe/sdk';
const server = new PromptFloe({ apiKey: process.env.PROMPTFLOE_API_KEY });
export async function POST(req: Request) {
const { userId } = await getCurrentUser(req);
const session = await server.sessions.issue({
endUserId: userId,
scopes: ['build', 'read'],
ttlMinutes: 30,
});
return Response.json({ token: session.token, expiresAt: session.expiresAt });
}Browser: use the token
import { PromptFloe } from '@promptfloe/sdk';
const { token } = await fetch('/api/promptfloe/session', { method: 'POST' })
.then(r => r.json());
const client = new PromptFloe({ apiKey: token }); // session token
const stream = client.apps.generateStream({
prompt: 'A landing page for ' + product,
});
for await (const ev of stream) {
if (ev.type === 'file') setProgress(ev.path);
if (ev.type === 'ready') setPreview(ev.previewUrl);
}#Session scopes
A session can have at most the scopes of the parent API key. We recommend issuing the minimum subset:
read+buildfor "user generates apps in browser".readonly for "user views their existing apps".read+build+deployfor the full lifecycle.
#Quota attribution
Calls made with a session token still count against your workspace's daily quota — but the run is tagged with the endUserId so you can break down usage per end-user in the dashboard.
#Rotation
Sessions expire automatically; the SDK refreshes them by hitting your /api/.../session endpoint on 401s if you provide a refreshFn:
const client = new PromptFloe({
apiKey: token,
refreshFn: async () => {
const { token } = await fetch('/api/promptfloe/session', { method: 'POST' })
.then(r => r.json());
return token;
},
});