---
title: "Responses and error codes"
description: "A 200 means the lead is stored. It does not mean it is delivered. Every answer the form address gives, and every refusal code."
url: "https://formparcel.live/docs/reference/responses"
---

# Responses and error codes

A 200 means the lead is stored. It does not mean it is delivered. Every answer the form address gives, and every refusal code.

## What a 200 means

FormParcel answers only after the lead is written to its database and has a receipt ID. Delivery to the Google Sheet runs after that, on its own, so the answer can reach you before the row reaches the Sheet.

Example lead at the moment the 200 is sent

Sheet Sheet: Accepted.

A 200 with `deliveryStatus` "pending" leaves the lead here. "delivered" means every destination confirmed within the 2-second window.

`deliveryStatus` says how far delivery got in the 2 seconds FormParcel waits before it answers:

- `"delivered"`: every enabled destination confirmed in time, and no Sheet column mapping was waiting for review.
- `"pending"`: the lead is stored and delivery carries on. Its receipt shows how it ends.

Both are success. Show the visitor your thank-you message either way, and never send the same lead again.

## Request and response

Ask for JSON with `Accept: application/json`. A script, a server or `curl` gets JSON even without it.

Example form address. Use yours from the dashboard.

```bash title="request.sh"
curl -X POST "https://f.formparcel.live/f/f_k7QmXp2aVb9sTn4cLw8RhE" \
  -H "Accept: application/json" \
  -d "name=Priya Sharma" \
  -d "email=priya.sharma@mail.example" \
  -d "message=Do you have Saturday appointments?"
```

```json title="200 OK application/json"
{
  "accepted": true,
  "deliveryStatus": "pending",
  "submissionId": "sub_m1x8k2q4_Zk3v..."
}
```

A `curl` post like this one carries no bot-check proof, so its lead waits in your Spam folder. It shows the answer; to test delivery, use **Send a test lead** in the dashboard. See [Bot check](https://formparcel.live/docs/reference/bot-check).

- **accepted boolean**: Always `true` on a 200.
- **deliveryStatus"delivered" or "pending"**: How far delivery got before the answer. See [What a 200 means](https://formparcel.live/docs/reference/responses#what-a-200-means).
- **submissionId string, starts with sub_**: The receipt ID. FormParcel also writes it into the `_submission_id` column (column A) of the lead’s Sheet row, so you can find the row from the receipt.

## Which answer you get

A browser that submits a plain HTML form gets a page or a redirect. Everything else gets JSON, for success and for refusals alike.

| Request | Status | Body |
| --- | --- | --- |
| fetch, XHR, curl or a server; the lead is accepted. A post without the bot check gets the same answer and waits in Spam | 200 | JSON with accepted, deliveryStatus and submissionId |
| A browser form post with a trusted _next or _redirect | 303 | Empty. Location is your thank-you page. |
| A browser form post with no trusted redirect | 200 | A short page headed "Your message was sent". No receipt ID is shown to the visitor. |
| A script request with the trap field filled | 204 | Empty. Nothing is stored and there is no receipt. |

A request counts as a browser form post only when it accepts `text/html`, its `Sec-Fetch-Mode` is missing or `navigate`, and it did not ask for JSON.

## Status and error codes

A refusal stores nothing and makes no receipt. The JSON body has `code`, `error` and `"success": false`; refusals marked “No code” leave `code` out. Each code links to its entry further down this page.

| Status | When it happens | Code | Retry |
| --- | --- | --- | --- |
| **Fix the request 400 413 415 422** |   |   |   |
| 400 | The JSON cannot be parsed | [INVALID_JSON](https://formparcel.live/docs/reference/responses#invalid-json) | No |
| 400 | The body is malformed, or the JSON is not a key-value object | [INVALID_BODY](https://formparcel.live/docs/reference/responses#invalid-body) | No |
| 400 | A top-level JSON key appears twice | [DUPLICATE_FIELD](https://formparcel.live/docs/reference/responses#duplicate-field) | No |
| 400 | More than 50 fields | [TOO_MANY_FIELDS](https://formparcel.live/docs/reference/responses#too-many-fields) | No |
| 400 | A field name is over 64 characters | [FIELD_NAME_TOO_LONG](https://formparcel.live/docs/reference/responses#field-name-too-long) | No |
| 400 | A field value is over 4,096 characters | [FIELD_VALUE_TOO_LONG](https://formparcel.live/docs/reference/responses#field-value-too-long) | No |
| 400 | JSON nested deeper than 4 levels | [PAYLOAD_TOO_DEEP](https://formparcel.live/docs/reference/responses#payload-too-deep) | No |
| 400 | A number that is not finite, such as 1e400 | [NON_FINITE_NUMBER](https://formparcel.live/docs/reference/responses#non-finite-number) | No |
| 400 | A multipart body contains a file | [FILE_UPLOAD_NOT_SUPPORTED](https://formparcel.live/docs/reference/responses#file-upload-not-supported) | No |
| 400 | No field holds readable content | [EMPTY_SUBMISSION](https://formparcel.live/docs/reference/responses#empty-submission) | No |
| 413 | The body is over 16 KB | [PAYLOAD_TOO_LARGE](https://formparcel.live/docs/reference/responses#payload-too-large) | No |
| 415 | Not form-encoded, multipart or JSON | [UNSUPPORTED_MEDIA_TYPE](https://formparcel.live/docs/reference/responses#unsupported-media-type) | No |
| 415 | A JSON or multipart body that is not UTF-8 | [UNSUPPORTED_CHARSET](https://formparcel.live/docs/reference/responses#unsupported-charset) | No |
| 422 | A field named like a password, one-time code or card detail | [PROHIBITED_FIELD](https://formparcel.live/docs/reference/responses#prohibited-field) | No |
| **Form settings 400 403 404** |   |   |   |
| 400 | The form has no enabled destination yet | [No code: Form not ready](https://formparcel.live/docs/reference/responses#form-not-ready) | No |
| 403 | The form is paused | [No code: Form paused](https://formparcel.live/docs/reference/responses#form-paused) | No |
| 403 | The page is not on the form's allowed origins | [No code: Origin not allowed](https://formparcel.live/docs/reference/responses#origin-not-allowed) | No |
| 404 | No form has this ID | [No code: Form not found](https://formparcel.live/docs/reference/responses#form-not-found) | No |
| **Spam check 403** |   |   |   |
| 403 | Turnstile is on and the token did not pass | [SPAM_CHECK_FAILED](https://formparcel.live/docs/reference/responses#spam-check-failed) | With a fresh token |
| **Slow down 429** |   |   |   |
| 429 | Too many requests from one client, across all forms | [CLIENT_RATE_LIMITED](https://formparcel.live/docs/reference/responses#client-rate-limited) | After Retry-After |
| 429 | Too many requests from one client to this form | [FORM_RATE_LIMITED](https://formparcel.live/docs/reference/responses#form-rate-limited) | After Retry-After |
| 429 | One client's hourly limit in the workspace | [CLIENT_HOURLY_LIMIT](https://formparcel.live/docs/reference/responses#client-hourly-limit) | After Retry-After |
| 429 | The form's hourly cap on accepted leads | [FORM_HOURLY_LIMIT](https://formparcel.live/docs/reference/responses#form-hourly-limit) | After Retry-After |
| 429 | The workspace's daily cap, on a busy form | [WORKSPACE_DAILY_LIMIT](https://formparcel.live/docs/reference/responses#workspace-daily-limit) | After Retry-After |
| **Plan limit 429** |   |   |   |
| 429 | The submission would be held as spam, and the workspace's Spam folder is full for the month. Nothing is stored and there is no receipt. | [SPAM_FOLDER_FULL](https://formparcel.live/docs/reference/responses#spam-folder-full) | Not this month as it is |
| 429 | The workspace has used three times its monthly leads. Nothing is stored and there is no receipt. | [PLAN_LIMIT_REACHED](https://formparcel.live/docs/reference/responses#plan-limit-reached) | Not this month |
| **Our side 500 503** |   |   |   |
| 500 | The lead could not be stored | [No code: Lead not saved](https://formparcel.live/docs/reference/responses#not-saved) | Once |
| 503 | The form lookup failed. Sends Retry-After: 5. | [SERVICE_UNAVAILABLE](https://formparcel.live/docs/reference/responses#service-unavailable) | After 5 s |
| **Wrong method 405** |   |   |   |
| 405 | A GET or HEAD request; the answer carries Allow: POST, OPTIONS. Other methods (PUT, PATCH, DELETE) get 405 with an empty body and no Allow header. | [No code: Method not allowed](https://formparcel.live/docs/reference/responses#method-not-allowed) | Use POST |

> **Note** The `error` text is written for people and can change. Branch on the HTTP status first, then on `code`.

```json title="429 Too Many Requests application/json"
{
  "success": false,
  "error": "Rate limit exceeded. Please try again later.",
  "code": "FORM_RATE_LIMITED"
}
```

## When to retry

- **200**: never send the lead again, whatever `deliveryStatus` says. It is stored; a second post makes a second lead with its own receipt ID.
- **429**: wait the seconds in `Retry-After`, then try once more. Not for `PLAN_LIMIT_REACHED`, which has no `Retry-After`.
- **500** or **503**: try once more. A 503 asks you to wait 5 seconds.
- **403** `SPAM_CHECK_FAILED`: ask the visitor to complete the spam check, then send it with the fresh token.
- Any other **4xx**: do not retry. Fix the request or the form’s settings.

## Every refusal

One entry for each refusal, with what the visitor sees when a browser form post is refused. The anchor of each entry is the code in lower case with dashes, or the name shown for a refusal that sends no code.

### Fix the request 400 413 415 422

#### 400 INVALID_JSON

- **When**: The JSON cannot be parsed.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message could not be sent".
- **What to do**: Send valid JSON, or send the form as a form post.

#### 400 INVALID_BODY

- **When**: The body is malformed, or the JSON is not a key-value object.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message could not be sent".
- **What to do**: Send a form post, or a JSON object of field names and values. An array or a bare value is refused.

#### 400 DUPLICATE_FIELD

- **When**: A top-level JSON key appears twice.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message could not be sent".
- **What to do**: Send each top-level key once.

#### 400 TOO_MANY_FIELDS

- **When**: More than 50 fields.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message could not be sent".
- **What to do**: Send at most 50 fields.

#### 400 FIELD_NAME_TOO_LONG

- **When**: A field name is over 64 characters.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message could not be sent".
- **What to do**: Keep each field name to 64 characters or fewer.

#### 400 FIELD_VALUE_TOO_LONG

- **When**: A field value is over 4,096 characters.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message could not be sent".
- **What to do**: Keep each value to 4,096 characters or fewer.

#### 400 PAYLOAD_TOO_DEEP

- **When**: JSON nested deeper than 4 levels.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message could not be sent".
- **What to do**: Flatten the JSON to at most 4 levels in total.

#### 400 NON_FINITE_NUMBER

- **When**: A number that is not finite, such as 1e400.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message could not be sent".
- **What to do**: Send a finite number, or send the value as text.

#### 400 FILE_UPLOAD_NOT_SUPPORTED

- **When**: A multipart body contains a file.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message could not be sent".
- **What to do**: Take the file input out of the form. FormParcel does not take file uploads.

#### 400 EMPTY_SUBMISSION

- **When**: No field holds readable content.
- **Retry**: No. Nothing is stored and there is no receipt.
- **Visitor**: A browser form post shows "Your message was empty".
- **What to do**: Make sure at least one field has a value and every field has a name attribute. A field without a name is not sent.

#### 413 PAYLOAD_TOO_LARGE

- **When**: The body is over 16 KB.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message is too long".
- **What to do**: Send 16 KB or less.

#### 415 UNSUPPORTED_MEDIA_TYPE

- **When**: Not form-encoded, multipart or JSON.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message could not be sent".
- **What to do**: Send the body as application/x-www-form-urlencoded, multipart/form-data or application/json.

#### 415 UNSUPPORTED_CHARSET

- **When**: A JSON or multipart body that is not UTF-8.
- **Retry**: No. The same body fails the same way.
- **Visitor**: A browser form post shows "Your message could not be sent".
- **What to do**: Send the body as UTF-8.

#### 422 PROHIBITED_FIELD

- **When**: A field named like a password, one-time code or card detail.
- **Retry**: No. Nothing is stored and there is no receipt.
- **Visitor**: A browser form post shows "This form cannot take that information".
- **What to do**: Take that field out of the form. FormParcel does not accept passwords, one-time codes or card details.

### Form settings 400 403 404

#### 400 Form not ready No code

- **When**: The form has no enabled destination yet.
- **Retry**: No. The owner finishes the form's setup first.
- **Visitor**: A browser form post shows "This form is not ready yet".
- **What to do**: The owner connects a destination to the form in the dashboard. A form accepts leads once it has somewhere to deliver them.

#### 403 Form paused No code

- **When**: The form is paused.
- **Retry**: No. The owner resumes the form first.
- **Visitor**: A browser form post shows "This form is not accepting messages right now".
- **What to do**: The owner resumes the form in the dashboard.

#### 403 Origin not allowed No code

- **When**: The page is not on the form's allowed origins.
- **Retry**: No. Post from an allowed page, or add the site to the form's allowed origins.
- **Visitor**: A browser form post shows "This form cannot be used from here".
- **What to do**: Add the page's site to the form's allowed origins, or post from a page that is already on the list.

#### 404 Form not found No code

- **When**: No form has this ID.
- **Retry**: No. Check the address.
- **Visitor**: A browser form post shows "We could not find this form".
- **What to do**: Copy the form address again from the dashboard.

### Spam check 403

#### 403 SPAM_CHECK_FAILED

- **When**: Turnstile is on and the token did not pass.
- **Retry**: Yes, once the visitor passes the check, with the fresh token the widget gives.
- **Visitor**: A browser form post shows "Please complete the spam check and try again".
- **What to do**: Ask the visitor to complete the check, then send the post again with the new token.

### Slow down 429

#### 429 CLIENT_RATE_LIMITED

- **When**: Too many requests from one client, across all forms.
- **Retry**: Wait the seconds in Retry-After, then send once more.
- **Visitor**: A browser form post shows "Please try again in a moment".
- **What to do**: Wait the seconds in Retry-After, then send once more.

#### 429 FORM_RATE_LIMITED

- **When**: Too many requests from one client to this form.
- **Retry**: Wait the seconds in Retry-After, then send once more.
- **Visitor**: A browser form post shows "Please try again in a moment".
- **What to do**: Wait the seconds in Retry-After, then send once more.

#### 429 CLIENT_HOURLY_LIMIT

- **When**: One client's hourly limit in the workspace.
- **Retry**: Wait the seconds in Retry-After, then send once more.
- **Visitor**: A browser form post shows "Please try again in a moment".
- **What to do**: Wait the seconds in Retry-After, then send once more.

#### 429 FORM_HOURLY_LIMIT

- **When**: The form's hourly cap on accepted leads.
- **Retry**: Wait the seconds in Retry-After, then send once more.
- **Visitor**: A browser form post shows "Please try again in a moment".
- **What to do**: Wait the seconds in Retry-After, then send once more.

#### 429 WORKSPACE_DAILY_LIMIT

- **When**: The workspace's daily cap, on a busy form.
- **Retry**: Wait the seconds in Retry-After, then send once more.
- **Visitor**: A browser form post shows "Please try again in a moment".
- **What to do**: Wait the seconds in Retry-After, then send once more.

### Plan limit 429

#### 429 SPAM_FOLDER_FULL

- **When**: The submission would be held as spam, and the workspace's Spam folder is full for the month. Nothing is stored and there is no receipt.
- **Retry**: Not as it is. A submission that passes every check (with the bot check, from a new sender, with new content) is still accepted.
- **Visitor**: A browser form post shows "This form cannot take more messages this month".
- **What to do**: Add the bot check script to the form's pages. The owner reviews the Spam folder; it empties at the next UTC month.

#### 429 PLAN_LIMIT_REACHED

- **When**: The workspace has used three times its monthly leads. Nothing is stored and there is no receipt.
- **Retry**: Not this month. There is no Retry-After header.
- **Visitor**: A browser form post shows "This form cannot take more messages this month".
- **What to do**: The owner upgrades the plan, or waits for the next UTC month.

### Our side 500 503

#### 500 Lead not saved No code

- **When**: The lead could not be stored.
- **Retry**: Yes, once. The lead was not accepted.
- **Visitor**: A browser form post shows "Something went wrong on our side".
- **What to do**: Send the post again once. If it keeps failing, tell us the time of the request.

#### 503 SERVICE_UNAVAILABLE

- **When**: The form lookup failed. Sends Retry-After: 5.
- **Retry**: Yes, after 5 seconds. The lead was not accepted.
- **Visitor**: A browser form post shows "Something went wrong on our side".
- **What to do**: Send the post again after 5 seconds.

### Wrong method 405

#### 405 Method not allowed No code

- **When**: A GET or HEAD request; the answer carries Allow: POST, OPTIONS. Other methods (PUT, PATCH, DELETE) get 405 with an empty body and no Allow header.
- **Retry**: Send a POST instead.
- **Visitor**: A browser form post shows "This is a form address".
- **What to do**: Send a POST. OPTIONS answers the CORS preflight.
