Server sessions (API keys)

Preferred session minting when you have a backend — API key stays on the server.

What this is

A server session is minted by your backend with a developer API key via POST /v1/sessions. The browser never sees the key. This is the preferred mint path whenever you can run server-side code.

When to use

  • You have a backend (SvelteKit, Next.js, or any server)
  • Per-user session context or app-owned authorization
  • Tighter control over who can start sessions and with which overrides
  • Custom integration settings (return URL, close button)

For client-only pages with no server, use browser embeds with allowed origins instead.

Flow

Your backend  →  POST /v1/sessions  →  Session Manifest
                                              ↓
Your frontend  ←  manifest or sessionEndpoint  ←  SDK

1. Mint on your server

Call the Liforma API with your developer API key. Never expose the key to the browser.

curl -X POST https://api.liforma.ai/v1/sessions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"experienceId": "exp_01DEMO1SPANISHCAFE"}'

2. Pass to the client

Recommended: use a same-origin sessionEndpoint so credentials never touch SSR HTML.

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"
    />
  );
}

3. Implement sessionEndpoint

SvelteKit

+server.ts
// 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 Router

route.ts
// app/api/liforma-session/route.ts (Next.js App Router)
import { createLiformaSessionRouteHandler } from '@liforma/client/next';

export const POST = createLiformaSessionRouteHandler();

Contract: POST with experienceId (and optional launch fields) → Session Manifest with Cache-Control: no-store, private.

SSR warning

Do not pass credential-bearing manifests through server load functions. The sessionToken in the manifest would be embedded in HTML. Use sessionEndpoint or client-side minting instead.

API key

Obtain a developer API key from the developer portal (app.liforma.ai). Store it in server environment variables only.