---
title: "Limit responses"
description: "Keep it to one response per browser, contact answer, or signed-in person, optionally scoped by a field value."
group: "Respondents & data"
order: 3
type: "feature"
keywords:
  - "responseLimit"
  - "duplicate"
  - "browser"
  - "field"
  - "identify"
  - "onRepeat"
updated: "2026-07-14"
---

Limit responses controls three things: who counts as the same person, what one response is scoped to, and what happens on a repeat.

## Choose how to recognize the person

| Mode | Schema value | Trust level | Good fit |
| --- | --- | --- | --- |
| Browser | `by: "browser"` | Device-local | Polls and article ratings |
| Email or phone field | `by: "field"` | Unverified self-claim | Reducing accidental duplicate submissions |
| Signed-in person | `by: "identify"` | Claimed or server-verified | Product flows with account context |

Browser limits are the easiest to bypass. Clearing site data, switching browsers or devices, or opening a private window creates a new browser identity — and a typed email or phone can be changed too. When a repeat has to be tied to an account, use a server-verified signed-in identity.

## In the dashboard

Open the form's **Respondents** settings and choose **Limit responses**.

1. Select browser, an always-visible required email or phone field, or signed-in person.
2. Optionally add a scalar scope field for rules like one response per person per article.
3. Keep the first answer, or let a verified signed-in person update it in place.
4. Submit twice with the same identity and scope before publishing the workflow.

A field used for identity or scope can't be conditionally hidden. A scope value has to be a single text or number — not an array, object, boolean, file, or signature.

## In code

```ts
const preference = defineForm({
  id: "article-vote",
  pages: [{
    id: "main",
    blocks: [
      { id: "article_id", kind: "hidden", label: "Article", paramName: "article" },
      {
        id: "helpful",
        kind: "select",
        label: "Was this helpful?",
        options: [
          { id: "yes", label: "Yes" },
          { id: "no", label: "No" },
        ],
      },
    ],
  }],
  settings: {
    responseLimit: {
      by: "identify",
      scopeField: "article_id",
      onRepeat: "update",
    },
  },
});
```

Pass `respondent={{ id, email, hash }}` to the renderer. Generate `hash` on your server with the workspace identity secret.

## Repeat behavior

`keep` leaves the first response standing and returns an already-answered state on a repeat. `update` edits the same response ID, anchors it to the current schema version, and emits `response.updated`.

Update in place only works for a verified `identify` respondent. A field-based self-claim or an unverified page-supplied ID can create the first response but can't overwrite it. And a person-keyed form rejects any submission it can't attribute to a person.

## Plan status

Response limits are available on every plan, Free included. They cut down repeat submissions — they aren't authentication, access control, or proof that an answer is honest.

## Related

- [Respondents and identity](/docs/respondents): Sign the stable account ID on your server.
- [You have already responded](/docs/troubleshooting/already-responded): Explain the respondent-facing repeat state.
- [Webhooks](/docs/webhooks): Handle both created and updated response events.
