Forms & Submissions
Build contact / signup / inquiry forms in the admin, embed them on any frontend, and triage incoming submissions from one inbox.
Form management routes
| Route | Method | Purpose |
|---|---|---|
/admin/forms | GET | List forms |
/admin/forms/create | GET | New form |
/admin/forms | POST | Create form |
/admin/forms/{form}/edit | GET | Edit form fields |
/admin/forms/{form} | PUT | Update form |
/admin/forms/{form} | DELETE | Soft delete |
/admin/forms/{form}/toggle-active | PATCH | Activate / pause submissions |
Form attributes
- name — internal name
- slug — used in the public submit endpoint
- fields — JSON definition: label, name, type, required, validation
- notify_emails — comma-separated recipients
- success_message — confirmation text
- spam_protection — honeypot field name, optional reCAPTCHA secret
- redirect_url — optional URL to redirect to after success
- is_active — when false, the submit endpoint returns 410
Submission inbox
| Route | Method | Purpose |
|---|---|---|
/admin/forms/{form}/submissions | GET | List submissions (status, date range, search) |
/admin/forms/{form}/submissions/export | GET | CSV export of all submissions |
/admin/forms/{form}/submissions/bulk | POST | Bulk mark as read / spam / delete |
/admin/forms/{form}/submissions/{submission} | GET | Submission detail |
/admin/forms/{form}/submissions/{submission}/mark-read | PATCH | Mark as read |
/admin/forms/{form}/submissions/{submission}/mark-unread | PATCH | Mark as unread |
/admin/forms/{form}/submissions/{submission}/mark-spam | PATCH | Mark as spam |
/admin/forms/{form}/submissions/{submission}/notes | PATCH | Update internal notes |
/admin/forms/{form}/submissions/{submission} | DELETE | Delete |
Submission record
- payload — JSON of submitted field values.
- status —
new|read|spam. - ip_address, user_agent — metadata captured on submit.
- referrer — page the form was submitted from.
- notes — internal-only follow-up notes.
Public submit endpoint
Forms are exposed publicly under the API. Frontends post directly without an API key:
POST /api/v1/forms/{formSlug}/submit
GET /api/v1/forms/{formSlug}
Throttled to 5 requests/min/IP. See API: Form Submissions for the request/response shape.
Notifications
On a successful submit, listeners on the FormSubmissionReceived event:
- Send a notification email to each address in
notify_emails. - Fire a webhook event (
form.submission.created) if any matching webhook subscriptions exist. - Write an activity log entry.
Spam mitigationUse the honeypot field plus the 5/min rate limit. For high-volume forms, add a reCAPTCHA secret in the form settings; submissions failing the challenge are rejected with a 422.