Forms
A form is a schema — it defines what information gets collected. Each submission creates a Record that stores the field values, timestamps, and any attached files.
Forms vs. Records
Think of a form as a database table definition: it describes the shape of your data. Records are the rows — individual filled-in submissions of that form.
One form can have thousands of Records. Each Record stores its field values, a creation timestamp, an optional submitter email, and a full audit trail of changes.
Creating a form
In the sidebar, click Forms then New Form. You'll provide:
- Name — displayed on the public form header and in the Snapi UI (e.g., "Bug Report", "Intake Form").
- Description — optional subtitle shown under the form title.
- Success message — what the submitter sees after a successful submission.
- Visibility — Public (anyone with the URL can submit) or Private (requires a Snapi login).
The form's Kanban tab can also set a Default Board (and optionally a Default Column) — new records then land on that board automatically as cards. If no column is chosen, the board's first column is used.
Field types
Add fields via the Add Field button on the form editor. Each field has a type, label, placeholder, and optional flags: required, show on board card (the value appears on the record's Kanban card), and internal (hidden from the public form — visible to your team only).
| Type | What it renders | Stored as |
|---|---|---|
| text | Single-line text input | String |
| textarea | Multi-line text area | String |
| Email input with format validation | String | |
| number | Numeric input | Number |
| tel | Phone number input | String |
| select | Dropdown with your defined options | String (chosen option) |
| radio | Radio button group with your defined options | String (chosen option) |
| checkbox | Single true/false toggle | Boolean |
| date | Date picker | ISO date string |
| sequence | Auto-assigned document number (e.g. PO-0042) drawn from an org-wide sequence. Read-only unless "allow manual editing" is enabled — see Auto-numbering | String (assigned server-side on create) |
| file | File upload button | File reference (stored in Snapi) |
| hidden | Hidden input — not shown to the submitter | String |
| object | Group of nested sub-fields (text, number, dropdown, radio, date, checkbox, reference …) | Object |
| array | Repeating rows of sub-fields with add/remove and optional drag-reorder — rows can include reference pickers (e.g. PO lines, each choosing a Part) | Array |
| table | Read-only data grid fed by the onload webhook, with optional row actions: navigate to a form carrying {{row.x}} params (list → detail) or call a webhook with the row | Not stored (display) |
| reference | Searchable dropdown linking one record from another form (with inline "create new"). Works at the top level, inside object fields, and in array rows. Always internal — never shown on the public form | Record ID + title label |
| display | Read-only text shown on the form | Not stored |
| title | Large page heading (layout only) — the natural top element of an app hub page | Not stored |
| subtitle | Section heading (layout only) | Not stored |
| divider | Horizontal divider (layout only) | Not stored |
| linebreak | Line break (layout only) | Not stored |
| button | Action button: call a webhook URL (server-side proxied), navigate to another form (with params), or go back — see Navigation | Not stored |
| link | Text link: navigate to another form (with params), open an external URL (interpolated), or go back | Not stored |
Tip: Field order matters — drag fields to reorder them (sub-fields of object/array fields reorder the same way inside the field editor). The order in the editor is the order in the public form.
Form-to-form navigation (multi-form apps)
Embedded forms can link to each other, turning a set of forms into a lightweight app on any website — all inside one embed container, SPA-style, with browser back/forward support and no page reloads.
| Mechanism | What it does |
|---|---|
| Navigate button / link | A button or link field with the Navigate to another form action loads the target form in place. Optional params carry data across: values support {{data.fieldName}} (current inputs) and {{params.x}} |
| Table row actions | Per-row buttons on a data table: navigate to a form with {{row.x}} params from the clicked row (list → detail), or call a webhook with the row payload |
| Back button | A button field with the Go back action returns to the previously shown form (browser history) |
| Post-submit navigation | Per form (Settings → "After submit, navigate to"): after a successful submit the embed loads the target form. Params support {{data.fieldName}} and {{record.id}} (the new record) |
| Receiving params | The target form reads params as {{params.x}} — in display fields, or as a field's default value to prefill it. URL query params (?x=y) work the same way |
| SDK | Snapi.navigateToForm(slug, params) is also callable directly from your page's JavaScript |
Passing data around: {{data.x}}, {{params.x}}, {{row.x}}, {{record.id}}
Several places in Snapi accept small template placeholders that are replaced with live values. There are four sources, and where you are determines which ones exist:
| Placeholder | Where the value comes from | Available in |
|---|---|---|
{{data.fieldName}} | The current form: what the user has typed so far, merged over any onload-webhook data. Use dot-paths for nested values ({{data.customer.email}}) | Display fields, navigation params, external link URLs, button payloads |
{{params.x}} | How the visitor arrived: URL query parameters (?x=y) merged with params passed by whichever navigation brought them here. Navigation params win | Display fields, field default values (prefill!), hidden fields, nav params |
{{row.x}} | The clicked table row — only meaningful inside a table's row-action params | Row-action params |
{{record.id}} | The id of the record that was just created | Post-submit navigation params only |
The pattern that ties an app together: form A sends a value as a nav param → form B receives it as {{params.x}} → B surfaces it via a display field, prefills an input with it (set the field's default value to {{params.x}}), or carries it invisibly in a hidden field — which submits with the record, so your webhooks see where the user came from.
Worked example — order follow-up: the Orders form's post-submit navigation targets "Order Feedback" with params {"order": "{{record.id}}", "email": "{{data.email}}"}. Order Feedback has a hidden field order_ref with default {{params.order}} and an email field with default {{params.email}} (prefilled, editable). When feedback is submitted, the record — and your n8n webhook — carries order_ref linking it back to the order.
Unknown placeholders resolve to an empty string — they never error. Values are substituted at render/click time with whatever is currently in the form.
Apps (starter forms)
An App is a form used as a starting point — a hub page with typically no inputs, just display text, links, and buttons that tie your other forms and boards into one experience: a CRM hub, an order portal, a helpdesk entry page. Apps live under the Apps menu (opened as full pages in Snapi) and embed on any website like any public form — the Submit button is omitted automatically when a form has no input fields. Full guide: Apps →
- Buttons and links can navigate to forms or boards (board targets work inside Snapi; they're hidden on public embeds).
- Boards can carry navigation too: admins add link/button items to a board's header (same editor, reduced to those two types), so users can move between boards and forms freely.
- Hidden fields with a default like
{{params.src}}carry state across navigation — the value rides along and is submitted with the record, so your webhooks know where the user came from. - If a hub does collect inputs, saving honors the form's post-submit navigation ("navigate on persist").
Portal users (sign-in-required forms)
Snapi distinguishes two kinds of people. Members are your team on the inside — they sign in to the Snapi workspace to build forms, manage boards, and work the data, each with a role (viewer / editor / admin / owner). Portal users are your application's end users — the clients, vendors, and field staff who use what you build: they sign in on your embedded forms, portals, and apps, and never see the Snapi workspace at all.
Turn on Require sign-in for a public form and only authenticated users can view or submit it: members always can; portal users can when assigned (per form, or all gated forms). Portal users are managed in Settings → Portal Users (a Team plan feature), can be suspended instantly, and share the plan's seat pool with members. The embedded form shows a sign-in screen automatically; sessions last 7 days.
Embed themes
Manage reusable color presets in Settings → Themes (primary, text, background, border, danger, success). Set one as the org default and/or pick a theme per form in the form builder — ideal for matching each client's website when you embed forms on their domains.
Webhook runs & proxying
All webhook calls — pre/post submit, onload data fetches, and button/row actions — are made server-side: no CORS requirements on your webhook endpoints, and headers/secrets never reach the browser. Every call is logged with status, duration, and error detail; see the Recent Webhook Runs panel on the form builder's Webhooks tab (last 100 calls per form).
Auto-numbering with sequences
Sequences give records automatic document numbers — PO numbers, invoice numbers, ticket IDs. A sequence is org-wide and named, so several forms can share one counter. Manage them in Settings → Sequences (or create one inline from the field editor), then add a Sequence Number field to a form and pick the sequence.
| Setting | What it does |
|---|---|
| Prefix | Text before the number, e.g. PO-. Supports date tokens {YYYY} {YY} {MM} {DD} rendered at assignment time — e.g. INV-{YYYY}- → INV-2026-0042 |
| Zero-padding | Digit width of the number: padding 4 → 0042 |
| Counter reset | Optionally restart at 1 every year or month. Pair with a matching date token in the prefix, or numbers will repeat across periods |
| Allow manual editing | Per field: users may type their own value; leaving it empty still auto-assigns. Manual values never move the counter |
Numbers are assigned server-side, atomically with the record insert — concurrent submissions (UI, public forms, REST API, and MCP agents alike) can never receive the same number. Values are stored as plain text and frozen at creation: renaming the sequence or changing its prefix later never rewrites existing records. When the sequence field is a form's first text-like field, its value becomes the record's title automatically.
Public form URL
Every form has a shareable public URL in the format:
https://app.snapi.ca/embed/{org-slug}/{form-slug}
Anyone with this URL can submit the form — no account needed. Use this link in emails, on websites, or in chatbots. To embed the form in an iframe or use the JavaScript SDK, see Embedding Forms.
Viewing and managing records
The Records view on any form shows all submissions in reverse chronological order. Each row shows the submission time, the value of the first text-like field as a title, and any status or board assignment.
Clicking a record opens the detail panel, where you can:
- View all field values
- Edit any field value (as a Snapi team member)
- Add internal comments (not visible to the submitter)
- Attach files
- View the audit log — every field change, comment, and file with the user who made it and when
Webhooks on submit
Each form can have webhooks that fire around the form lifecycle. There are three types:
| Type | When it fires | Can abort the submission? |
|---|---|---|
| onload webhook | When the form loads | No — its response can prefill form data before anything is submitted |
| pre-webhook | Before the record is saved | Yes — respond with {"abort": true} to reject the submission and surface the error to the submitter; the response can also modify the submitted data before it's saved |
| post-webhook | After the record is saved | No — the record already exists; use for notifications and downstream automation |
The pre-webhook payload is a JSON object containing the form's ID, name, and slug plus all submitted field values; post-webhook payloads also include the saved record. See Webhooks for the full payload schema and authentication headers.
Note: forms are called entities in the API for backwards compatibility — API paths and webhook payload keys use entity.
Record data via API
You can read and create records programmatically using your API key. This is useful for building integrations, bulk imports, or giving an AI agent read access to submitted data. See API Keys for the endpoints and authentication details.
Note on file uploads: File fields store a reference to the uploaded file, not the raw bytes. Use the GET /api/v1/files/:filename endpoint to download the actual file. Files count against your plan's storage limit.