# Submit Form (POST)

Send a response. The submission becomes a message on the owner's account and is pushed to their phone — the same pipeline a [webhook](https://info.pocketalert.app/api/webhooks/create.md) call goes through. **No authentication**.

## Endpoint

```
POST https://api.pocketalert.app/v1/forms/{tid}/submit
```

## Authentication

> **Tip: Not required**
>
> Anyone holding the link can submit. The owner controls who may: see [allowed origins](https://info.pocketalert.app/api/forms/create.md#allowed-origins) and the limits below.

## Request

### Path Parameters

| Parameter | Type | Required | Description |
|-----------|------|:--------:|-------------|
| `tid` | string | ✅ | Form identifier |

### Body Parameters

| Parameter | Type | Required | Description |
|-----------|------|:--------:|-------------|
| `values` | object | ✅ | Answers keyed by field `id` |
| `website` | string | ❌ | Honeypot. Leave it out; anything non-empty is treated as a bot |

Value per field type:

| Type | Value |
|------|-------|
| `text`, `textarea`, `email`, `phone`, `number`, `url` | String. Format is validated for email, phone, number and URL |
| `date` | String, `YYYY-MM-DD` |
| `dropdown`, `radio` | String, one of the field's `options` |
| `checkboxes` | Array of strings from `options` |
| `checkbox` | Boolean |
| `rating` | Number, 1–5 |
| `nps` | Number, 0–10 |
| `hidden` | String, normally taken from the page URL |

Layout fields (`heading`, `paragraph`, `divider`) take no value, and unknown keys are ignored. The body is capped at 64 KB.

### Example Request

**cURL**

```bash
curl -X POST "https://api.pocketalert.app/v1/forms/vml19aihrga216gt8apc5e3m9/submit" \
  -H "Content-Type: application/json" \
  -d '{
    "values": {
      "name": "Jane Doe",
      "email": "jane@example.com",
      "message": "Do you ship to Canada?",
      "src": "newsletter"
    }
  }'
```

**JavaScript**

```javascript
const response = await fetch(
  'https://api.pocketalert.app/v1/forms/vml19aihrga216gt8apc5e3m9/submit',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      values: {
        name: 'Jane Doe',
        email: 'jane@example.com',
        message: 'Do you ship to Canada?'
      }
    })
  }
);

const result = await response.json();
if (response.status === 400) {
  // result.fields = { email: "Enter a valid email address" }
  console.log(result.fields);
}
```

**Python**

```python
import requests

response = requests.post(
    'https://api.pocketalert.app/v1/forms/vml19aihrga216gt8apc5e3m9/submit',
    json={
        'values': {
            'name': 'Jane Doe',
            'email': 'jane@example.com',
            'message': 'Do you ship to Canada?',
        }
    }
)

print(response.status_code, response.json())
```

## Response

### Success Response

> **Tip: 200 OK**
>
> ```json
> { "status": "ok" }
> ```

The owner receives a push built from the answers. Nothing about the owner — their plan, their devices, their quota — is exposed to the visitor.

### Validation Errors

> **Warning: 400 Bad Request**
>
> `fields` maps a field `id` to the reason it was rejected, which is what the form page renders under each input.

```json
{
  "error": "Please check the highlighted fields.",
  "fields": {
    "email": "Enter a valid email address",
    "name": "This field is required"
  }
}
```

### Closed Form

> **Danger: 410 Gone**
>
> ```json
> {
>   "error": "This form is no longer accepting responses.",
>   "closed": true
> }
> ```

`error` carries the owner's own `closed_message`.

### Limits

| Limit | Value |
|-------|-------|
| Per IP | 10 submissions a minute |
| Per form | 60 a minute, 300 an hour |
| Owner's quota | Each submission uses one message from their daily allowance |

A submission that would exceed the owner's daily message limit gets a neutral `429` with a `Retry-After` header and no mention of the plan.

### Error Responses

| Status | Description |
|:------:|-------------|
| `400` | Invalid body, or `fields` lists the answers to fix |
| `403` | The form does not accept submissions from this site — see [allowed origins](https://info.pocketalert.app/api/forms/create.md#allowed-origins) |
| `404` | Form not found |
| `410` | The form is closed |
| `429` | Too many submissions, or the owner is out of messages for today |
| `500` | The form is misconfigured, or the message could not be created |
