Adapter

An OnboardAdapter is how BYO mode talks to your store. Helper: createLocalAdapter (demo). Hosted mode is used internally when you pass flowId + publishableKey and omit adapter.

Resolve order

  1. If adapter is passed, use it for progress. Published config is still fetched when flowId and publishableKey are set (Path C).
  2. Else if flowId and publishableKey are set, use Hosted progress.
  3. Else fall back to createLocalAdapter.

Required methods

getProgress(input)
completeStep(input)
skipStep(input)
snooze(input)
dismiss(input)
resume(input)
resetPersonal(input)
reportOutcome(input)

Optional methods

unskipStep(input) deletes a skipped progress row so that optional step is incomplete again. The widget shows Undo only when this method exists. It uses the skip permission and does not uncomplete a complete row. Omit it and current BYO adapters keep compiling.

Pinned input

Mutations include revision, personalGeneration, workspaceGeneration, and requestId. Return AdapterResult with changed and acceptedTransitions. See progress.

reportOutcome

Called after evaluation with applicable required keys, item states, and requirementsSatisfied. Attach integration-reported summary metadata on your backend. See summaries.

BYO example

<Onboard
  flowId="flw_..."
  publishableKey={process.env.NEXT_PUBLIC_BSW_ONBOARD_PUBLISHABLE_KEY}
  user={currentUser}
  workspaceKey={workspace.id}
  onEvent={(event, payload) => { /* analytics */ }}
  adapter={{
    getProgress: async (input) =>
      fetch(`/api/onboard/progress?${new URLSearchParams(input as Record<string, string>)}`).then((r) => r.json()),
    completeStep: async (input) =>
      fetch("/api/onboard/progress", { method: "POST", body: JSON.stringify({ action: "complete", ...input }) }).then((r) => r.json()),
    skipStep: async (input) =>
      fetch("/api/onboard/progress", { method: "POST", body: JSON.stringify({ action: "skip", ...input }) }).then((r) => r.json()),
    unskipStep: async (input) =>
      fetch("/api/onboard/progress", { method: "POST", body: JSON.stringify({ action: "unskip", ...input }) }).then((r) => r.json()),
    snooze: async (input) =>
      fetch("/api/onboard/progress", { method: "POST", body: JSON.stringify({ action: "snooze", ...input }) }).then((r) => r.json()),
    dismiss: async (input) =>
      fetch("/api/onboard/progress", { method: "POST", body: JSON.stringify({ action: "dismiss", ...input }) }).then((r) => r.json()),
    resume: async (input) =>
      fetch("/api/onboard/progress", { method: "POST", body: JSON.stringify({ action: "resume", ...input }) }).then((r) => r.json()),
    resetPersonal: async (input) =>
      fetch("/api/onboard/progress", { method: "POST", body: JSON.stringify({ action: "resetPersonal", ...input }) }).then((r) => r.json()),
    reportOutcome: async (input) =>
      fetch("/api/onboard/outcomes", { method: "POST", body: JSON.stringify(input) }).then((r) => r.json()),
  }}
/>

Related: Modes · Identity · AGENTS.md