API reference
API v1One endpoint per form, one predictable contract. Everything on this page applies to every plan.
Endpoint
POST https://formivo.faizyab.me/api/f/{formId}
GET https://formivo.faizyab.me/api/f/{formId} -> 405 Method Not Allowed| Property | Value |
|---|---|
| Authentication | None for submissions. Reading data is session-based. |
| Methods | POST only. |
| Content types | application/json, application/x-www-form-urlencoded, multipart/form-data |
| Body limit | 256 KB, up to 100 fields, 5 levels of nesting |
| Idempotency | Optional Idempotency-Key header; retries never create duplicates |
| CORS | Any origin by default; restrictable per form |
Request
Send the values you want stored. Every key becomes a column in the submission, and the server adds ownership, timestamps and plan context itself.
POST https://formivo.faizyab.me/api/f/YOUR_FORM_ID HTTP/1.1
Content-Type: application/json
Idempotency-Key: contact-7f2a
{
"name": "Ada Lovelace",
"email": "ada@example.com",
"message": "Please call me back.",
"metadata": { "page": "/contact", "campaign": "spring" }
}Reserved fields
| Field | Type | Behaviour |
|---|---|---|
| metadata | object | Context stored with the submission, such as the page path or campaign. |
| _redirect | string (URL) | Where to send the browser after a successful HTML submission. |
| _gotcha | string | Honeypot. Leave empty; a filled value marks the request as a bot. |
| any other key | string / number / boolean | Stored as-is and shown in the dashboard. |
Idempotency
Networks drop. If you send an Idempotency-Key header, a retry with the same key returns the original result instead of creating a second submission. Use a value that is stable for one logical submission, such as an order id or a generated UUID stored with the form state.
Keys are scoped per form
Responses
HTML submissions receive a rendered thank-you page. JSON submissions receive a body with a stable shape:
{
"success": true,
"leadId": "9d1f2c4a-7b3e-4f1a-9c2d-1a2b3c4d5e6f",
"message": "Submission received."
}{
"success": false,
"error": {
"code": "FORM_PAUSED",
"message": "This form is not accepting submissions."
}
}Error bodies never include stack traces, SQL or configuration details. Treat error.code as the machine-readable branch and error.message as something safe to show a person.
Status codes
| Status | Meaning | Notes |
|---|---|---|
| 200 | HTML submission accepted | Returns the thank-you page, or redirects to _redirect. |
| 201 | JSON submission accepted | Returns the new leadId. |
| 400 | Invalid payload | Malformed JSON, unsupported field types, too many fields. |
| 404 | Unknown form | The form id does not exist or was deleted. |
| 405 | Read attempted | The public endpoint only accepts POST. |
| 409 | Form paused or quota exceeded | Resume the form or upgrade the plan. |
| 413 | Body too large | Requests are capped at 256 KB. |
| 415 | Unsupported content type | Use JSON, form-encoded or multipart. |
| 429 | Rate limited | Includes a Retry-After header in seconds. |
| 500 | Server error | Retry with backoff; the submission was not stored. |
Retry 5xx responses with backoff. Do not retry a 4xx without changing the request: the same payload will fail the same way.
Rate limits and quotas
- 120 requests per minute per form.
- 60 requests per minute per IP address, shared across forms.
- 30 requests per minute per form and IP address.
- The monthly plan quota is enforced atomically in the database.
429 responses
Retry-After in seconds. Back off for at least that long instead of retrying immediately.CORS and allowed domains
Browser submissions are accepted from any origin by default. A form can restrict browser access to specific domains, which tightens CORS and rejects requests that declare a different Origin.
Not an authentication mechanism