Forms: newsletter boxes, waitlists and lead capture on any page

Embed
Preview of the card
<a href="https://forumulate.com/guides/forms"><img src="https://forumulate.com/guides/_api/public/orgs/046a9bc0-d530-43ae-9bae-af7945008cda/spaces/36aa4df0-1e95-4cef-8ae9-795169441345/pages/345eac1f-558f-4f99-8657-affb64423deb/social-card.svg?v=3e7ae33eec8b" alt="Forms: newsletter boxes, waitlists and lead capture on any page" width="600" height="315"></a>

A form is something a reader fills in and sends: an email box at the foot of a post, a waitlist on a pricing page, a "request a demo" form, a survey. You build the form once, place it on as many pages as you like, and every response lands in one inbox that you can read, export and delete.

Forms work on every reader address: our own (forumulate.com/p/…), a custom domain, and a custom domain mounted under a path (acme.com/docs). They also work in a browser with JavaScript turned off.

Forms are on every plan, the free plan included: every field type, branching, scoring and integration. The one thing that needs a paid plan is taking a payment with a form.

The two halves: the form and the block

The form is stored on the server. It holds the fields, what the button says, what happens after sending, the consent text and who is emailed. A submission is checked against this stored definition and nothing else. Answers to fields the definition does not name are thrown away, so a reader who edits the page's HTML cannot add a field, drop a required, or widen a dropdown.

The block places the form on a page. It names the form by its id and keeps a copy of the fields so the page can render without a second request. On docs pages, docs landing pages, blog posts and changelog entries, the reader draws the stored definition instead of the copy, so an edit in the console shows up on every page that carries the form without republishing. A block whose form was deleted says so. A form that is closed shows that it is not taking responses.

Building a form

There are two places to build one:

  • Console → Forms (/o/{orgId}/forms). Here you create, edit and delete forms, read their responses, and export them.

  • The editor. Type /form, then choose an existing form or "Create a new form" in the block's inspector. "Edit fields and settings" changes the stored form. The inspector tells you this changes the form on every page that carries it.

Field types

Type

Renders as

Checked on the server

text

one-line input

length (min/max), pattern

email

type="email"

is an address; the first one is the DSAR key

textarea

multi-line input

length, pattern

select

dropdown

is one of options

radio

one choice from a group

is one of options

checkbox

a single tick box

required means it must be ticked

number

type="number"

is a number within min/max

url

type="url"

is an http(s):// address

phone

type="tel"

digits, spaces, +()-., at least 5 digits

date

type="date"

a real calendar date, YYYY-MM-DD

hidden

not shown

the page's value, or the field's own value

multiselect

several tick boxes

each one of options

consent

a box that must be ticked

always required; stores the label agreed to

rating

stars, 1 to max (3–10, default 5)

a whole number in range

scale

a row of numbers, min (0 or 1) to max (2–10); NPS is 0–10

a whole number in range; lowLabel/highLabel name the ends

yesno

Yes / No

stored as yes or no, never a translated word

time

type="time"

HH:MM

country

a country picker, names in the reader's language

an ISO 3166-1 code, stored upper case

ranking

put the options in order (up to 12)

each option once; required = all of them

matrix

a grid: one column (options) per row (rows, up to 20)

listed answers only; required = every row

address

street, line 2, town, region, postcode, country

required = a street and a town

statement

a heading (label) and text (help) between questions

never answered

group

a titled group of the questions under it

never answered

page

a page break; its label titles the next page

never answered

Three more types, file, signature (a drawn signature, stored as a PNG) and video (a recorded or picked video or voice answer, mp4 or webm), take uploads. They exist only on the forms that have an upload door, the contact page and the careers apply form (Contact spaces). A page form refuses them with a 422, because a control that takes a file and sends nothing would be worse than none.

Every answer is stored as text or a list of text, so the inbox, the CSV, the webhook and a data-protection export read the new kinds with no new shape: a ranking is the ordered list, a matrix one Row: Column line per row, an address its lines.

Every field has a name. It is the key in submissions, the CSV column and the API, and it must be lower-case letters, digits and _, starting with a letter, at most 40 characters, and unique within the form. Names starting with _ are reserved for the plumbing below. Visible fields also need a label. Optional settings are placeholder, help (a line under the control), required, options (for select and radio), min/max (the value range for numbers, the length range for text) and pattern. Later settings: value (the starting answer, or a hidden field's value), prefill, showIf, rows (a matrix), optionImages (a picture per option on a radio or multiselect, loaded through our image proxy), points (scoring) and hideWhenKnown, all described below.

A pattern is a regular expression that the whole answer must match. Because your pattern runs against a stranger's input on our servers, it is limited to 200 characters and only ever runs against the first 500 characters of an answer. Patterns with nested quantifiers like (a+)+ are refused, and so are backreferences, because both can make matching take forever.

The limits on a form's definition:

What

Limit

Form name

1–120 characters

Fields

1–30

Field name

40 characters

label, placeholder

200 characters each

help

500 characters

options

50 per field, each 1–120 characters, unique

pattern

200 characters

A hidden field's value

500 characters

Submit button label

60 characters

Thank-you message

1,000 characters

Redirect

2,048 characters

Consent text

1–500 characters

Notify addresses

10

After sending

  • Thank-you message. Shown in place of the form. The default is "Thanks — your response was sent."

  • Redirect. Send the reader somewhere instead. This is either a page on your own site (/thanks) or an absolute https:// address. A plain http:// address is refused with a 422. javascript:, data:, //host and backslash tricks are refused when you save.

  • Layout. stacked puts one field per row. inline puts the fields and the button on one row, which is the usual newsletter box.

Pages, branching and prefilled answers

Pages. A page field is a page break: every field after it is on a new page, titled by the break's label, with Continue and Back buttons and a progress bar. Enter moves on. Without JavaScript the form shows every page at once, so it still works.

Branching. Any field, and any page break, can carry showIf, a test on an answer given above it:

{ "name": "team_size", "type": "number", "label": "How many people?",
  "showIf": { "field": "plan", "op": "equals", "value": "Business" } }

op is one of equals, notEquals, includes (a ticked option of a multiselect), answered, empty, gt and lt. Every op except answered and empty needs a value. A showIf on a page break skips the whole page. The condition must name a field above it (a later field, or a typo, is a 422 naming it), and a field cannot depend on its own answer.

The reader and the API evaluate the same rule. A question the reader never saw is never required and its answer is never stored, even if someone posts one, so a hidden question can never be the reason a send is refused.

Prefilled answers. Mark a field prefill: true and ?<name>= on the page's address fills it in: /pricing?plan=Business opens the form with the plan chosen. Hidden fields always take their value from the address, which is how you capture a campaign tag. A field's value is its starting answer. hideWhenKnown: true (HubSpot's "smart field") hides a question whose answer the address already supplied.

How a form behaves

behavior holds the settings that change how a form runs rather than what it asks. Page forms and the contact form share it (forms.behavior and contactForm.behavior), and the editor shows it as the Behaviour box.

Key

Default

What it does

flow

all

one asks one question at a time (Typeform). Each question gets its own screen; a statement or group stays with the question after it; choosing an answer or pressing Enter moves on.

verifyEmail

off

Verify the email address with a code before sending. See below.

blockedDomains

none

Refuse addresses at these domains, up to 200 (competitor.com, mailinator.com). Subdomains are included. The send is refused with a 422 on the email field. Never shown to readers.

savePartials

off

Keep partial answers from people who start and do not send. Kept 90 days, then swept.

scoreEndings

none

Up to 10 thank-yous chosen by the response's score. See Scoring.

integrations

none

Up to 5 destinations every response is appended to. See Sending responses to your own tables.

The reader is told flow, verifyEmail, savePartials and scoreEndings. It is never told the blocked domains or anything about the integrations.

Verified email. With verifyEmail on, the form asks the reader for a 6-digit code mailed to the address they typed (email template forms.verify-code, through your organization's mail provider) before it will send. The code is valid for 10 minutes and allows 5 tries. Codes are limited to 4 per address and 20 per network per hour. A confirmed code becomes a token, bound to this form and this address, that the send carries as _emailToken. It lasts an hour. A send without a valid token is refused with a 422 (_emailToken: ["unverified"]).

POST /public/orgs/{orgId}/form-flow/form/{formId}/verify-email           { "email": "…" }
POST /public/orgs/{orgId}/form-flow/form/{formId}/verify-email/confirm   { "email": "…", "code": "123456" } → { "token": "…" }

Scoring

Give answers points and the API adds them up (Typeform's and Tally's calculations). points is set per field:

  • on a select, radio, multiselect: points per option, { "Enterprise": 10, "Startup": 3 };

  • on a yesno: { "yes": 5 }; on a checkbox: { "on": 2 };

  • on a number, rating or scale: points per unit, { "*": 2 }.

Each value is between -1000 and 1000. The score is computed by the API on the checked answers, never taken from the browser, and stored with the response (the inbox shows it). {score} in the thank-you message prints it. behavior.scoreEndings picks a thank-you by range: the first ending whose min/max (both inclusive, either may be left open) holds the score supplies its title, message and redirectUrl.

"behavior": { "scoreEndings": [
  { "min": 20, "title": "You qualify", "message": "Score {score}. Book a call below.", "redirectUrl": "/book" },
  { "max": 19, "message": "Thanks, we will be in touch." }
] }

A form with no scored field has no score and says nothing about one.

Taking a payment with a form

Taking payments is on paid plans only (Starter and up), to keep fraud off the platform; the money goes through your own Stripe and we take no cut. On the free plan the editor shows an upgrade prompt in place of the switch, and the API answers 402 plan.required to a form created or updated with a payment. A paid form that existed before 20 October 2026 is grandfathered and keeps charging on any plan. See Which plans can take payments.

payment charges the reader when they send (Typeform's and Tally's payment step). In the editor it is Take a payment with each response.

Key

What it does

label

What the payment is for, as Stripe shows it on the checkout and receipt. Required.

currency

Three letters, lower case. Default usd.

amountCents

A fixed price, at least 50 (0.50).

amountField

Or: the amount from one of the form's own answers (a donation box, a choice of tiers), read in whole units ("25" is 25.00).

minCents, maxCents

Bounds for amountField. The floor is 50 if you set none.

Set exactly one of amountCents and amountField. The amount always comes from the stored form and the answers the API has already checked, never from an amount the request names. An amount outside the bounds is a 422 on the field.

What happens on send:

  1. The answers are checked as usual and filed as a response with payment status pending.

  2. The API opens a Stripe Checkout on your organization's own Stripe account and answers with its checkoutUrl. The reader is sent there (the reader script only follows a checkout.stripe.com address). A paid form must be sent from a page, because Stripe needs a page to send the reader back to.

  3. When Stripe reports the payment, the response becomes paid, and only then is it announced: the notification email, the form.submitted webhook, the form.paid webhook and your integrations. An unpaid response never alerts anyone.

  4. A refund, from the console or from Stripe, marks the response refunded.

Paid responses also appear as orders in Settings → Shop → Orders, where you can refund them. The promotion-code, Stripe Tax and ship-to switches there apply to form checkouts too.

What you need to set up

A paid form needs your organization's Stripe account connected under Settings → Payments, with sales switched on and the webhook registered. That is the same connection every paid feature uses: Payments and the shop. Until it is, sending a paid form answers 409 ("This organization does not take card payments yet"). The answers are still kept as an unpaid response (and an order awaiting payment), and nobody is notified about them. Test the form before you publish it.

Insights and partial answers

Every form, page form or contact form, counts views, starts and sends per day, and shows where people stop: the drop-off per question (the last question answered before they left). The panel is on the form's page in Console → Forms, and in the contact space's Contact form settings.

With behavior.savePartials on, the answers a reader typed before leaving are kept as partials you can read and delete. They are deleted after 90 days. Without it, only the counts are kept.

GET    /orgs/{orgId}/form-insights/form/{formId}                      forms.read
DELETE /orgs/{orgId}/form-insights/form/{formId}/partials/{partialId} forms.manage

For a contact space use contact/{spaceId} instead of form/{formId}; that needs space.read (and space.update to delete). The reader reports its progress to POST /public/orgs/{orgId}/form-flow/{kind}/{id}/progress, a counter only, throttled per network.

Sending responses to your own tables

behavior.integrations appends every response to a table you own, using your own token (Send every response to in the Behaviour box).

kind

Needs

Token

airtable

base (appXXXX…), table (name or tbl… id)

an Airtable personal access token

notion

database (the database id)

a Notion internal-integration token

sheets

spreadsheet (the sheet id), sheet (the tab)

a Google service-account key (the JSON); share the sheet with the service account's address

The token is write-only: it is stored encrypted, read back only as tokenSet: true, and a save without token keeps the stored one. enabled: false pauses a destination. Delivery happens after the response is stored, so a failing destination never loses a response. The insights panel shows the last delivery to each destination, with its error. Spam is never delivered, and a paid form's response is delivered once it is paid.

What you need to set up

Each destination needs your own account and token with that service, entered in the form's Behaviour box. Nothing is sent anywhere until you add one.

When a form stops taking responses

A page form is open or closed: status: "closed" stops it, the block shows that it is not taking responses, and a send answers 409.

The contact form of a contact space can also close itself (PATCH /orgs/{orgId}/spaces/{spaceId}/contact/form, When the form closes in its settings):

Key

Default

What it does

closesAt

none

Closed from this moment (ISO 8601 with an offset).

maxSubmissions

none

Closed once this many messages have arrived (1 to 1,000,000). Spam does not count, so a bot flood cannot close a sales form.

maxPerSender

none

At most this many messages from one address in 24 hours (1–100).

closedMessage

the reader's own words

What the page and every contact-form block show instead of the form (500 characters, translatable).

All three are checked by the API against the database's own clock and counts, never the reader's clock. A closed form answers 410 with your closedMessage. A sender over maxPerSender answers 429 and is told to try again tomorrow.

The conversion event

When a form sends, the page fires a forumulate:form-sent DOM event (detail: form, kind (form or contact), topic, reference) and, if your own tag manager put a dataLayer on the page, pushes { event: "forumulate_form_submit", … } to it. We load no analytics script and send nothing to a third party. A contact space also has a /thanks address for goals that count URLs (Contact spaces).

If your form collects personal data, set consent text, for example "I agree to the privacy policy and to being contacted." The form then shows a checkbox with that text and will not send until it is ticked. Each submission stores the exact text the reader agreed to and when they agreed. The console shows both, and the CSV export includes them in consent_text and consent_at.

Spam and abuse

Every submission passes these checks, in this order:

  1. A closed form answers 409.

  2. Size. A request body over 64 KB answers 413.

  3. A honeypot. Every form has a hidden _hp field that people never see and bots fill in. A filled honeypot gets a success reply, and nothing is stored.

  4. A rate limit. Each network gets 10 submissions per form every 10 minutes. On top of that there is a per-address throttle on the endpoint. Going over either answers 429.

  5. Your screening list, by network. A network you have screened out (Settings → Screening) gets the same silent success as the honeypot.

  6. Your captcha, if you configured one (Turnstile or hCaptcha, using your own keys). The page shows the widget automatically. A reader without JavaScript cannot solve it, so if you require a captcha, those readers cannot send.

  7. Field validation against the stored definition, and the consent box if the form has consent text. A failure answers 422 and details names each field that is wrong.

  8. Your screening list, by email. If the submitter's email address is screened out, the same silent success. This runs after validation because the address is one of the answers. Then the form's own rules: an address at one of its blockedDomains is a 422, and with verifyEmail on, a send without a valid code token is a 422 (How a form behaves).

  9. Content heuristics: a flood of links, HTML or BBCode links, or a long run of text with no spaces. These do not reject the submission, because a false positive would lose a real lead without anyone knowing. The submission is stored and flagged as spam, no email is sent, and no webhook fires. You can review it under Flagged as spam and mark it "Not spam".

A submission is limited to 64 KB, 5,000 characters per answer and 40,000 characters in total.

Notifications

  • Email. Each response that is not spam is emailed to the form's notify addresses, or to your organization's support address if the form has none. Mail goes through your organization's own mail provider, the same one the contact page uses.

  • Webhook. Subscribe an endpoint to form.submitted (Webhooks). The event's data carries exactly formId, title and formName (both the form's name), submissionId, the answers in data, the submitter's email, pageUrl and createdAt. It is not sent for spam.

Without JavaScript, and with it

The block renders a real HTML form that posts to your own address: /api/public/orgs/{orgId}/forms/{formId}/submissions on our domain, and /<mount>/_api/public/… on a custom domain. The browser's own validation (required, type="email", pattern, min) runs before anything is sent.

  • Without JavaScript, the API answers a plain form post with a 303 back to the page you came from, plus a fragment: #fm-{formId}-sent or #fm-{formId}-error. The page shows the matching message using CSS alone. If the form has an external redirect, the API returns a short page that moves the reader on, because browsers apply the page's form-action policy to redirects too.

  • With JavaScript, the reader posts JSON instead. It puts each error next to the field it belongs to, marks that field aria-invalid and moves focus to it. On success it shows the thank-you message in place or follows the redirect.

The reader's Content-Security-Policy now carries form-action 'self' (plus our own origin). Every form the renderer emits posts to the same origin it was served from, so this holds on every address.

The API

Public. No credentials are needed:

GET  /public/orgs/{orgId}/forms/{formId}               the public definition (no notify addresses)
POST /public/orgs/{orgId}/forms/{formId}/submissions   send a form

A submission is a flat object with each answer under its field's name, plus these reserved keys:

Key

Meaning

_hp

the honeypot; leave it empty

_consent

true / "on" when the form has consent text

_captcha

your captcha's token, when you configured one

_return

no-JS only: the page to return to (same origin as the post)

_session

the browser's id for this pass, so insights count the send as that pass's end

_emailToken

the verified-email token, when the form verifies addresses

POST /public/orgs/{orgId}/forms/{formId}/submissions
{ "email": "ann@example.com", "role": "PM", "_consent": true }

201 { "ok": true, "submission": { "id": "…", "createdAt": "…" },
      "successMessage": "Thanks — you are on the list.", "redirectUrl": null }

A scored form adds score; a paid form adds checkoutUrl, the Stripe page to send the reader to.

Owner side. These need forms.read or forms.manage:

GET    /orgs/{orgId}/forms                                        forms.read    list, with response counts
POST   /orgs/{orgId}/forms                                        forms.manage  create
GET    /orgs/{orgId}/forms/{formId}                               forms.read
PATCH  /orgs/{orgId}/forms/{formId}                               forms.manage  any subset of the fields
DELETE /orgs/{orgId}/forms/{formId}                               forms.manage  also deletes its submissions
GET    /orgs/{orgId}/forms/{formId}/submissions?spam=             forms.read    paged once you send a version
GET    /orgs/{orgId}/forms/{formId}/submissions.csv               forms.read    CSV export
PATCH  /orgs/{orgId}/forms/{formId}/submissions/{submissionId}    forms.manage  { "spam": true|false }
DELETE /orgs/{orgId}/forms/{formId}/submissions/{submissionId}    forms.manage
GET    /orgs/{orgId}/form-submissions?email=                      forms.read    a person's data, across all forms
DELETE /orgs/{orgId}/form-submissions?email=                      forms.manage  erase it

The CSV has one column per current field, then any older field that still has answers, then submitted_at, spam, consent_text, consent_at, page_url and id. A cell that starts with =, +, - or @ is prefixed with an apostrophe, so a spreadsheet cannot run a stranger's text as a formula.

Who can do what

forms.read lets someone see forms and the submissions they collected. Submissions are personal data a stranger handed over, so this is its own capability rather than part of space.read. forms.manage lets someone create, edit and delete forms and submissions. Owner and Admin hold both. So do Maintainer and Editor, because they place forms on the docs and blog pages they own. Member and Guest hold neither. For tokens, the scopes are forms:read and forms:manage (API tokens and scopes).

Data-protection requests

Submissions are tenant data and are deleted with your organization. For a single person:

  • Access: GET /orgs/{orgId}/form-submissions?email=person@example.com returns every submission, across every form, whose first email answer is that address.

  • Erasure: DELETE on the same address deletes them all and returns the count. The console's form page has a "Data requests" box that does the same thing.

  • One submission: DELETE …/submissions/{submissionId}.

Matching uses the first email field of each form. A form with no email field cannot be searched this way, so delete those submissions one at a time.

In markdown

The block is exported as a single encoded directive, because field labels are data, not prose. It survives a round trip through git sync and the .md export:

:::form{id="6f1c2a5e-0b8e-4a5b-9f60-3a7b2c1d4e5f" data="%7B%22fields%22…"}:::

A contact space's form on any page

The Contact form block (slash menu: Contact form) places a CONTACT space's form on any page — "Talk to sales" under a pricing table, "Still stuck?" at the foot of a guide. It names the space and nothing else: the questions, the topics, the button and the thank-you are read live from that space when the page renders, so editing the contact form in its Settings fold updates every page carrying the block. Optionally fix the topic (the message is routed as that topic and only that topic's questions are asked) and override the heading, description and button label.

Messages land in the contact space's inbox exactly as if sent from the contact page: topic routing and assignment, auto-replies, staff mail and the contact.submitted webhook all apply. Like a form block it works with no JavaScript (it posts to POST /public/orgs/{orgId}/spaces/{spaceId}/contact/submissions and is answered with a 303 back to the page), and with scripts on it shows only the questions the chosen topic asks. In markdown it is :::contactform{space="<spaceId>" data="…"}:::.

The block carries the same booking step, work-email hint, closed state and theme.contact.form look as the contact page, its built-in words follow the page's language, and your translations reach it. Upload questions (file, signature, video) cannot be answered from a block: it shows a note linking to the contact page. The API does not require a file question from a block send, but a required signature or video question is still required, so keep those optional on a contact form you place as a block.

A form block placed in a contact space's own intro is live too.

A contact inbox: folders, spam, CRM and retention

A contact space's messages are filed in folders — Inbox (unread and read), Archived and Spam — set with PATCH /orgs/{orgId}/spaces/{spaceId}/contact/{id}/status (unread read archived spam) and listed with ?folder=inbox|unread|archived|spam|all. Without folder the list is everything except Spam.

Each message is scored on arrival: too many links, pasted markup, keyword stuffing, known spam phrases, a link where a name goes, and the organization's screening list (a screened address, domain or link puts it straight over the line). A message over the line is kept in Spam — the sender gets the same answer as anyone — but nobody is alerted, no auto-reply goes out and no webhook or CRM lead fires. "Not spam" moves it back. Turn scoring off with contactForm.spamFilter: false. contactForm.workEmailOnly: true refuses consumer mailboxes (gmail.com, outlook.com…) with a 422 on email, which the form shows beside the address.

CRM handoff. Install the HubSpot integration (a HubSpot form's submit URL) or the Salesforce one (your Web-to-Lead URL with oid) and every contact.submitted becomes a HubSpot form submission or a Salesforce lead: the name split into first and last, the message (with its topic and subject) as the message/description, and each question under its own name — name a question company, phone or website and it fills that CRM property.

Booking. contactForm.bookingUrl (or a topic's booking) is a Cal.com or Calendly page framed under the thank-you, with the sender's name and address filled in.

Retention and data requests. contactForm.retentionDays deletes messages older than that many days, nightly. For one person, GET /orgs/{orgId}/contact-submissions?email=… returns everything that address sent the organization across every contact space, and DELETE on the same address erases it (replies included) — both need org.manage_settings. The inbox's "Erase sender's data" does the same.

Languages. contactForm.translations holds the form's words per reader locale (fr, pt-BR): topic names and lines, question labels, help and placeholders, the button and the thank-you. Readers in that language see them; what is posted and stored stays the original, so routing and the inbox keep one vocabulary.

In the API

Every operation below is also reachable from the other surfaces without a dedicated command. forumulate api <Operation> calls it from the CLI, the JavaScript SDK has a typed method for each one (the last column names it; the Python and Go clients are generated from the same spec), and an agent reaches the organization-scoped ones through the MCP call_api tool. Where a CLI command or MCP tool exists too, the last column names it. Operation names link to the API reference.

Contact Block

Operation

Request

What it does

Shortcuts

ContactBlock_submit

POST /public/orgs/{orgId}/spaces/{spaceId}/contact/submissions

Create a submission in a space (contact)

client.forms.contactBlock.submit()

Contact Privacy

Operation

Request

What it does

Shortcuts

ContactPrivacy_byEmail

GET /orgs/{orgId}/contact-submissions

List contact submissions in an organization

client.forms.contactPrivacy.byEmail()

ContactPrivacy_eraseByEmail

DELETE /orgs/{orgId}/contact-submissions

Delete contact submissions in an organization

client.forms.contactPrivacy.eraseByEmail()

Forms

Operation

Request

What it does

Shortcuts

Forms_list

GET /orgs/{orgId}/forms

List forms in an organization

forumulate forms list<br>client.forms.list()

Forms_create

POST /orgs/{orgId}/forms

Create a form in an organization

forumulate forms create<br>client.forms.create()

Forms_get

GET /orgs/{orgId}/forms/{formId}

Get the form

forumulate forms get<br>client.forms.get()

Forms_update

PATCH /orgs/{orgId}/forms/{formId}

Update the form

forumulate forms update<br>client.forms.update()

Forms_remove

DELETE /orgs/{orgId}/forms/{formId}

Delete the form

forumulate forms delete<br>client.forms.remove()

Forms_submissions

GET /orgs/{orgId}/forms/{formId}/submissions

List submissions in an organization

forumulate forms submissions<br>client.forms.submissions()

Forms_exportCsv

GET /orgs/{orgId}/forms/{formId}/submissions.csv

File of submissions

forumulate forms export<br>client.forms.exportCsv()

Forms_setSpam

PATCH /orgs/{orgId}/forms/{formId}/submissions/{submissionId}

Update the submission (forms)

forumulate forms spam<br>client.forms.setSpam()

Forms_deleteSubmission

DELETE /orgs/{orgId}/forms/{formId}/submissions/{submissionId}

Delete the submission

forumulate forms submissions delete<br>client.forms.deleteSubmission()

Form Submissions By Email

Operation

Request

What it does

Shortcuts

FormSubmissionsByEmail_byEmail

GET /orgs/{orgId}/form-submissions

List form submissions in an organization

client.forms.submissionsByEmail.byEmail()

FormSubmissionsByEmail_eraseByEmail

DELETE /orgs/{orgId}/form-submissions

Delete form submissions in an organization

client.forms.submissionsByEmail.eraseByEmail()

Public Form Flow

Operation

Request

What it does

Shortcuts

PublicFormFlow_progress

POST /public/orgs/{orgId}/form-flow/{kind}/{id}/progress

Create the progress in an organization

client.forms.publicFlow.progress()

PublicFormFlow_sendCode

POST /public/orgs/{orgId}/form-flow/{kind}/{id}/verify-email

Verify email in an organization

client.forms.publicFlow.sendCode()

PublicFormFlow_confirm

POST /public/orgs/{orgId}/form-flow/{kind}/{id}/verify-email/confirm

Confirm the verify email

client.forms.publicFlow.confirm()

Public Forms

Operation

Request

What it does

Shortcuts

PublicForms_definition

GET /public/orgs/{orgId}/forms/{formId}

Get the form (public reader)

client.forms.public.definition()

PublicForms_submit

POST /public/orgs/{orgId}/forms/{formId}/submissions

Create a submission in an organization

client.forms.public.submit()

The Forumulate teamUpdated