Quick Start

Get Marli Running in One Afternoon

TL;DR — the integration is a single iframe, authenticated by short-lived JWTs your backend signs with a private key. Three steps, roughly one afternoon of engineering.

1Receive Your Welcome Packet

LyticaLabs gives you:

  • Partner slug — kebab-case identifier (e.g. iscream)
  • Embed URL https://alpha.lyticalabs.ai/embed/cp/{slug}
  • API key — 64-char hex, shown once. Save it immediately in your secrets manager.
  • Approved domains — exact origins where your iframe will load.

You give LyticaLabs:

  • Company name + technical contact email
  • Production & staging domain(s) (e.g. https://app.example.com)
  • A public JWK (RS256 or ES256) — generate with jose or openssl. Keep the private half in your secrets manager.

2Wire Up Your Backend (Mint Endpoint)

Expose one endpoint that the SDK calls from the browser. Use our createMarliMintHandler helper for ~10 lines of code:

app/api/marli-token/route.ts
import { createMarliMintHandler } from '@lyticalabs/marli-sdk/server';

export const POST = createMarliMintHandler({
  partnerSlug: process.env.CP_PARTNER_SLUG!,
  privateJwkJson: process.env.CP_PARTNER_PRIVATE_JWK!,
  kid: process.env.CP_PARTNER_KID!,
  alg: 'RS256',
  resolveContext: async (request) => {
    const session = await getYourSession(request);
    if (!session) return null;
    return {
      partnerOrgId: session.activeOrg.id,
      userId: session.user.id,
    };
  },
});

Set three env vars: CP_PARTNER_SLUG, CP_PARTNER_KID, and CP_PARTNER_PRIVATE_JWK.

3Mount the Widget in Your Frontend

components/MarliPanel.tsx
'use client';
import { MarliWidget } from '@lyticalabs/marli-sdk/react';

export function MarliPanel() {
  return (
    <MarliWidget
      baseUrl="https://alpha.lyticalabs.ai"
      partnerSlug="your-slug"
      onMintJwt={async () => {
        const res = await fetch('/api/marli-token', { method: 'POST' });
        if (!res.ok) throw new Error('mint failed');
        const { jwt } = await res.json();
        return jwt;
      }}
      iframeAttrs={{
        title: 'Marli',
        allow: 'clipboard-read; clipboard-write',
        style: { width: '100%', height: '600px', border: 'none' },
      }}
    />
  );
}

4Provision a Customer Org

Before any user from a customer can chat, register that customer with a single server-to-server call:

curl -X POST https://alpha.lyticalabs.ai/api/v1/cp/orgs \
  -H "Authorization: Bearer ${CP_PARTNER_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"partnerExternalOrgId": "publisher_12345", "name": "Acme Media"}'

Idempotent — calling twice with the same ID returns 409 (org already exists).

5Test It

Open your product, send "ping" to Marli. You should see a response stream in within ~2 seconds. Then run the full pre-launch production checklist before going live.

JWT Cheat Sheet

Your JWT has exactly five payload claims: iss (your slug), iat, exp (5–15 min TTL), partnerOrgId, and userId.

Important: partnerOrgId and userId must not contain the colon character (:). If your IDs use colons, URL-encode or replace them consistently.