Documentation
Everything you need to ship a form.
One endpoint, any stack. Below: the quickstart, every reserved field, JSON submissions and delivery behaviour. Using the dashboard, file uploads, spam controls, webhook signing, routing, the REST APIs and the MCP server each have their own page in the sidebar.
Quickstart
Every form gets an endpoint: https://send.webforms.to/wft_your_key. Create a free account, copy the key from your dashboard and point any form at it. No backend, no SMTP credentials, nothing to deploy.
<form action="https://send.webforms.to/wft_your_key" method="POST"> <input type="email" name="email" required /> <textarea name="message" required></textarea> <button type="submit">Send</button> </form>
Submissions accept application/x-www-form-urlencoded, multipart/form-data (needed for file uploads - see file uploads) and application/json. Browser submits without an accept: application/json header get a friendly HTML success page, or a redirect if you set _next.
The same endpoint also fans out to Slack, Discord, Telegram, webhooks and more from the one submission - configure destinations and routing rules in the dashboard, or over the write API.
Special fields
Field names starting with an underscore control behaviour and are never stored as submission data.
_nextRedirect URL after a successful browser submission._redirectAlias for _next, also used on validation failure to redirect back with a wf_error query param._gotchaHoneypot. Add it hidden with CSS; if it arrives filled, the submission is dropped while the bot sees a success response. Customisable per form - see spam protection._subjectOverrides the email notification subject._from_nameOverrides the sender name shown in the email notification._replytoSets the reply-to address on the email notification (must be a valid email)._ccComma-separated list of extra email recipients, up to 10 valid addresses._templateSelects a specific notification template id for this submission._pageFull URL of the page the form is on, e.g. https://yoursite.com/pricing. Shown as “Submitted from” in the notification email. A cross-origin POST's Referer header is stripped to bare origin by the browser, so set this explicitly (a hidden field with document.location.href) if you want the exact page, not just the domain._start_timeUnix ms timestamp of when the form was rendered - powers the fill-time spam trap. Set it in a hidden field via JS on page load._testMarks the submission as a test: stored and returned normally, but excluded from quota and from the default Submissions API listing._captcha / cf-turnstile-response / h-captcha-response / g-recaptcha-responseCaptcha verification token, whichever your captcha provider's widget produces.
There is no field for choosing where a submission is delivered - destinations are read only from your own workspace configuration, never from the request. A public form key that could redirect delivery would turn the endpoint into an open relay.
JSON submissions
Send content-type: application/json with any flat or nested object. Add accept: application/json to get a JSON response instead of HTML.
await fetch("https://send.webforms.to/wft_your_key", {
method: "POST",
headers: {
"content-type": "application/json",
accept: "application/json",
},
body: JSON.stringify({ email: "ada@example.com", message: "Hi" }),
});A repeated key (a checkbox group posted as interest=a&interest=b) collapses into an array automatically - the same shape whether it arrived urlencoded, multipart or as a JSON array to begin with.
Responses
Successful JSON submissions return:
{
"ok": true,
"alert": "success",
"id": "sub_...",
"message": "Thanks! Your message was received."
}Every JSON response carries alert alongside ok - either "success" or "error" - so your own code can switch on one field instead of checking ok plus an HTTP status class. On Pro and Agency, a form can also merge its own flat string key/value pairs into the success and error responses (confirmation.customJsonSuccess and confirmation.customJsonError in the write API, or the form's Settings tab in the dashboard) - useful for a constant like a client-side redirect flag or a support link, without needing your own server round trip to add it.
Validation failures return 422 with a list of field errors. Rate limiting returns 429 with a retry-after header; the ingest endpoint allows bursts and is limited per IP, per form. A submission that trips spam detection also returns 200 with a success message - bots that receive an error learn to adapt, so quarantine is silent by design. See the full error reference for every code the endpoint can return.
Delivery and retries
Deliveries fan out in parallel per destination. Transient failures retry with exponential backoff; permanent failures (bad webhook URL, revoked token) stop immediately and surface in your delivery log with the exact error. Paused destinations are skipped and logged. Every attempt records status, duration and response detail in the dashboard.
Sending the same submission twice on a network retry is safe: pass an Idempotency-Key header and a repeat request with the same key returns the original result instead of creating a second submission.
Get your endpoint
Free for 300 submissions a month. No card required.
Start free