Skip to main content

Creating a Form

Create a form under Messages → Forms → New form in the dashboard, or through the API:

curl -X POST https://posta.example.com/api/v1/workspaces/current/forms \
-H "Authorization: Bearer $POSTA_API_KEY" \
-H "X-Posta-Workspace-Id: 1" \
-H "Content-Type: application/json" \
-d '{
"name": "Contact form",
"allowed_origins": ["https://example.com", "https://www.example.com"],
"notify_emails": ["team@example.com"],
"reply_from": "support@example.com"
}'

The response includes public_key — the unguessable 22-character string that appears in your embed URL.

note

Form configuration endpoints require the admin API-key scope, not write. A form row holds notification recipients and the reply sender, so a content-level key has no business changing it. Reading and replying to messages needs only read/write.

Ingest surface

FieldDefaultNotes
allowed_origins(empty)Blank accepts any origin. Otherwise an exact scheme://host[:port] match.
strict_originfalseReject submissions that send no Origin header at all (curl, server-to-server).
redirect_url(empty)Where a no-JS form lands after submitting.
max_body_bytes65536Clamped to 1 KiB–1 MiB.
max_fields40Clamped to 1–200.
allow_attachmentsfalseWhen off, uploaded files in a multipart post are discarded and the text fields still store.
caution

The origin allowlist is a spam speed bump, not a security boundary. A plain <form method="post"> with the default encoding is a CORS simple request, so the browser sends it without a preflight regardless of the allowlist. Treat the endpoint as public, because it is.

Bot controls

FieldDefaultNotes
honeypot_field_gotchaA non-empty value rejects the submission outright. The generated embed code includes it.
require_noncefalseRequires a short-lived signed token from GET /api/v1/f/{key}/nonce. Single-use, and breaks no-JS forms.
min_fill_seconds3Minimum age of the nonce at submit time. Only applies when require_nonce is on.

Scanning thresholds

FieldDefaultEffect at or above the score
flag_threshold3Stored and notified, marked for review.
quarantine_threshold6Stored, not notified, not dispatched as message.received.
reject_threshold10Stored for audit only. Hidden from the inbox.

Thresholds must satisfy flag ≤ quarantine ≤ reject. Set scan_enabled: false to store everything untouched.

Notifications

FieldDefaultNotes
notify_enabledtrueMaster switch for this form.
notify_emails(empty)Blank notifies workspace owners and admins, honouring each user's Web form messages preference. At most 10 addresses.
notify_modeimmediateimmediate, hourly, daily, or off. Digests are assembled by the message-digest cron job.
notify_on_flaggedtrueWhether flagged messages also generate a notification.

A busy contact form on immediate can send a lot of mail. Switching to hourly is usually what keeps the feature switched on.

Replying

FieldNotes
reply_fromMust be an address on an ownership-verified domain in this workspace. Validated when you save, not just when you send.
reply_from_nameDisplay name used on outgoing replies.

Without reply_from, the dashboard shows the message but disables the reply composer.

Rotating the public key

POST /workspaces/current/forms/{id}/rotate-key issues a new key. Existing embeds stop working immediately — update your site first, or expect a gap.