# Submit endpoint

Full reference for POST /s/{key}, the one public endpoint every Nisuform form posts to, with request formats, responses and headers.

```http
POST https://api.nisuform.com/s/{key}
```

This is the only endpoint your forms need. It is public, needs no API key and accepts requests from any website.

## Path

| Parameter | Description                                                                                                          |
| --------- | -------------------------------------------------------------------------------------------------------------------- |
| `key`     | The form key. Letters, digits and hyphens, starting with a letter or digit, up to 64 characters. Not case sensitive. |

## Request body

| Content type                        | Use it for                                                                           |
| ----------------------------------- | ------------------------------------------------------------------------------------ |
| `application/x-www-form-urlencoded` | Plain HTML forms. The browser's default.                                             |
| `multipart/form-data`               | HTML forms with `enctype="multipart/form-data"`, `FormData` bodies and file uploads. |
| `application/json`                  | `fetch` and server-side requests. The body must be a JSON object.                    |

Any other content type, or JSON that can't be parsed, is rejected with `415` and `unsupported_media_type`.

Fields are name and value pairs:

* Up to 50 fields. Extra fields are ignored.
* Names up to 200 characters. A name ending in `[]` is saved without the brackets.
* Values up to 10,000 characters. Longer values are cut.
* Repeated names are joined into one comma-separated value, and so are JSON arrays.
* Up to 200 KB per request, or 25 MB on Pro forms with file uploads turned on.

Names that start with an underscore, and the captcha token names, are [special fields](/docs/connect/special-fields). They change how the submission is handled and are never stored.

## Response format

The response depends on the request's `Accept` header:

| `Accept` includes `text/html`          | Response                          |
| -------------------------------------- | --------------------------------- |
| Yes, like a normal browser form submit | An HTML page, or a `303` redirect |
| No, like `fetch`, cURL or a server     | JSON                              |

Send `Accept: application/json` from your code to always get JSON.

## JSON responses

```ts
type SubmitSuccess = {
  ok: true
  submissionId?: string
  warning?: 'near_limit'
}

type SubmitFailure = {
  ok: false
  error: string
  message: string
}
```

| Status       | Body                                                             | When                                                                              |
| ------------ | ---------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `200`        | `{ "ok": true, "submissionId": "..." }`                          | The submission was stored.                                                        |
| `200`        | `{ "ok": true, "submissionId": "...", "warning": "near_limit" }` | Stored, and the team has used 95% of its monthly submissions.                     |
| `200`        | `{ "ok": true }`                                                 | Dropped by the honeypot, the fill-time check or a block list. Nothing was stored. |
| `4xx`, `5xx` | `{ "ok": false, "error": "...", "message": "..." }`              | Rejected. See [Errors](/docs/reference/errors).                                   |

Submissions that went to the spam folder answer like stored submissions, with a `submissionId`.

## HTML responses

| Status       | Page                                                                      |
| ------------ | ------------------------------------------------------------------------- |
| `200`        | The thank-you page, or your custom message on Pro.                        |
| `303`        | Redirect to your redirect URL or to `_redirect`, on Pro.                  |
| `4xx`, `5xx` | A page with the reason and a link back to the page the visitor came from. |

## Headers

| Header                           | Sent                                                                    |
| -------------------------------- | ----------------------------------------------------------------------- |
| `Access-Control-Allow-Origin: *` | On every response, so browsers can read it from any website.            |
| `Retry-After`                    | On `429`, with the number of seconds to wait.                           |
| `Location`                       | On `303`, with the redirect URL.                                        |
| `x-request-id`                   | On every response. Include it when you contact support about a request. |

`OPTIONS` requests get `204` with the CORS headers, so preflight requests from `fetch` succeed. Browsers may send the `Content-Type` and `Accept` request headers. The response headers above, other than `Content-Type`, are only readable outside the browser, like from a server or cURL.

## Other methods

`GET` and other methods answer `405` with `method_not_allowed`. Browsers that open the endpoint see a short page explaining that it is a form endpoint.

## Processing order

1. Find the form by its key, or answer `404`.
2. Drop submissions from blocked IP addresses and countries.
3. Check allowed websites, or answer `403`.
4. Check rate limits, or answer `429`.
5. Read and parse the body, or answer `413` or `415`.
6. Drop bots caught by the honeypot or the fill-time check.
7. Reject empty submissions with `422`, and invalid files with `400`, `413` or `415`.
8. Reject submissions to closed forms with `423`.
9. Verify the Turnstile token, or answer `403`.
10. Mark link spam and duplicates as spam.
11. Check the monthly limit, or answer `402`.
12. Validate answers against the form's fields, store the submission and its files, and queue notifications.

## Examples

```sh
curl https://api.nisuform.com/s/YOUR_FORM_KEY \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{"email":"jane@example.com","message":"Hello!"}'
```

```sh
curl https://api.nisuform.com/s/YOUR_FORM_KEY \
  -H 'Accept: application/json' \
  --data-urlencode 'email=jane@example.com' \
  --data-urlencode 'message=Hello!'
```

```sh
curl https://api.nisuform.com/s/YOUR_FORM_KEY \
  -H 'Accept: application/json' \
  -F 'email=jane@example.com' \
  -F 'attachment=@brief.pdf'
```
