SavartEFG Alignment Studio

One scoring science · Four integration contracts

Meet clients wherever their journey begins.

Every path resolves to frozen Savart scoring science, complete immutable version provenance and a tenant-scoped result. Secrets stay on the server.

01
Fastest launch

Hosted by Savart

Send a client from your CRM or adviser workflow into the branded, accessible assessment.

// Server only: select the frozen arm, then upsert and enroll.
const programId = mode === "full"
  ? process.env.SYNAPSE_SAVART_FULL_PROGRAM_ID!
  : process.env.SYNAPSE_SAVART_ADAPTIVE_PROGRAM_ID!;
const subject = await synapse.subjects.upsert(orgId, appId, envId,
  { externalId: crmClientId }, { idempotencyKey: subjectKey });
await synapse.programs.enroll(orgId, appId, envId,
  { subjectId: subject.subject.id, programId });
const session = await synapse.participantSessions.create(
  orgId, appId, envId,
  { subjectId: subject.subject.id, programId },
  { idempotencyKey: sessionKey },
);

cookies().set("savart_participant", session.participant_token, {
  httpOnly: true, secure: true, sameSite: "strict", path: "/",
});
redirect("https://efg.savart.in/assessment");
02
Native journey

Embedded experience

Use the compile-tested React artifact with an authenticated BFF-issued, short-lived participant token. No app or environment key enters browser code.

"use client";
import { useEffect, useState } from "react";
import {
  EmbeddedAssessmentExample,
  createSavartEmbeddedController,
} from "./embedded-assessment-example";

export function HostAssessment() {
  const [controller, setController] = useState(null);
  useEffect(() => {
    // An authenticated same-origin BFF returns only a short-lived spt_ token
    // and opaque session ID. App/environment credentials remain server-side.
    fetch("/api/synapse-participant-token", {
      method: "POST", credentials: "same-origin",
    }).then((response) => response.json()).then((launch) => {
      setController(createSavartEmbeddedController({
        baseUrl: "https://api.synapse.example",
        participantToken: launch.participantToken,
        opaqueSessionId: launch.sessionId, // never use the token as a queue key
      }));
    });
  }, []);
  return controller
    ? <EmbeddedAssessmentExample controller={controller} />
    : <p>Starting secure assessment…</p>;
}
03
Full control

Server API + SDK

Sync managed users and enroll them in the correct published pilot program from your backend.

import { createSynapseClient } from "@apart-ai/synapse";

const synapse = createSynapseClient({
  baseUrl: process.env.SYNAPSE_API_URL!,
  apiKey: process.env.SYNAPSE_ENVIRONMENT_KEY!, // server only
});

const synced = await synapse.subjects.upsert(
  process.env.SYNAPSE_ORGANIZATION_ID!,
  process.env.SYNAPSE_APP_ID!,
  process.env.SYNAPSE_ENVIRONMENT_ID!,
  { externalId: crmClientId },
  { idempotencyKey: requestId },
);
04
Reliable events

Signed webhooks

Verify the raw payload before parsing, then deduplicate every at-least-once delivery by event ID.

import { verifyWebhookSignature } from "@apart-ai/synapse/webhooks";

export async function POST(request: Request) {
  const payload = await request.text(); // verify before JSON parsing
  await verifyWebhookSignature(
    payload,
    request.headers.get("x-synapse-signature") ?? "",
    [process.env.SYNAPSE_WEBHOOK_SECRET!,
     process.env.SYNAPSE_WEBHOOK_PREVIOUS_SECRET!],
  );
  const event = JSON.parse(payload);
  await processEventOnce(event.id, event); // deliveries are at least once
  return new Response(null, { status: 204 });
}

Trust boundary

One simple rule: app and environment credentials never enter participant JavaScript.

  1. 1

    Your server identifies the Savart client
    Raw external IDs stay in request bodies and out of URLs and product analytics.

  2. 2

    Synapse starts a tenant-scoped assessment
    The server SDK authenticates using the matching test or live environment key.

  3. 3

    Synapse issues a short-lived participant credential
    Savart's hosted flow keeps it in an HttpOnly cookie; an embedded host may explicitly pass only this audience-restricted credential to the public browser SDK.