Skip to the documentation
API

Update a form

A partial update. `document` replaces the draft whole, and a live form keeps showing its published copy until you publish. `settings` is merged field by field and takes effect at once.

PATCH/forms/{id}

Runs the real call on your workspace.

PATCH /forms/{id}

A partial update. document replaces the draft whole, and a live form keeps showing its published copy until you publish. settings is merged field by field and takes effect at once.

Example

Needs forms:write, and emails:send as well when it turns on double opt-in, names a sender or changes the confirmation email. Send the updatedAt you read as expectedUpdatedAt: if someone saved the form in between, your change is refused rather than written over theirs.

curl
curl -X PATCH "$OE/forms/frm_3b9d2e7a1c4f80d56e2a9b14" -H "$AUTH" -H 'content-type: application/json' -d '{  "settings": { "successAction": "redirect", "redirectUrl": "https://acme.com/thanks" },  "expectedUpdatedAt": "2026-09-12T08:00:00.000Z"}'
Response
{  "object": "form",  "id": "frm_3b9d2e7a1c4f80d56e2a9b14",  "name": "Newsletter sign-up",  "description": null,  "status": "live",  "url": "https://nextmail.sh/form/frm_3b9d2e7a1c4f80d56e2a9b14",  "subscribeUrl": "https://api.nextmail.sh/subscribe/frm_3b9d2e7a1c4f80d56e2a9b14",  "audienceIds": ["aud_4c1b8e2a7d9f05c36b4e8a71"],  "doubleOptIn": true,  "hasUnpublishedChanges": true,  "document": {    "fields": [      {        "id": "fld_k2m8q4w9z1x7",        "type": "email",        "key": "email",        "label": "Email",        "placeholder": "you@example.com",        "help": null,        "required": true,        "defaultValue": null,        "options": [],        "mapsTo": "email",        "width": "full",        "minLength": null,        "maxLength": null,        "min": null,        "max": null      }    ],    "copy": {      "title": "Join our list",      "description": "News from us, about once a month.",      "submitLabel": "Subscribe",      "successTitle": "You are on the list",      "successMessage": "Thanks for signing up.",      "pendingTitle": "Check your inbox",      "pendingMessage": "Open the email we sent and confirm to finish.",      "closedTitle": "This form is closed",      "closedMessage": "It is not taking new sign-ups right now.",      "footer": null    },    "style": {      "accent": "#18181b",      "background": "#f4f4f5",      "surface": "#ffffff",      "text": "#18181b",      "font": "system",      "radius": "md",      "width": "normal",      "layout": "card",      "align": "left",      "logoUrl": null    }  },  "publishedDocument": { "fields": ["…"], "copy": { "title": "Join the list" }, "style": { "accent": "#18181b" } },  "settings": {    "audienceIds": ["aud_4c1b8e2a7d9f05c36b4e8a71"],    "doubleOptIn": true,    "senderAddress": "news@acme.com",    "confirmSubject": "Please confirm your subscription",    "confirmMessage": "Confirm your email address to start getting our messages.",    "confirmButton": "Confirm my subscription",    "successAction": "message",    "redirectUrl": null,    "notifyAddresses": ["team@acme.com"]  },  "audiences": [{ "id": "aud_4c1b8e2a7d9f05c36b4e8a71", "name": "Newsletter", "builtin": false }],  "senderIssue": null,  "senderProblem": null,  "stats": { "views": 1840, "submissions": 212, "added": 198, "pending": 14, "lastSubmittedAt": "2026-09-30T16:42:11.000Z" },  "publishedAt": "2026-09-12T08:00:00.000Z",  "createdAt": "2026-09-10T14:20:00.000Z",  "updatedAt": "2026-09-12T08:00:00.000Z"}

A stale expectedUpdatedAt is a 409 version_conflict, and nothing is written. Read the form again and redo the change.

A value of the wrong shape, such as a colour that is not #rrggbb or a redirectUrl that is not an http or https URL, is a 422 invalid_parameter, or unknown_parameter for a key the body does not take. A rule the document or settings break together is a 422 invalid_form. Both name the field in param, such as document.fields.2.key or settings.redirectUrl.

To change one field of the document, read the form, change it and send the whole document back.

Reference