Skip to content

The config JSON

A form is defined by a single JSON document. It holds the fields, the validation rules, the emails to send, the webhooks to call, and a few settings. Everything about how a form behaves lives here, and it is stored as one value so it can never drift out of sync with itself.

You edit it either as raw JSON or through the structured editor. Both are views of the same document.

{
"fields": [ /* the inputs the form accepts */ ],
"sections": [ /* optional: group the fields into cards or steps */ ],
"email": { /* notifications and autoresponders */ },
"webhooks": [ /* endpoints to POST each submission to */ ],
"settings": { /* submit label, thank-you screen, layout, and more */ }
}

Only fields is required. Everything else is optional. Note that email is a single object, not an array, and webhooks is an array because a form can call several endpoints.

Here is a contact form that emails you, sends the visitor a confirmation, and posts to a webhook:

{
"fields": [
{ "name": "name", "type": "text", "label": "Your name", "rules": [{ "r": "required" }] },
{ "name": "email", "type": "email", "label": "Email", "rules": [{ "r": "required" }, { "r": "email" }] },
{ "name": "phone", "type": "tel", "label": "Phone", "rules": [{ "r": "tel" }] },
{ "name": "message", "type": "textarea", "label": "Message", "rules": [{ "r": "required" }, { "r": "max:2000" }] },
{ "name": "consent", "type": "checkbox", "label": "I agree to be contacted", "rules": [{ "r": "required" }], "consent": true }
],
"email": {
"notification": {
"enabled": true,
"to": ["sales@example.com"]
},
"confirmation": {
"enabled": true,
"toField": "email"
}
},
"webhooks": [
{ "url": "https://hooks.example.com/octaforms", "active": true, "secret": "a-long-random-secret-at-least-32-chars" }
],
"settings": {
"submitLabel": "Send message",
"thankYouTitle": "Thanks!",
"thankYouText": "We will get back to you shortly."
}
}

An array of the inputs your form accepts. Each field has at least a name and a type, usually a label, and optional validation rules. Field names must be unique.

{ "name": "email", "type": "text", "label": "Email", "rules": [{ "r": "required" }, { "r": "email" }] }

Every type and every option is covered in Field types. The rule format is covered in Validation rules.

Long forms read better in groups. A section gives a group of fields a title, an optional short text above the fields and an optional note below them:

"sections": [
{ "title": "Contact details", "text": "How we can reach you.", "fields": ["name", "email", "phone"] },
{ "title": "Your tax return", "fields": ["business", "tax_number", "documents"], "note": "Leave out anything you are not sure about." }
]

A form has three ways to show its fields:

Config What the visitor sees
no sections every field one under another, as before
sections each section as a card, one under another
sections and settings.layout set to "steps" one section at a time, with Back and Next buttons and “Step 2 of 5”

Sections only change how the form looks. Submissions, webhooks and the CSV export keep the plain list of fields. The notification email and the submission screen follow your sections, with the section titles as headings.

  • A section lists field names in the order to show them. The title, text and note are all optional, so a card can have just a short text and no heading.
  • A field can be in one section only.
  • A field you did not put in any section still shows, in a final card without a title, and the editor warns you about it. In the email and on the submission screen those fields come under “Other details”.
  • A section whose fields all wait for an answer (showIf) stays hidden until one of them appears. In steps it is skipped and not counted in “Step 2 of 5”. A section with no fields, only a text, always shows.

In steps, Next checks the fields of the current step on the server before the visitor moves on, so they see the same messages the final submit would give. Without JavaScript every section shows one under another. In the editor, sections are headings in the Fields list, and the steps switch is in the Settings tab.

Controls what gets sent when a submission arrives: a notification to you, and an optional confirmation to the person who submitted. Full details, including subjects, bodies and attachments, are on the Emails page.

An array of endpoints that receive a signed POST for every submission. Each active webhook needs an https URL and a secret of at least 32 characters, used to sign the payload. See Webhooks.

Small tweaks to how the form behaves:

Setting Effect
submitLabel Text on the submit button. Defaults to “Send”.
layout "steps" shows one section at a time. Leave it out to show sections as cards one under another. See sections.
thankYouTitle and thankYouText The success message shown after submitting. Both fall back to a translated default if left empty.
redirectUrl If set, the visitor is redirected here after a successful submit instead of seeing the thank-you message. Takes priority over the thank-you text. The address can carry answers from the form, see Thank-you page address.
requireToken Defaults to true. Set to false only as a last resort if a security plugin blocks the token endpoint. Honeypot and rate limiting stay active either way. See Anti-spam.

A thank-you page often needs something from the submission, such as the package someone picked or a number for a conversion tag. Put a field name in braces and OctaForms fills it in:

"redirectUrl": "https://example.com/thanks?package={package}"

After someone picks Premium & VIP, they land on https://example.com/thanks?package=Premium%20%26%20VIP&id=123.

  • {field_name} is that field’s answer. {lead_id} is the submission number and {form_id} the form’s ID. These two win over a field with the same name.
  • Every value is encoded, so an &, # or / typed into the form stays part of the value and cannot change where the visitor goes.
  • A checkbox group gives the ticked options separated by commas. A single checkbox gives 1 when ticked.
  • A name that matches no field, an empty answer and a field hidden by showIf all give an empty value. Renaming a field therefore never puts {old_name} in the address bar.
  • id=<submission number> is always added at the end, unless your address sets id itself.

The config is validated every time you save. Problems come in two kinds:

  • Errors block the save. For example, a select field with no options, or an active webhook with a weak secret.
  • Warnings let the save through but flag a risk. For example, a honeypot field with a name browsers tend to autofill.

Each problem is tied to the exact place it came from, so the editor can point you straight at it. On save, the previous version is kept, so the raw JSON editor offers a one-step undo.