DocsRespondents
Respondents
Because the form renders inside your product, it can behave like your product: recognize the signed-in user, remember progress, and let people return to edit. Needs @usefillo/* 0.7.0 or newer.
Identify who's filling the form
Pass your signed-in user with the respondent prop. Every response that person submits arrives with their id, email, and name attached — in the responses grid, the response drawer, webhooks, Zapier, and CSV export — and a person view filters everything one respondent ever submitted. respondent.id is your own user id; it’s the identity key within your workspace.
"use client";
import { FilloForm } from "@usefillo/react";
// Pass your signed-in user. Every response this person submits now arrives
// with their id, email, and name attached — in the responses grid, webhooks,
// Zapier, and CSV export. Safe to pass late (after your session loads).
export function NpsForm({ user }: { user: { id: string; email: string; name: string } }) {
return (
<FilloForm
formId="nps"
respondent={{ id: user.id, email: user.email, name: user.name }}
/>
);
}The same option works on <FilloProvider>, the framework-free renderForm() (with setRespondent() to bind late), and createFormController().
import { createFormController } from "@usefillo/core";
// The respondent option works everywhere the engine does — the DOM renderer
// (renderForm), the headless controller, and <FilloProvider> in React.
const form = renderForm("#form", {
formId: "nps",
respondent: { id: user.id, email: user.email, name: user.name },
});
// Late-bind it once your session resolves:
form.setRespondent({ id: user.id, email: user.email, name: user.name });By default identity is an unverified claim from the page — fine for enriching your own dashboards. Turn on verification when you rely on it.
Verify identity
In Fillo → Settings → Developers → Identity verification, generate a workspace secret. From then on, Fillo records a respondent only when the submission carries a valid HMAC-SHA256(userId, secret) computed on yourserver — so a script on the embedding page can’t claim to be another user. Verified responses are marked as such in the grid, drawer, and payloads.
// On YOUR server — never in the browser (the secret must not ship to clients).
// Copy the secret from Fillo → Settings → Developers → Identity verification.
import { createHmac } from "node:crypto";
export function respondentHash(userId: string): string {
return createHmac("sha256", process.env.FILLO_IDENTITY_SECRET!)
.update(userId)
.digest("hex");
}Then pass it with the identity:
// Pass the server-computed hash alongside the identity. Once your workspace
// has a secret, Fillo records identity ONLY when the hash validates, so a
// script on the page can't impersonate or enrich another user's profile.
<FilloForm
formId="nps"
respondent={{ id: user.id, email: user.email, hash }}
/>Never compute the hash in the browser — that would expose the secret. If verification is on and no valid hash arrives, the response is still recorded, just anonymous.
Save and resume
Turn on Save and resumein a form’s Respondents settings. The SDK then autosaves in-progress answers as the visitor types and restores them when they return — after a reload, a closed tab, or (for a verified respondent) on another device. A “picked up where you left off” notice appears with a Start over action. Unsubmitted drafts are deleted on submit, on start over, or automatically 7 days after the last change. It’s a per-form setting the renderer honors — no code changes. Headless UIs can read resumedDraft and call resetDraft() / flushDraft().
One response per person
Two per-form settings, both keyed on the identified respondent:
- Reject repeats (
submissionLimit: "once_per_person") — one response per person; a repeat submit returns the standing response. Anonymous submits are rejected, so the form must be embedded withidentify(). - Update their response (
responseMode: "upsert") — the same person edits their response in place instead of creating duplicates. It re-anchors to the current form version, emits aresponse.updatedwebhook event, and — for a verified respondent — prefills their previous answers for editing. This turns a one-shot form into a living record: a profile, a preference center, a recurring check-in.
Both are unenforceable without identity, so turn on verification before you rely on them for anything that matters.
Erase a person (GDPR)
From the person view (filter the responses grid to one respondent), Forget person removes their profile, drafts, and identity across the workspace while leaving the answers anonymous; Delete person + responses also deletes their responses and the uploaded files in your connected storage. Both act workspace-wide, because identity is workspace-level.