Overview
Spreeflo Forms let you build beautiful, conversational forms and surveys — then collect responses on a standalone Spreeflo page or embedded anywhere on your own website. You can ask any of a wide range of question types, map answers straight onto contacts in your audience, and show forms inline or as a popup, slider, popover, or side tab.
Forms are part of the free plan and include unlimited responses — there's no cap on how many people can submit. Every submission is saved in the form's Results tab, where you can review responses individually and download them all as a CSV for use in a spreadsheet or another tool. This guide covers the question types you can use, how to branch the flow with logic, how to map responses to contacts, every way to embed and trigger a form, how to push submissions to your own webhook, and the optional add-on to remove Spreeflo branding.
Question types
Add questions to your form from the Add content panel on the Content tab. Questions are grouped into categories so you can quickly find the right field. Most questions support a Required toggle, an optional description and image, and field-level settings (such as choice layout, character limits, or rating icons).
| Category | Question type | What the respondent does |
|---|---|---|
| Contact info | Contact Info | Enters first name, last name, email, phone, and company in one step (each field can be shown, hidden, or required). |
| Enters a single email address. | ||
| Phone | Enters a phone number with a country-code selector. | |
| Address | Enters a full postal address (street, city, state, zip, country). | |
| Company | Enters a company name. | |
| Website | Enters a website URL. | |
| Choice | Multiple Choice | Picks one or several options; supports an "Other" field, randomized order, and a selection limit. |
| Dropdown | Selects one option from a dropdown list. | |
| Picture Choice | Selects one or several image options. | |
| Yes / No | Picks a binary answer with customizable labels. | |
| Legal | Accepts or declines a consent / agreement statement. | |
| Checkbox | Ticks a single checkbox (e.g. opt-in confirmation). | |
| Rating & ranking | Opinion Scale | Rates on a numeric scale with labels for the low, middle, and high ends. |
| Rating | Rates using star, heart, thumb, or smiley icons. | |
| Ranking | Drags options into a preferred order. | |
| Matrix | Answers several rows against shared columns in a grid. | |
| Text, number & date | Short Text | Types a single line of text. |
| Long Text | Types a longer, multi-line response. | |
| Number | Enters a number, with optional min/max limits. | |
| Date | Picks a date in your chosen format. | |
| Other | Video & Audio | Records a video or audio reply. |
| File Upload | Uploads one or more files. | |
| Custom Design | Displays a free-form designed block instead of a question. | |
| Flow & structure | Welcome Screen | Opens the form with an intro splash (one per form). |
| Statement | Shows a text-only screen with no input. | |
| Question Group | Groups related questions under a shared heading. | |
| Multi-Question Page | Shows several questions together on one page. | |
| End Screen | Closes the form with a thank-you screen, optional share buttons, and a button. | |
| Redirect to URL | Sends the respondent to an external URL after submitting. |
Add logic to your form
By default a form runs straight down: question 1, question 2, question 3, ending. Logic lets an answer decide what comes next — skip questions that don't apply, send unqualified respondents to a different ending, or route each option to its own follow-up. You build it on the Workflow tab of the form builder, next to Content.
The logic map
The Workflow tab shows your whole form as a map. Each card is one screen, laid out left to right in flow order — welcome screen first, ending screens last. Questions nested inside a Question Group or a Multi-Question Page aren't separate cards; the container is the screen, so that's what the flow jumps to. Arrows show where the flow can go, and their style tells you why:
| Arrow | What it means |
|---|---|
| Blue, with a number | A conditional rule. The number matches the rule's number in the panel, so you can tell which rule draws which arrow. |
| Solid grey | The card's "in all other cases" jump — where the flow goes when no rule matches. |
| Light grey | The implicit next screen. Nothing is configured here — the form simply continues in order. |
Ctrl/⌘ + Z and Ctrl/⌘ + Shift + Z.Build a rule
Select a card and use Add rule in the panel. A rule is one or more conditions plus a destination — read it as "if this, go there". Add several conditions with Add condition and choose whether they join with and (all must be true) or or (any one is enough).
Below the rules sits In all other cases, go to — the fallback when no rule matches. Leave it on Next question (in order) to just continue down the form. On screens that can't have conditions (statements, media, groups) this is the only control, and it reads Always go to.
Which conditions you can pick depends on the question's type — Spreeflo only offers comparisons that make sense for the answer:
| Question type | Available conditions |
|---|---|
| Multiple Choice, Dropdown, Picture Choice | is, is not — compared against the question's own options. |
| Yes / No, Legal, Checkbox | is — pick the yes/no (or checked/unchecked) side, using your own labels. |
| Number, Rating, Opinion Scale | is equal to, is not equal to, is greater than, is less than, is greater than or equal to, is less than or equal to. |
| Short Text, Long Text, Email, Phone, Website, Company | is, is not, contains, does not contain, starts with, ends with. |
| Date | is on, is before, is after. |
| All of the above | is answered, is not answered — useful for routing around skipped optional questions. |
| Statements, media, groups, other screens | No conditions — there's no answer to test, so only the "always go to" jump applies. |
How the flow is decided
When a respondent finishes a screen, Spreeflo works through that screen's rules in order and takes the first one that matches. If none match, it uses the "in all other cases" jump; if that isn't set, it moves to the next screen in order. Rule order is therefore meaningful — put your most specific rule first and reorder with the up/down arrows on each rule.
Warnings on the map
As you edit, Spreeflo checks the logic against the current form and flags problems on the affected card and in its panel. You'll see a warning when a rule has no conditions, when a jump points at a question that no longer exists, points back at the same question, or points at a welcome screen, when a condition no longer fits the question's type (for example after changing a question type), and when no path leads to a card — the sign of an orphaned question a respondent can never reach.
Two screens don't take logic. A welcome screen always continues to the first question, and ending screens finish the form — nothing leaves them. To end a form early, point a rule at the ending screen you want: that's how a "not a fit right now" path or a qualification flow is built.
Map responses to contacts
A form can do more than collect answers — it can feed straight into your audience. Each form has a Map to contacts toggle. When it's on, every submission creates or updates a contact in your contact list, so responses don't just sit in a results table — they become people you can segment, message, and automate against.
To make sure every submission lands on the right person, add a required Email field. Spreeflo uses the email address to match a submission to an existing contact or create a new one. If a form has no email field, only visitors already identified by the Spreeflo SDK (see Web Tracking & Analytics) are mapped — everyone else is recorded as an anonymous response.
Mapping fields to attributes
Beyond the built-in contact fields (email, name, phone, company, website), you can map almost any question to a contact attribute. Open a question's settings and use Map to attribute to choose which contact field the answer should be written to. For example, a "What's your role?" dropdown can be mapped to a job_title attribute, or a number question to a budget attribute.
Contact options
When Map to contacts is enabled, a few extra options become available:
What this unlocks
Mapping responses to contacts turns a form into a growth tool rather than a one-off survey. Common use cases include:
Tip: Leave Map to contacts off when you only want anonymous feedback (e.g. an NPS survey) and don't need the responses tied to people in your audience.
Embed your form
Every form is reachable at its own Spreeflo-hosted link, so you can share it directly without any setup. To put a form on your own website, you have two options — embed it inline in the page, or show it as a popup. The difference matters because they're installed differently: inline embeds use a small embed.js snippet, while popups require the full Spreeflo SDK.
Embed a form inline
An inline embed renders the form directly inside your page, in the spot where you place the code. There are two inline styles:
| Type | How it appears |
|---|---|
| Standard | The form renders inside your page at a width and height you set (with optional responsive auto-height). |
| Full Page | The form fills the entire browser viewport, like a dedicated landing page. |
From the Embed tab, Spreeflo gives you a small snippet to copy. Paste it into your page wherever you want the form to appear:
<div data-spreeflo-embed="YOUR_EMBED_ID"></div>
<script src="https://spreeflo.com/cdn/sdk/embed.js"></script><div data-spreeflo-embed="…"> marks where the form renders and identifies which form to load.embed.js is Spreeflo's lightweight loader script. It scans the page for your embed placeholder and renders the form. You only need to include it once per page, even if you embed several forms — no SDK or account key required. The Embed tab also includes step-by-step instructions for popular platforms like WordPress, Shopify, and Squarespace, in case you'd rather paste the snippet through their editors.
Show a form as a popup
A popup overlays the form on top of your page and opens based on visitor behavior, rather than sitting inline. Popups come in four styles:
| Type | How it appears |
|---|---|
| Popup | A modal that opens over your page with a dimmed backdrop. |
| Slider | A panel that slides in from the edge of the screen. |
| Popover | A floating chat-style panel attached to a launcher button. |
| Sidetab | A fixed tab pinned to the side of the screen that opens a panel when clicked. |
Popups require the Spreeflo SDK. Unlike inline embeds, popups aren't loaded by theembed.jssnippet — they're delivered through the full Spreeflo SDK installed on your site. When you create a popup, Spreeflo shows you the SDK install steps instead of an embed snippet. See Web Tracking & Analytics for how to install the SDK. Once it's on your site, your popups appear automatically — no per-form code to paste.
Each popup style can be styled in the builder — size, launcher button text and color, launcher position (left or right), border radius, and overlay opacity. Choose a trigger for when it opens:
You can also fine-tune close behavior — keep the form open until it's submitted, auto-close after submission, or prevent it from reopening once a visitor has dismissed it — so popups stay helpful rather than annoying.
Forms and the Spreeflo SDK
To recap the two paths: inline embeds need only the embed.js snippet and nothing else, while popups need the full Spreeflo SDK installed on your site. The SDK is the same one used for web analytics and visitor tracking, so if you already have it installed, popups work right away.
With the SDK in place, forms also integrate more deeply: a submission is tied to the SDK's identified visitor, so even forms without an email field can be mapped to the right contact, form submissions become events you can act on, and Spreeflo can deliver scheduled popup forms as part of your campaigns without you editing the page again. For how to install the SDK, identify visitors, and track events, see Web Tracking & Analytics.
Attach your own metadata
Sometimes the answers alone aren't enough — you need to know where a submission came from. Which account was the visitor logged into? Which pricing page did they fill the form on? Which A/B variant did they see? Add a data-spreeflo-metadata attribute to the embed placeholder with a JSON object, and those keys travel with the submission:
<div
data-spreeflo-embed="YOUR_EMBED_ID"
data-spreeflo-metadata='{"plan":"trial","accountId":"acct_7712"}'
></div> The metadata is stored on the response and included in the metadata field of the webhook payload, so your server sees exactly the context your page had. Since the attribute is rendered by your own page, you can fill it in with template values — a user id from your backend, a plan name, a campaign code.
{ }, not an array or a bare value). Invalid JSON is ignored — the form still loads and submits, just without your metadata, and a warning is logged to the browser console. userAgent and referrer to every submission's metadata. These are reserved — if your object uses the same keys, Spreeflo's values win. ?spMeta= followed by the URL-encoded JSON object to the link, and it behaves the same way. Don't put secrets in metadata. It lives in your page's HTML and in the form URL, so a visitor can read it and change it. Treat it as a hint about context, and re-verify anything security-sensitive server-side when the webhook arrives.
Listen to form events
When a form is embedded on your site, it tells the host page what the visitor is doing — so you can react to it. Use this to redirect after a submission, fire your own analytics or conversion tracking, reveal a thank-you message, or anything else. The same events fire for both inline embeds (embed.js) and popups (the Spreeflo SDK), with the same data, so you write your handler once.
| Event | Fires when |
|---|---|
open | The form becomes visible — an overlay opens, or an inline form mounts on the page. |
submit | The visitor submits the form. Fires exactly once per submission. |
dismiss | The visitor closes the form without submitting — a genuine dismissal. |
close | The form closes for any reason. Check detail.submitted to tell whether a submit came first. |
You can listen in either of two equivalent ways — pick whichever fits your site. The simplest is to assign a callback object to window.SpreefloEmbed; each handler runs when its event fires:
window.SpreefloEmbed = {
onOpen: (detail) => { /* form became visible */ },
onSubmit: (detail) => { /* submitted — detail.embedId, detail.formId */ },
onDismiss: (detail) => { /* closed WITHOUT submitting */ },
onClose: (detail) => { /* closed for any reason — detail.submitted says which */ },
}; Or listen for DOM events named spreeflo:<event>. For inline embeds these are dispatched on the embed's placeholder and bubble up, so a single window listener catches every form on the page:
window.addEventListener('spreeflo:submit', (e) => {
console.log('Form submitted', e.detail.embedId, e.detail.formId);
});
window.addEventListener('spreeflo:dismiss', (e) => {
// visitor closed the form without submitting
}); Every event carries a detail object with the same shape — the type of event, the embedId (the data-spreeflo-embed value, or the popup's embed id), the published formId, and, on close, a submitted boolean. Use embedId or formId to react only to a specific form when you embed more than one.
open and submit — they have no close button, so they never emit dismiss or close. close (with submitted: false), but a close that follows a submit fires only close with submitted: true — never dismiss. dismiss will ever fire for it.Send responses to a webhook
Form events tell your page what happened. A webhook tells your server. Turn one on and Spreeflo sends every submission to an endpoint you control as a POST request with a JSON body — so responses can flow straight into your CRM, database, Slack channel, or any internal tool, without polling the Results tab or exporting a CSV.
Open your form's Settings (the cog in the builder toolbar), go to the Integrations tab, and switch on Send responses to a webhook. Then paste your endpoint URL. Webhooks are available on every plan, including free.
http isn't accepted, because submissions can carry personal data. What Spreeflo sends
Each submission is delivered as a single JSON POST with Content-Type: application/json:
{
"formId": "frm_8Kq2wZ",
"formVersion": 7,
"responseId": "res_3xN9dLpQ",
"submittedAt": "2026-07-23T09:41:12.804Z",
"contactId": "con_51bTmA",
"email": "ada@example.com",
"answers": {
"q_email": {
"type": "email",
"title": "What's your work email?",
"value": "ada@example.com"
},
"q_team_size": {
"type": "dropdown",
"title": "How big is your team?",
"value": "11-50"
},
"q_notes": {
"type": "long_text",
"title": "Anything else we should know?",
"value": "We're migrating off a competitor next quarter."
}
},
"metadata": {
"plan": "trial",
"accountId": "acct_7712",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) …",
"referrer": "https://yoursite.com/pricing"
}
}| Field | What it contains |
|---|---|
formId | The form the submission belongs to. |
formVersion | The published version of the form that was filled in. |
responseId | Unique id for this submission. Use it to de-duplicate — a retry re-sends the same responseId. |
submittedAt | When the form was submitted, as an ISO 8601 timestamp. |
contactId | The contact this submission was mapped to, when the visitor could be identified — otherwise null. |
email | The identified visitor's email address — otherwise null. |
answers | One entry per question, keyed by question id, with the question's type, title, and the answer given. |
metadata | Context captured with the submission — the userAgent and referrer Spreeflo records, plus any keys you attached yourself with data-spreeflo-metadata. |
Identified visitors
contactId and email are what turn a webhook payload into something actionable — they let your system attach the submission to a person you already know instead of an anonymous row. A visitor counts as identified when either:
For anonymous submissions — no email field answered and no SDK identity — both fields are null and the rest of the payload is delivered as usual. The single best way to get identified submissions is to add a required Email field to your form.
Verify the signature
Your endpoint is a public URL, so anyone who finds it could post fake submissions to it. To prevent that, set an optional signing secret in the same Integrations tab. When one is set, every request carries an X-Spreeflo-Signature header containing an HMAC-SHA256 of the raw request body, computed with your secret. Recompute it on your side and compare — if it doesn't match, reject the request.
import crypto from 'node:crypto';
// Express — note express.raw(), so we hash the exact bytes Spreeflo sent
app.post('/hooks/spreeflo', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.get('X-Spreeflo-Signature') || '';
const expected = crypto
.createHmac('sha256', process.env.SPREEFLO_WEBHOOK_SECRET)
.update(req.body) // the raw Buffer, not a parsed object
.digest('hex');
const valid =
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!valid) return res.sendStatus(401);
const submission = JSON.parse(req.body.toString('utf8'));
// ignore submissions you've already handled — retries reuse responseId
handleSubmission(submission);
res.sendStatus(200); // acknowledge quickly; do slow work in the background
});Sign against the raw body. Compute the HMAC over the exact bytes you received, before any JSON parsing or re-serializing — re-encoding the payload changes the signature. And compare with a timing-safe function, as above, rather than===.
Delivery & retries
spreeflo:submit event for embedded and popup forms. responseId as an idempotency key and ignore ones you've already processed. Webhook or form events? Use form events for anything in the browser — redirects, thank-you messages, conversion pixels. Use a webhook for anything that must be reliable and server-side — creating a CRM record, notifying your team, kicking off fulfilment. Browser events can be missed if the visitor closes the tab; webhooks are sent from Spreeflo's servers and retried.
Remove Spreeflo branding
By default, forms show a small "Powered by Spreeflo" badge in the footer. To hide it, subscribe to the No Spreeflo Branding add-on for $10 per month. It:
Toggle Spreeflo branding off in your form's settings to start. If you're not subscribed yet, you'll be prompted to add the subscription first.
Pricing & limits
Forms are included on the free plan with unlimited responses — there's no limit on how many submissions you can collect, on any plan. The form builder, question types, embed types, contact mapping, and webhooks are all available for free. Some contact options, like marking submissions as marketing contacts and double opt-in, are part of paid plans.
For a full breakdown of what each plan includes, see About Spreeflo Pricing Plans.
Need Immediate Help? Contact our support team at support@spreeflo.com or check our other guides for more information.