Skip to content

Forms ​

A form is a hosted endpoint you point ordinary HTML at. Its submission route is the one place in the public API with no API key — HTML forms post there directly, and it is rate-limited instead.

Every submission emits form.submitted, which is the usual trigger for an automation.

The form object ​

json
{
  "id": "…",
  "name": "Contact form",
  "fields": [
    { "key": "email", "label": "Email", "type": "email", "required": true },
    { "key": "message", "label": "Message", "type": "textarea", "required": false }
  ],
  "is_active": true,
  "redirect_url": "https://example.com/thanks",
  "submission_count": 42,
  "last_submission_at": "…",
  "created_at": "…"
}

Create and update ​

POST   /v1/forms
GET    /v1/forms
GET    /v1/forms/{id}
PATCH  /v1/forms/{id}
DELETE /v1/forms/{id}
FieldRequiredNotes
nameyesUp to 255 characters.
fieldsnoUp to 50. See below.
redirect_urlnoAbsolute http(s). An explicit null or empty string clears it.
is_activeupdate onlyfalse makes the submission endpoint reject.

A PATCH must carry at least one of name, fields, is_active, redirect_url.

Fields ​

A field is { "key", "label", "type", "required" }, or a bare string used as the key. key is up to 100 characters and unique within the form (case-insensitively); label up to 200 and defaults to the key. type is one of text, email, tel, url, number, date, textarea, checkbox.

Declaring fields is optional

An empty fields list means the form captures whatever is posted. Declared fields drive the embed snippets and how values are labelled in stored submissions; they are not a schema the endpoint enforces. Markup already live on your site keeps working after you change them.

Deleting a form removes every submission it received.

The submission endpoint ​

POST /v1/forms/{id}/submissions

Public — no API key. Rate-limited, and scoped by the form id in the path.

html
<form action="https://api.example.com/v1/forms/FORM_ID/submissions" method="post">
  <input type="email" name="email" required>
  <textarea name="message"></textarea>
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" hidden>
  <button type="submit">Send</button>
</form>

What it accepts ​

Form encodings only — application/x-www-form-urlencoded or multipart/form-data, which is what a browser sends. JSON returns 415 notifyzr.form.error.unsupported_media_type.

What it answers ​

The caller acceptsResponse
text/html (a browser)303 to the form's redirect_url, or to the JSON response when none is set
Anything else201 with the usual JSON envelope

Limits ​

100 fields per submission, 200 characters per label, 5,000 per value, and 256 KB across all of them combined. Values are trimmed, stripped of control characters and length-capped on the way in; escaping happens at render time.

Reserved names ​

Field names starting with _ are reserved and never stored as content. One of them is load-bearing:

_gotcha is a spam trap

A bot that fills every input fills the hidden _gotcha too. When it arrives filled, the endpoint answers 201 as usual and stores nothing. Keep it in your markup and keep it hidden — and leave it out of any request you make yourself, or your test submission will vanish.

An inactive form's endpoint answers 404, the same as an unknown id.

Submissions ​

GET /v1/forms/{id}/submissions

Paginated (page, per_page, default 25, max 100).

json
{
  "submissions": [{
    "id": "…",
    "data": [
      { "label": "email", "value": "someone@example.com" },
      { "label": "message", "value": "Hello" }
    ],
    "remote_ip": "203.0.113.10",
    "created_at": "…"
  }],
  "pagination": { "page": 1, "per_page": 25, "total": 1, "total_pages": 1 }
}

data is an ordered list, not an object

Submissions keep the labels and the order the visitor's browser sent, and a list allows duplicate labels — which an object would silently collapse. Read it by iterating, not by key lookup.

Released under the Apache 2.0 License.