# Forms

Forms are standalone feedback forms and surveys you build in Usero, publish to a public link, and collect responses to. They are
separate from the floating widget: a widget sits on your app and catches passing feedback, a form is a page you send people to on
purpose (a bug report intake, a feature request survey, an onboarding questionnaire, a churn exit survey). Responses land in the
same Usero project as the rest of your feedback.

## What it does

- **Visual builder.** Drag fields into order, set labels, mark fields required, add options, and set a brand color. No code, no
  deploy.
- **Public link.** Every form publishes to `usero.io/f/<slug>` with an 8-character slug. Share it directly or embed it in a
  cross-origin iframe; the form submits to itself either way.
- **Responses table.** Submissions collect into a table you can read in the dashboard and export to CSV.
- **Built-in analytics.** Views, unique visitors, submission rate, average time to complete, and a view-to-submit funnel, per form
  and across all forms.
- **Privacy-first.** Respondent IP addresses are encrypted at rest with AES-GCM. You never see a raw IP.

## Create a form

1. Open your project and go to **Forms** (`usero.io/YOUR_CLIENT_ID/forms`).
2. Click **New Form**. Usero creates the form, gives it a unique public slug, and drops you into the builder.
3. In the builder, give the form a title and an optional description. These show at the top of the public form.
4. Add fields (next section), set a brand color under **Settings**, and the form is live. New forms publish as **Open** by
   default, so the public link works immediately.

A form has an **Open / Closed** toggle in the builder. Closed forms still resolve at their link but show a "no longer accepting
responses" message instead of the fields, so you can pause collection without breaking the URL or losing past responses.

## Field types

Ten field types cover the common cases. Each field has a label, an optional description, and a required toggle.

| Type            | What it collects                                                        |
| --------------- | ----------------------------------------------------------------------- |
| Short Text      | A single line of text.                                                  |
| Long Text       | A multi-line text area, for open-ended answers.                         |
| Email           | A text field validated as an email address on submit.                   |
| Number          | A numeric value, with optional min and max bounds.                      |
| Dropdown        | One choice from a list you define.                                      |
| Multi-Select    | Several choices from a list.                                            |
| Multiple Choice | One choice, shown as radio buttons.                                     |
| Checkboxes      | Several choices, shown as checkboxes.                                   |
| Star Rating     | A 1 to N star rating (default 5).                                       |
| Linear Scale    | A numeric scale with optional min and max labels (for example 1 to 10). |

Choice fields (dropdown, multi-select, multiple choice, checkboxes) take a list of options you edit inline. Number, rating, and
scale fields take min and max bounds. Fields support conditional logic: a field can be set to show only when an earlier field
equals, does not equal, contains, or is not empty, so the form adapts to what the respondent picked.

## Share it

Every form has a public URL at `usero.io/f/<slug>`, where the slug is the 8-character id assigned at creation.

- From the **Forms** list, open a form's menu and use **Copy Public Link** or **View Public Form**.
- The public page renders on a clean light or dark surface (it follows the visitor's OS color scheme) with your title,
  description, brand color, and fields. There is no Usero account required to fill it in.
- Submitting shows your custom success message. You set that message and the submit button label under **Settings** in the
  builder.

To take a form offline, flip it to **Closed** in the builder. The link stays valid and shows a closed message; existing responses
are untouched.

## Embed a form on your site

A form does not have to live on its own page. Every form can also be dropped straight into a page you own, as an iframe that sizes
itself to the form and reports responses back to the same place.

1. Open the form in the builder.
2. Click **Embed**, next to **Copy Public Link**. A dialog opens with two live controls, **Theme** (Auto, Light, or Dark) and
   **Show form title**, and a ready-to-paste snippet below them that updates as you change either one.
3. Copy the snippet and paste it into your page, wherever the form should sit.

The snippet is an `<iframe>` plus a small script, no dependencies:

```html
<iframe
	src="https://usero.io/f/SLUG?embed=1"
	title="FORM TITLE"
	data-usero-form
	loading="lazy"
	style="width:100%;border:0;display:block;min-height:420px"
></iframe>
<script>
	window.addEventListener('message', function (event) {
		var message = event.data
		if (!message || message.type !== 'usero:form:resize') return
		var frames = document.querySelectorAll('iframe[data-usero-form]')
		for (var i = 0; i < frames.length; i++) {
			if (frames[i].contentWindow === event.source) {
				frames[i].style.minHeight = '0'
				frames[i].style.height = message.height + 'px'
			}
		}
	})
</script>
```

The script listens for a resize message the form posts whenever its content changes height (a validation error, an extra
conditional field, the success state) and grows or shrinks the matching iframe to fit. Without it the iframe just sits at its
`min-height` fallback, so the snippet is worth pasting in full even if your CMS lets you drop the `<script>` tag somewhere odd.

**URL parameters**, appended to `usero.io/f/SLUG`:

| Param   | Values          | Default                            | Effect                                                                           |
| ------- | --------------- | ---------------------------------- | -------------------------------------------------------------------------------- |
| `embed` | `1`             | not set                            | Strips the page chrome so the form sits flush.                                   |
| `theme` | `light`, `dark` | follows the visitor's system theme | Pins the palette instead of reading the OS setting.                              |
| `title` | `0`             | shown                              | Hides the form's heading and description, for pages that already have their own. |

A few things worth knowing:

- The embedded form works across origins. There is no allowlist to configure on your side or ours.
- Embedded pages are marked `noindex`, so the iframe view never competes with the form's own public page in search results.
- Responses land in the same place whether someone fills in the form at `usero.io/f/SLUG` directly or inside your iframe.

**Troubleshooting.** If the iframe is not resizing (it holds a fixed height, or scrolls internally), the most common cause is a
CMS or rich-text editor stripping the `<script>` tag when the snippet was pasted in. Check that the script survived by viewing the
page source. If it did and the form still is not resizing, check your site's Content-Security-Policy: a `frame-src` directive that
does not include `https://usero.io` will block the iframe from loading at all, which looks the same as a resize failure from the
outside.

## See responses and analytics

Open a form and switch tabs in the builder:

- **Responses.** Every submission as a row, with the answers laid out by field. **Export CSV** downloads the lot for a spreadsheet
  or a pivot table.
- **Analytics.** Views, unique visitors, submissions, submission rate, and average time to complete, plus a funnel from viewed to
  interacted to tried-to-submit to submitted, and a daily trend chart. The **Forms** list also has a cross-form **Analytics** tab
  that rolls these up across every form in the project.

If you turn on **Notify on submission** in a form's settings, Usero emails the project owner each time someone submits (the email
includes the answers and a link to the responses tab). This respects the owner's transactional email preference.

## Surveys

A survey in Usero is a form built from a survey template. There is no separate survey product, no second builder, and no extra
model: the template stamps a `surveyTemplateId` and a metric onto the form's settings, so everything above (fields, conditional
logic, the public link, the Open/Closed toggle, CSV export, analytics) works identically.

Five templates ship today:

| Template                     | Question                                                          | Score              | Benchmark shown           |
| ---------------------------- | ----------------------------------------------------------------- | ------------------ | ------------------------- |
| Net Promoter Score (NPS)     | 0-10 "how likely to recommend", plus a why                        | NPS, -100 to +100  | Aim for 50 or higher      |
| Customer Satisfaction (CSAT) | 5-star satisfaction, plus "what would have made this better"      | % satisfied        | Aim for 80% or higher     |
| Customer Effort Score (CES)  | 1-7 "how easy was it", plus a why                                 | Mean, 1 to 7       | Aim for 5.5 or higher     |
| Product/Market Fit           | Sean Ellis's "how would you feel if you could no longer use this" | None (a breakdown) | 40% "very disappointed"   |
| Open question                | "What's the most annoying thing about using this product?"        | None               | Ten answers shows a theme |

### Run one

1. Go to **Forms** and click **New Form**. The picker opens on **Start a new form**.
2. Every template card is a live render of the survey, so click the 0-10 row or the stars to try it before you commit. Pick one
   with **Use this**, or choose **Start blank** for an ordinary form.
3. Usero creates the form seeded with the template's title, questions, and benchmark, and drops you into the builder.
4. Edit anything. Rewrite the question labels, change the end labels on the scale, add fields, delete the follow-up. The score
   still works, because the scoring field keeps its template id even after you retitle it.
5. Copy the public link (`usero.io/f/<slug>`) from the form's menu and send it: in an email, a changelog note, a Slack message, or
   an iframe on your own page. New forms are **Open** on creation, so the link works immediately.

### Read the score

Open the survey and go to the **Responses** tab. The score panel sits above the response table:

- The headline number (for example `+42` for NPS), and the response count it was computed from.
- The benchmark line the template carries, so the number has something to be read against.
- For NPS, the promoter / passive / detractor split as a bar with counts and percentages.
- The raw distribution across every possible answer, as a bar chart.

With no scoreable responses yet, the panel reads **No responses yet** rather than showing a misleading zero. Only responses that
answered the scoring question count toward the denominator, so a skipped question does not drag the score down.

### Watch the score over time

Under the score panel sits **NPS over time** (or CSAT / CES over time, depending on the template). A single all-time number cannot
show improvement, so this panel charts the same responses as a series.

**Ranges.** Four buttons in the top right: `7d`, `30d`, `90d`, `All`. The default is 30 days. `All` starts at your oldest
response. The bucket size follows the span, so you never pick it:

| Span            | Bucket |
| --------------- | ------ |
| 7 days or fewer | Day    |
| 8 to 90 days    | Week   |
| More than 90    | Month  |

Buckets are UTC, and weeks start on Monday.

**Empty buckets.** A bucket that collected no responses keeps its place on the axis with no point drawn on it. Dropping quiet
weeks would compress the axis and make a two-month gap look like a week; plotting a zero would look like everyone suddenly hated
you. The response-count row under the chart shows `----` for those buckets.

**The reading.** Above the chart: the score for the window, a delta chip against the window of equal length immediately before it,
and one sentence, for example `Up 8 points to 31, in good territory, from 23 in the period before.` The band word (`great`,
`good`, `fair`, `poor`) uses the same threshold as the template's benchmark, so `great` starts at 50 for NPS, 80% for CSAT and 5.5
for CES.

**The minimum before a delta is reported.** Both windows need at least **3 scoreable responses**. If either has fewer, the chip
reads **No comparison yet** and the panel tells you how many the earlier period holds, rather than quoting a change one extra
answer would reverse. The current score is still shown.

**Pull request markers.** Merged AI-authored pull requests for this project are drawn on the timeline at the bucket they landed
in, in a `SHIPPED` lane above the chart. Hover a marker for the PR titles, merge dates and links to GitHub. Every PR that merged
inside the range is also listed under **Shipped in this window**. Markers only appear once a GitHub repo is connected and a PR has
merged; PRs outside the selected range are filtered out rather than clamped to the edge.

Two limits worth knowing: the trend is per form, so two NPS surveys give you two charts and no combined number, and the series
itself is not exportable (the response CSV is).

### Responses land in the inbox

Every survey response is also written into your feedback inbox as a feedback item with the source **survey**. You do not have to
turn this on.

That copy carries the answer text as the comment, the score (for example `9 / 10 NPS`) with its benchmark, and a link back to all
responses for the survey. Because it is ordinary feedback, it is embedded and clustered with everything else in the project, so
the four people who complained about slow exports in a survey and the six who reported it through the widget end up in one
cluster. And like any other feedback item, it carries **Create Pull Request**: connect a GitHub repo and Usero writes a first pass
at the fix on a branch and opens a PR you review and merge.

That is the point of running the survey here rather than in a survey tool. Ask 200 users what annoys them most, cluster the
answers, then open a PR against the top cluster.

### Current limits

- A survey publishes to its public link. It does not yet pop up inside your app; in-app rendering through `@usero/sdk` is not
  shipped.
- There are no trigger, delay, frequency, or sampling rules yet, because there is no in-app surface to fire them on.
- NPS, CSAT, and CES are scored. Product/Market Fit and the open question collect answers and show a breakdown without a single
  headline number.

## Next

A form response is feedback like any other in Usero, so it feeds the same loop as the widget and your imported GitHub issues. The
AI clusters duplicate reports, and from a clustered item you can open a GitHub pull request with a first pass at the fix, which
you review and merge yourself. Nothing auto-merges.

- [Feedback widget](/docs/widget): the floating button, for passive in-app feedback alongside your forms
- [GitHub (AI pull requests)](/docs/integrations/github): turn a clustered request into a PR
- [Find your clientId](/docs/find-your-client-id)
