Pollaris Docs
Embed a form

Headless step-by-step SDK

Build a custom UI while Pollaris owns sessions, branching, and server validation.

Use the headless SDK when you want complete control over markup and CSS. It exposes state and methods but renders nothing. It supports both linear and branching published forms.

Create a client

import { createPollarisClient } from 'https://cdn.example.com/embed/v1/index.js';

const client = createPollarisClient({
  form: 'FORM_SLUG',
  apiBase: 'https://api.example.com',
  redirect: 'manual'
});

const unsubscribe = client.subscribe((state) => {
  renderYourInterface(state);
});

await client.start({
  utm_source: 'website',
  utm_medium: 'embed',
  language: navigator.language,
  screen_w: window.innerWidth
});

Render and answer

When state.status === 'ready', render state.question using your own components. Keep the value locally or pass it to client.setValue(value), then submit:

try {
  await client.answer(value);
} catch (error) {
  // The same error is also available as client.getState().error.
  showError(error.message);
}

Pollaris returns only the current question. After an answer, the server validates it and resolves the next graph edge. Do not attempt to calculate branching in the browser.

State

client.getState() and subscription callbacks return:

FieldMeaning
statusidle, loading, ready, submitting, complete, closed, or error
formPublic title, description, branding, and close/duplicate settings
responseIdCurrent public response session ID
questionCurrent question, or null
valueValue most recently passed to setValue
progressApproximate value from 0 to 1
completionCompletion message, redirect, and optional quiz score
errorA PollarisError, or null

Supported question types are yesno, single, multi, rating, nps, text, star, contact, date, and time. Treat question.config as type-specific configuration.

Lifecycle events

const offReady = client.on('ready', ({ form }) => {});
const offQuestion = client.on('question', ({ key, progress }) => {});
const offComplete = client.on('complete', ({ responseId }) => {});
const offError = client.on('error', ({ code, retryable }) => {});

These events intentionally omit answer values. Avoid copying answer data into analytics, console logs, or error-reporting metadata.

Errors and retry

Stable public codes include:

FORM_NOT_FOUND        FORM_CLOSED          ORIGIN_NOT_ALLOWED
VALIDATION_FAILED     RATE_LIMITED         SESSION_EXPIRED
NETWORK_ERROR         SERVER_ERROR         PROTOCOL_ERROR

Check error.retryable. retry() is available only when the client has a retryable operation. A validation error should be shown beside the relevant control instead of retried unchanged.

Resume, reset, and cleanup

The default client storage remembers an in-progress response and duplicate-response status. This is a user-experience feature, not an anti-abuse security boundary.

await client.reset();  // clear this form's saved local state
unsubscribe();
client.destroy();      // abort requests and remove listeners

Use storage: null when persistence is undesirable, or provide a StorageLike object with getItem, setItem, and removeItem.

On this page