Form Publishing
Form Lifecycle
Forms move through three statuses:
| Status | Meaning | Public access |
|---|---|---|
draft |
Work in progress, not publicly available | No |
published |
Live and accepting responses | Yes |
closed |
No longer accepting responses | Shows closed message |
Publishing a Form
# Create as draft (default)
pnpm action create-form --title "Survey" --fields '[...]'
# Publish when ready
pnpm action update-form --id <form-id> --status published
# Close when done collecting responses
pnpm action update-form --id <form-id> --status closed
Public URLs
Published forms are accessible at:
/f/<slug>
The slug is auto-generated from the title + a short unique suffix:
- Title: "Contact Form" -> Slug:
contact-form/a1b2c3 - Full URL:
https://yourapp.com/f/contact-form/a1b2c3
The slug updates automatically when the title changes.
Captcha Protection
Public form submissions can be protected with Cloudflare Turnstile (opt-in). This prevents bot submissions without degrading the user experience.
Branding
Public forms display a "Built with Agent-Native" badge by default. This can be configured in the form settings.
Form Settings
Each form has a settings JSON object:
{
"submitText": "Submit",
"successMessage": "Thank you! Your response has been recorded.",
"redirectUrl": null,
"completionMode": "message",
"completionRefreshSeconds": 5,
"showProgressBar": false,
"emailOnNewResponses": false,
"anonymous": false,
"integrations": []
}
| Setting | Type | Description |
|---|---|---|
submitText |
string | Custom submit button text |
successMessage |
string | Message shown after successful submission |
redirectUrl |
string | URL to redirect to after submission |
completionMode |
string | message, redirect, message_then_refresh, or refresh |
completionRefreshSeconds |
number | Delay before refreshing for message_then_refresh (1–3600) |
showProgressBar |
boolean | Show progress bar for multi-section forms |
emailOnNewResponses |
boolean | Email the form owner's account when someone submits a response |
anonymous |
boolean | Suppress IP, submitter identity, chat/run ids, page URL, and client-surface metadata for every response |
integrations |
array | Webhook/Slack/Discord/Google Sheets notification configs |
For a genuinely anonymous form, set anonymous: true when creating the form.
Do not describe an ordinary published form as anonymous: published forms accept
public responses, but only anonymous mode suppresses identifying and source
metadata.
Unset completionMode keeps legacy behavior: forms with a redirectUrl redirect
and all other forms show the success message. A redirect with an invalid URL
falls back to the success message.
Integration Types
Forms can notify external services on submission:
| Type | Description |
|---|---|
webhook |
POST JSON to any URL |
slack |
Send to a Slack channel through an Incoming Webhook URL |
discord |
Send to a Discord webhook |
google-sheets |
Send response JSON to a deployed Apps Script /exec URL |
These are outbound form destinations configured in the form builder's
Integrations tab. They are separate from the managed Slack/Messaging
connection. Google Sheets Apps Script handlers should parse
JSON.parse(e.postData.contents); a spreadsheet URL or /dev URL will not
receive submissions.
Related Skills
- form-building — Creating and structuring forms
- form-responses — Viewing data after forms are published