Documentation

Everything NibbleForms can do — and how to set it up.

Build forms, share them as links, QR codes or embeds, read what came back, then automate what happens next with workflows and webhooks.

Get started

From a blank account to a published, shared form.

Create your account and workspace

Sign up with an email and password. New accounts go through a short onboarding flow that asks about your organisation, so your workspace, defaults and reporting are right from the start — everything it sets can be changed later in Settings.

If email confirmation is switched on for this deployment, confirm your address before signing in; otherwise you land straight in your workspace.

  1. 1Open /signup and create an account.
  2. 2Complete onboarding. Your workspace is created for you.
  3. 3Signed-in visitors return through /dashboard.
Personal or organisation
Individual accounts get a personal workspace. Organisation workspaces add shared members, roles, folders and an activity log.

Build and publish your first form

  1. 1From the dashboard choose New form — start blank or pick a template.
  2. 2Add fields on the Fields tab: drag from the palette, or click a field type to append it.
  3. 3Set each field's label, help text, placeholder and required flag in the field editor.
  4. 4Optionally add conditional rules on the Rules tab, and split long forms with a Page Break.
  5. 5Set the confirmation message, redirect, notification email, response limit, close date or password on the Settings tab.
  6. 6Pick an accent colour on the Themes tab and check the live preview.
  7. 7Publish, then copy the link, QR code or embed snippet from the Share tab.

The builder autosaves as you work — there is no separate save button to forget. A form is only publicly reachable once it is published.

Build forms

Every field type, rule and theme the builder ships — 16 field types across 6 groups.

Field types

Forms are assembled from the same catalogue the builder, the renderer and the server-side validator all read from: 16 types across 6 groups.

  • Every field can be required, and can carry help text and a placeholder.
  • Choice fields (Radio buttons, Checkboxes, Dropdown) get real options you can add, rename and reorder.
  • The File and Payment fields expose extra configuration — accepted types and size limits, or amount and currency.
  • Page Break is structural: it splits the form into pages and never produces an answer.
FieldGroupWhat it is for
TextBasicOne line of text — names, titles, short answers.
ParagraphBasicA multi-line paragraph for open-ended answers.
EmailBasicValidated email address. This is also the address confirmation emails reply to.
PhoneBasicPhone number. Ugandan mobile shapes are canonicalised to +256…; other numbers are kept as typed.
NumberBasicNumeric input, validated as a number.
URLBasicValidated web address.
Radio buttonsChoicePick exactly one option (radio buttons).
CheckboxesChoicePick any number of options (checkboxes).
DropdownChoicePick one option from a compact list.
RatingChoiceA score out of five — useful for satisfaction and NPS-style questions.
DateDate & mediaCalendar date.
TimeDate & mediaTime of day.
File uploadDate & mediaUpload documents or images, stored privately with signed-URL downloads.
CheckboxOtherA single yes/no box for consent or acknowledgement.
PaymentPaymentsCollect a payment where MarzPay is configured for the deployment.
Page BreakLayoutStructural marker: splits the form into pages and collects no answer.

Conditional rules (show a field if…)

A field can appear or disappear based on earlier answers. Open the field editor and add one or more rules. Every rule must pass — they are combined with AND, not OR.

  • Rules read fields that come earlier in the form; a field can't depend on a question below it.
  • The Rules tab lists every rule on the form in one place — click a rule to jump straight to its field.
  • A hidden field is skipped by validation and is not part of the stored answers.
OperatorMatches when
equalsthe answer is exactly the value you typed.
not_equalsthe answer is anything else.
containsa text answer contains the value.

Multi-page forms

Drop a Page Break where a new page should start. The Page Break's label becomes the page title, and respondents can move back and forth before submitting.

  • The first page has no separate title — it uses the form title.
  • Each page is validated before the respondent can continue, so nobody can skip a required question by moving forward.
  • A page indicator shows where the respondent is, on the public page and in the embed widget alike.

Accent colours

Choose one of the 5 accent presets on the Themes tab: Plum, Indigo, Teal, Rose, Amber. The choice is saved on the form.

  • The accent is applied through a single CSS custom property, so the builder preview, the public page and the embed widget always match.
  • The theme belongs to the form, not the account — different forms can use different accents.

Templates

The gallery ships 31 ready-to-use templates across 12 categories — registration, HR, feedback, marketing, sales, events, education, healthcare, hospitality, real estate, support and non-profit.

Every template is a set of real fields with real options and sensible required flags. Pick one and it opens in the builder, ready to edit.

  • Browse them from the dashboard gallery, or from the public /templates page before you sign up.
  • Templates are starting points — nothing about them is locked.

AI assistance while you build

The AI tab works on the form you are editing. AI runs on the deployment's own OpenAI-compatible key, and every AI action is metered in credits from the workspace wallet.

ActionWhat it does
Generate a formDescribe the form in plain English and get real fields back, added to the builder.
Import a formUpload a photo or scan, a PDF or a Word document and turn it into editable fields.
ImproveRewrite labels, help text and options so the questions read better.
CritiqueReview the form before publishing and flag structure or clarity problems.

If this deployment has no AI key configured, AI requests answer 503 AI isn't configured yet. That is a setup step, not a bug — set AI_API_KEY (plus AI_BASE_URL / AI_MODEL if you are not using the defaults) and redeploy.

Share and embed

Send a form anywhere: public link, QR code, script embed or iframe.

Public link and QR code

Every published form has a public URL at /f/<form-id>. Open the Share tab in the builder to copy it, or download a QR code as a PNG for print, signage and handouts.

  • Only published forms resolve publicly — drafts stay private to your workspace.
  • The QR code points at the same link, so editing the form changes what the code opens.
  • The Share tab also writes the embed code for you; set a height and copy the snippet.

Embed a form in your own website

Embedding puts the live form inside your page, so visitors never leave your site. The Share tab generates the code for you — pick a height, then copy either the script or the iframe version.

The script embed is recommended: it renders responsively and grows to fit its content, so long or multi-page forms never get cut off.

  • No plugins or frameworks are needed — the snippet is plain HTML, so it drops into any site.
  • The loader reads its own src to work out the deployment origin, so localhost, a preview URL and a custom domain all work with no configuration.
  • The child widget posts an easyform:resize message, which is how the script embed resizes itself to fit its content. A plain iframe keeps whatever height you set.
  • allow="camera; microphone" matters on the iframe embed when the form has File or photo fields.
  • window.EasyForm.version reports the loader version (1.0.0).
WhereHow to add it
Plain HTML / static sitePaste the snippet where the form should appear.
Website builders (WordPress, Webflow, Squarespace)Add a Custom HTML or Embed block and paste the snippet.
ReactRender the iframe snippet in JSX, or inject the script in a useEffect when the component mounts.
Next.jsUse the iframe snippet as JSX, or load the script with next/script and strategy="afterInteractive".
Script embed (recommended)
<script
  src="https://your-domain.com/widgets/easyform.js"
  data-form-id="YOUR_FORM_ID"
  data-height="640"
  async
></script>
iframe embed
<iframe
  src="https://your-domain.com/widgets/form.html?form=YOUR_FORM_ID"
  width="100%"
  height="640"
  style="border:0;overflow:hidden"
  loading="lazy"
  allow="camera; microphone"
  title="Contact form"
></iframe>
Programmatic mount
<div id="my-form"></div>
<script src="https://your-domain.com/widgets/easyform.js" async></script>
<script>
  window.EasyForm.mount(document.getElementById("my-form"), "YOUR_FORM_ID", {
    height: 640,
  });
</script>

Only published forms render in an embed — the embed endpoint returns 404 for a draft. The embed payload carries display data only: never the password hash, your notification address or any webhook secret. Password-protected forms work in embeds too: the widget forwards the access token with the submission instead of relying on a cookie, because third-party cookies are blocked inside iframes.

Responses and analytics

Read, search, export and interrogate what came in.

The responses table

Responses is scoped to one form at a time — pick the form, then work through its answers. The table is searchable and exportable, and every response carries an internal status that workflows can set.

  • Search across a form's responses and export them to CSV.
  • Each response has an internal status: New, In Progress, Completed or Archived.
  • Open a response to see every answer, including uploaded files.
  • The AI Analysis tab sits alongside the table, in the same per-form context.

Analytics

Analytics turns submissions into KPI cards and charts computed from the actual response rows — not from a cached number, so the figures always match what you can see in the table.

  • KPI cards cover things like total responses, completion and how many fields are conditional.
  • Generate insights runs an AI pass over the form and its data to summarise what stands out.
  • Insights are cached per form, so re-opening the view is cheap.

Ask your data

The AI Analysis tab in Responses answers plain-English questions about one form's submissions. It reads the real responses and answers with counts, tables and charts rather than a wall of text.

  • One form per question — the selector chooses which form the AI is looking at.
  • Export the rows behind an answer as CSV, or as an Excel-compatible file where the AI ran a query.
  • If a question references something the form does not collect, the AI asks you to rephrase instead of inventing an answer.
  • AI questions are metered in credits like every other AI action.

Files and uploads

File fields upload to a private storage bucket. Each file is tracked with its own metadata, and the Files page lists what has been collected across your workspace.

  • Files are never publicly readable — there is no plain public URL, only short-lived signed download links generated for you.
  • Uploads are checked against the field's accepted types and size limits before they land.
  • Uploads count toward your plan's storage allowance and are tracked per form.

Automate

Workflows inside the workspace, webhooks out to everything else.

Workflows: one trigger, up to five actions

Workflows live under Workflows in the sidebar. Choose a trigger — a new response on one form, or on any form in the workspace — then add up to five actions, in order.

  • Every action is isolated and best-effort: a bad URL, a missing email key or a stale assignee is written to the run log, and the respondent's submission still succeeds.
  • Runs happen exactly once per response — the run log is unique per workflow and response, so a retried submission can never assign or email twice.
  • A run costs 1 credit, and each email actually sent costs 1 credit. A workspace that can't afford it simply isn't charged, and the workflow still runs.
  • Results reach people through the header bell, and every run is recorded in the workspace activity log.
ActionWhat it does
Send email notificationEmails you — or an address you set — when the form receives a response. Falls back to the form's notification address.
Send confirmation emailConfirms the submission to the respondent, when the form collected their address.
Add a webhookPOSTs the same signed submission payload the form-level webhooks send, to a URL you set.
Update internal statusMoves the response to New, In Progress, Completed or Archived. The status is visible in the responses table.
Assign to a team memberRoutes the response to one active member of the organisation workspace.
Notify the teamWrites to the header bell for everyone with access, or for the members you pick.

Email actions need RESEND_API_KEY plus a sender address (DEFAULT_FROM_EMAIL), and the sending domain must be verified with the email provider. Without that key the action reports "email is not configured" in the run log instead of failing the submission.

Webhooks: push submissions into your own systems

A webhook POSTs each submission to a URL you control, so your CRM, helpdesk, spreadsheet or internal service can react the moment a response arrives. Webhooks are configured per form in the builder, under Integrations.

  1. 1Pick an endpoint that accepts HTTPS POST requests.
  2. 2Open the Integrations tab in the builder and click Add webhook.
  3. 3Paste the URL and choose when it fires — On submit, On deadline, or both.
  4. 4Add a signing secret if you want to prove a request really came from this form (recommended).
  5. 5Click Test on the webhook card, then check Recent deliveries for the status code and latency.
  • Requests time out after 8 seconds. Any 2xx status code counts as success.
  • Submission webhooks are fire-once — a failed attempt is logged but not retried. Deadline webhooks are retried hourly by the scheduler until they succeed, and never fire twice for the same deadline.
  • Every attempt is recorded in the delivery log and shown under Recent deliveries with its status code and latency.
  • Signing secrets are only ever sent outbound. They are never exposed on the public form, in the embed payload, or to anyone without access to the form.
  • A workflow's webhook action reuses this same signed payload, timeout and delivery log, so one verification routine covers both.
Method and body
POST with a JSON body (Content-Type: application/json).
X-FormCraft-Event
submission, deadline or test.
X-FormCraft-Webhook-Id
the id of the webhook that fired.
X-FormCraft-Signature
sha256=<hex> — an HMAC-SHA256 of the exact raw body. Only sent when the webhook has a secret.
EventFires when
On submit (per record)Once per response, as soon as it is recorded. Use it for real-time CRM pushes, chat notifications and internal queues.
On deadlineOnce, after the form's close date passes and the form is auto-closed. Use it to hand over a final report when a survey ends.
testOnly when you press Test — never for real data.
Payload — submission
{
  "event": "submission",
  "form": { "id": "...", "title": "Event registration", "schemaVersion": 3 },
  "response": {
    "id": "...",
    "createdAt": "2025-08-29T10:00:00.000Z",
    "answers": { "fieldId1": "Ada Lovelace", "fieldId2": "ada@example.com" }
  },
  "fields": [
    { "id": "fieldId1", "label": "Full name", "value": "Ada Lovelace" },
    { "id": "fieldId2", "label": "Email", "value": "ada@example.com" }
  ],
  "meta": { "userAgent": "Mozilla/5.0 ..." }
}
Payload — deadline
{
  "event": "deadline",
  "form": { "id": "...", "title": "Event registration", "closeDate": "2025-09-30" },
  "responseCount": 128,
  "createdAt": "2025-10-01T00:00:00.000Z"
}
Verify the signature (Node.js)
// Header: X-FormCraft-Signature: sha256=<hex>
import { createHmac, timingSafeEqual } from "crypto";

const secret = process.env.FORMCRAFT_WEBHOOK_SECRET;
const signature = req.headers["x-formcraft-signature"].replace("sha256=", "");
const expected = createHmac("sha256", secret)
  .update(rawBody) // the EXACT raw request body, not a re-stringified copy
  .digest("hex");

if (!timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
  return res.status(401).end(); // not from us — reject
}
Try it from your terminal
curl -X POST https://your-endpoint.example.com/hook \
  -H "Content-Type: application/json" \
  -H "X-FormCraft-Event: submission" \
  -d '{"event":"submission","form":{"title":"Event registration"}}'

Send a Test from the webhook card before relying on real data: it posts the same signed payload using your latest real response as the sample (or a mock built from the form's fields if there are no responses yet). If nothing arrives, check Recent deliveries first — it records the status code and error for every attempt.

Use cases

Six end-to-end setups, built only from features that ship today.

Event registration with instant confirmations

Collect registrations, confirm every attendee automatically, and push each signup into your CRM or spreadsheet without exporting anything by hand.

  1. 1Start from the Event registration template — it already has Text, Email, Dropdown and Checkbox fields.
  2. 2Settings: write a confirmation message, set a close date for registrations, and turn on the response limit if seats are capped.
  3. 3Workflows: add Send confirmation email and Notify the team.
  4. 4Integrations: add a webhook for On submit pointed at your CRM, and one for On deadline so the final attendee count lands when registration closes.
  5. 5Share: download the QR code for printed signage and post the link wherever you are promoting the event.

Lead capture on a marketing site

Put the form inside your own site and have leads routed to sales the second they arrive.

  1. 1Build a short form: Text, Email, Phone, and a Dropdown for budget or interest.
  2. 2Publish it, then paste the script embed from the Share tab into your site's HTML.
  3. 3Workflows: Assign to a team member, Notify the team, and Update internal status to New.
  4. 4Integrations: add a webhook for On submit to your CRM, or to a chat tool's incoming-webhook URL.

Keep it short. The script embed resizes to fit its content, so a two-field form stays a two-field form instead of leaving a tall empty box on the page.

Support and product feedback intake

One form for everything, triaged automatically, then summarised with AI when you want the themes rather than the individual messages.

  1. 1Fields: Rating, Dropdown (topic), Paragraph (what happened), and File for a screenshot.
  2. 2Settings: set a confirmation message, and enable password protection if only staff should reach the form.
  3. 3Workflows: Update internal status to New and Notify the team on every response.
  4. 4Later, open Responses → AI Analysis and ask which complaints come up most; export the rows behind the answer.

Screenshots stay private: reviewers open them through short-lived signed download links, not public URLs.

Job applications with private files

Collect CVs, portfolios and cover letters without exposing anyone's documents.

  1. 1Fields: Text (full name), Email, Phone, Dropdown (role), and File for the CV and portfolio.
  2. 2Settings: set a close date so the posting archives itself, plus a response limit if you expect a flood.
  3. 3Workflows: Send confirmation email to the applicant, and Notify the team so the hiring manager sees each one.
  4. 4Review downloads go through signed links generated for your workspace only.

Use the File field's accepted-types and size limits so applicants cannot attach an unreadable 200 MB file.

Post-event survey that closes and reports itself

Send a survey when the event ends and let the deadline webhook hand you the wrap-up.

  1. 1Fields: Rating (overall), Rating (venue), Radio buttons (would you attend again?), and a Paragraph for anything else.
  2. 2Settings: turn on Close on date and pick the day the survey should stop accepting responses.
  3. 3Integrations: add a webhook for On deadline pointing at the channel or endpoint that should receive the final count.
  4. 4Read the trends in Analytics, or ask the AI Analysis tab a plain-English question such as what people liked most.

Paid workshop signup

Take payment and registration in a single form, wherever MarzPay is configured for the deployment.

  1. 1Add a Payment field with its amount and currency, then the attendee details.
  2. 2Settings: set the response limit to the number of seats, and a close date for the booking window.
  3. 3Workflows: Send confirmation email to the attendee and Notify the team.
  4. 4Integrations: add a webhook for On submit so your finance sheet or accounting tool sees each sale.

Payment fields only work when the deployment has MarzPay configured. If it does not, leave the field out and collect payment elsewhere.

Security, plans and access

How protection is enforced, what each plan includes, and how teams share a workspace.

How access and protection are enforced

  • Row Level Security in Postgres: every form, response, file and workflow row is scoped to the workspace that owns it, enforced in the database rather than only in the UI.
  • Form passwords are stored as a hash, and the hash column is denied at the database level — so it cannot be read through a direct browser-side client call either.
  • The submit endpoint re-validates every answer against the form's schema on the server, then enforces the password gate, close date and response limit before writing anything.
  • Password protection, close dates and response limits cannot be bypassed by editing the page: they are checked server-side on submit.
  • Submissions are rate-limited per IP and honeypot-protected. Bot submissions that trip the honeypot are dropped silently.
  • Uploaded files live in a private bucket and are served only through short-lived signed URLs.

Self-hosting note: the built-in rate limiter is in-memory and per instance, so a multi-instance deployment should put a shared store (for example Redis) in front of it for strict limits. Form passwords are a "keep casual visitors out" gate rather than a vault — move hashing to bcrypt or argon2 if you need stronger guarantees.

Plans and credits

Plans set the limits the product actually enforces — forms, monthly responses, storage, workflows, file uploads and AI credits. These are the same numbers the pricing page reads from.

  • Personal plans are for one person; organisation plans replace the member limit and are listed in the next article.
  • Credit packs are available from the Wallet when AI credits, workflow runs or outgoing emails run low.
  • AI actions, workflow runs and each email actually sent are metered in credits.
PlanPriceFormsResponses / monthStorageAI credits / month
FreeFree55001.0 GB500
Pro$19 / month255,0005.0 GB2,000
Premium$49 / month10050,00025.0 GB5,000

Organisation workspaces

An organisation workspace is shared: members, roles, folders, an activity log, and a plan sized for teams rather than individuals.

  • Members have roles and permissions; workflows can assign a response to an active member or notify specific people.
  • The activity log records what happened and who did it. System events, such as a workflow firing, show no actor because nobody signed in.
  • Folders organise forms without deleting anything — removing a folder returns its forms to Uncategorized.
PlanPriceFormsResponses / monthStorageMembers
FreeFree55001.0 GB1
Starter$12 / month255,00010.0 GB5
Business$39 / monthUnlimited20,000100 GB25
Enterprise$99 / monthUnlimited100,0001.0 TB250

FAQ and troubleshooting

The handful of issues that account for almost every support question.

Common questions

AI buttons return 503 AI isn't configured yet
This deployment has no AI key. Set AI_API_KEY (and AI_BASE_URL / AI_MODEL if you are not on the defaults) and redeploy.
A workflow email action reports that email is not configured
Set RESEND_API_KEY and a sender address, and verify the sending domain. The workflow still runs — only the email step reports the miss.
My form link returns 404
Only published forms are public. Publish the form from the builder.
The embed shows nothing
Publish the form first — the embed endpoint returns 404 for drafts — then paste the snippet where it can actually load.
Nothing arrived at my webhook
Press Test on the webhook card and read Recent deliveries. Your endpoint must answer within 8 seconds with a 2xx status.
A submission webhook failed and never retried
Submission deliveries are fire-once by design and are recorded in the delivery log. Only deadline webhooks retry, hourly, until they succeed.
Is there a REST API for creating forms?
No. The integration surface is webhooks going out, plus the public form and embed endpoints. Managing forms themselves happens in the workspace.

Ready to try it?

Every plan starts free, and your forms, responses and files stay in your own workspace.