Skip to content

API reference

POST https://app.formkove.com/api/forms/{form_id}/submissions

Replace {form_id} with your form ID from the Formkove dashboard. The form ID in the URL is all you need. There’s no separate access key.

Content-Type Typical use
application/x-www-form-urlencoded A plain HTML <form method="POST">
multipart/form-data fetch(url, { body: new FormData(form) })
application/json fetch() with JSON.stringify(...)

Send a flat object of fields. Any field names work. Nested objects are stored as JSON strings.

File uploads are not supported. File inputs are not stored.

Formkove picks the response style from your request:

  • JSON mode: when Content-Type is application/json or the Accept header includes application/json. You get a JSON body and a status code (below). Redirects are never used.
  • HTML mode: everything else (a normal browser form post). Formkove answers with a 303 redirect: to your ?redirect= URL, or to its own success page. Errors redirect to a Formkove error page.
Field Description
email / replyto / reply_to / reply-to / _replyto The visitor’s email. Used as the Reply-To of notification emails. The first non-empty valid email wins.
Your honeypot field The field name you configured under Settings → Honeypot. If it has any value, Formkove pretends to accept the submission but stores nothing.
cf-turnstile-response The Turnstile token, required when Turnstile is enabled. Stripped before storage.

The notification email subject is always New Submission for "[Form Name]" and can’t be overridden.

Parameter Description
redirect HTML mode only. Where to send the visitor after success. Must be a full URL whose domain is in the form’s Allowed Origins; anything else falls back to Formkove’s success page. See Success Pages and Redirects.
Header Description
Accept: application/json Forces JSON mode, even for FormData bodies
Idempotency-Key Optional, any unique string per submission attempt. If a request with the same key already succeeded on this form within 24 hours, Formkove returns the original response instead of creating a duplicate. Use it when retrying after a network error.
const res = await fetch('https://app.formkove.com/api/forms/YOUR_FORM_ID/submissions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
name: 'Jane Smith',
message: 'Hi, I would like to schedule a demo.',
}),
});
const json = await res.json();

Every JSON response has a success boolean. Errors include an error message.

{
"success": true,
"id": "550e8400-e29b-41d4-a716-446655440000"
}
Status error When
400 Invalid submission body The body can’t be parsed, or isn’t a flat object
400 Too many fields or a field value is too large More than 100 fields, or a field over 10 KB
403 Security verification failed Turnstile is enabled and the token is missing or invalid
403 Submission limit reached The workspace’s monthly submission limit is used up
404 Form not found The form ID is wrong or the form was deleted
409 Request already processing Another request with the same Idempotency-Key is still in flight
410 This form has been disabled The form was disabled for abuse
413 Payload too large Body over 100 KB
415 Unsupported content type Not JSON, urlencoded, or multipart
429 Too many submissions. Please try again later. Rate limit exceeded (see below)
500 / 503 Failed to process submission / Service temporarily unavailable… Temporary server problem. Safe to retry with the same Idempotency-Key.
Limit Value
Request body 100 KB
Fields per submission 100
Size per field 10 KB
Rate limit (per IP, per form) 10 per minute, 30 per hour

429 responses include Retry-After and X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers. Successful JSON responses include the X-RateLimit-* headers too.

Monthly submission limits depend on your plan. See Pricing.

Browser fetch() calls work directly, with no proxy, once your site’s origin is in the form’s Allowed Origins (Settings → Allowed Origins & Redirects). Enter full origins like https://example.com, or a bare domain like example.com to include its subdomains. Until you add it, the browser blocks the response (and, for JSON requests, the request itself).

Plain HTML form posts and server-to-server requests aren’t affected by CORS.

For the payload Formkove sends to your endpoints and how to verify signatures, see Zapier and Webhooks.


For a complete fetch-based implementation, see JavaScript Form Submission.