Use a server session
Preferred mint path — API key on your server, sessionEndpoint in the client.
Overview
Your server mints a Session Manifest with an API key. Your client embeds via sessionEndpoint. Prefer this whenever you have a backend.
Steps
- Store
LIFORMA_API_KEY(and optionallyLIFORMA_API_URL) in server env - Create a same-origin API route that calls
POST /v1/sessions - Pass
sessionEndpointto<Experience> - SDK POSTs to your route, receives manifest, starts session
Client
Svelte
<Experience
experienceId="exp_01DEMO1SPANISHCAFE"
sessionEndpoint="/api/liforma-session"
/> React
PrivateLesson.tsx
import { Experience } from '@liforma/client/react';
export function PrivateLesson() {
return (
<Experience
experienceId="exp_01DEMO1SPANISHCAFE"
sessionEndpoint="/api/liforma-session"
/>
);
} Server
SvelteKit
// src/routes/api/liforma-session/+server.ts (SvelteKit)
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const { experienceId, language, mode, speechInputMode, speechOnly } = await request.json();
const res = await fetch('https://api.liforma.ai/v1/sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LIFORMA_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
experienceId,
...(language ? { language } : {}),
...(mode ? { mode } : {}),
...(speechInputMode ? { speechInputMode } : {}),
...(speechOnly === true ? { speechOnly: true } : {})
})
});
if (!res.ok) {
return json({ message: 'Failed to mint session' }, { status: res.status });
}
return json(await res.json(), {
headers: { 'Cache-Control': 'no-store, private' }
});
} Next.js
// app/api/liforma-session/route.ts (Next.js App Router)
import { createLiformaSessionRouteHandler } from '@liforma/client/next';
export const POST = createLiformaSessionRouteHandler(); Security
- Never expose your API key to the browser
- Never pass manifests with
sessionTokenthrough SSR load data - Derive signed-in identity on the server; never trust a browser-supplied user id alone