Send API reference
One endpoint receives every submission. Your form, or your server, posts JSON to it; FormRoute checks it and emails it from your domain.
Endpoint
POST https://form-route.com/api/v1/send
Content-Type: application/jsonNo authentication header is needed: the site key in the body identifies your form, and the checks decide what is sent.
Request body
{
"siteKey": "fr_your_site_key",
"t": 5400,
"hp": "",
"name": "Ada Lovelace",
"email": "[email protected]",
"message": "Could you send me a quote for next month?"
}| Key | Type | Required | Description |
|---|---|---|---|
siteKey | string | Yes | Your form's key from your account. Starts with fr_. |
t | integer | Yes | Milliseconds between showing the form and sending it. Missing, or under 1200, means the submission is treated as automated. |
hp | string | Send it | The honeypot input's value. Empty for people. Anything else and the submission is filtered as spam. |
captchaToken | string | No | A Cloudflare Turnstile token, only when a response asked for one. |
your fields | string | As configured | One key per field you set up, such as name, email, message. Values are trimmed. |
Form fields
- Only fields you set up are kept. Any other key is dropped silently, so extra inputs on your page do no harm.
- Required fields must be present and not blank after trimming.
- Maximum lengths are counted in characters after trimming.
- A field's type describes it in your account; values are checked for presence and length, not format.
- The email lists your fields in the order you set them up.
Responses
Every response is JSON with a message. Treat any 2xx status as success.
HTTP/1.1 200 OK
Content-Type: application/json
{ "message": "sent", "id": "0102019a…" }HTTP/1.1 403 Forbidden
Content-Type: application/json
{ "message": "Origin not allowed" }| Status | message | Meaning |
|---|---|---|
| 200 | sent | Passed every check and handed to Amazon SES. id is the message ID. |
| 200 | OK | Accepted but not sent: the honeypot was filled in. Bots get the same answer as people on purpose. |
| 400 | Invalid payload | The body is not JSON, or t is not a whole number. |
| 400 | Missing required fields: … | A required field was empty or missing. The message names the fields. |
| 400 | Field "…" exceeds max length (n) | A value is longer than the limit you set for that field. |
| 400 | captcha_required / captcha_failed | The submission looked automated and needs a valid Turnstile token. |
| 403 | Unknown siteKey | The site key doesn't match a form. |
| 403 | Site disabled | The form is switched off in your account. |
| 403 | Origin not allowed | The page that sent it is not in your form's allowed websites. |
| 403 | Site unavailable | Your account cannot send right now, for example because the subscription is not active or the domain is not verified. |
| 413 | Payload too large | The body is larger than 20 KB. |
| 429 | Too many requests / captcha_required | A rate limit was reached. reason is ip_per_min, ip_site_per_min or site_per_hour. |
| 500 | Internal error | Something failed on FormRoute's side. Nothing was sent; it is safe to retry. |
Every submission to a known form, accepted or refused, is listed in your account with its result. Field values are never stored.
Order of checks
A body over 20 KB is refused before anything is read. After that, checks run in this order and the first failure decides the response:
- Form: the site key exists and the form is on.
- Account: the subscription is active and the domain verified.
- Website: the
Originis one of your allowed websites. - Fields: unknown keys dropped, required fields present, lengths within limits.
- Honeypot:
hpis empty. - Rate limits.
- CAPTCHA, when the submission looks automated.
Protection explains each check in plain language.
Browsers and CORS
Browsers send an Origin header with every fetch. FormRoute answers the preflight request for any origin listed on a form, allows the Content-Type header, and accepts the submission only if its origin is listed on that form. Origins match exactly: scheme, host and port.
Requests without an Origin header, such as cURL or a server, skip the website check. They still go through every other check, and all of them count against the rate limits of the server's address.
Limits
| Per visitor | 20 submissions a minute, across forms |
| Per visitor, per form | 8 submissions a minute |
| Per form | 300 submissions an hour |
| Body size | 20 KB |
| Timing | t of at least 1200 ms |
| Allowed websites | Up to 10 per form |