> ## Documentation Index
> Fetch the complete documentation index at: https://docs.allgoodhq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Reason codes

> The single machine-readable outcome every capture request comes back with.

Every capture request returns a JSON body containing a `status`. **Exactly one code describes a request**, because the checks stop at the first failure — the first thing that was wrong names the outcome.

## The codes

| Code              | HTTP                     | Surfaces       | Meaning                                                                                                 |
| ----------------- | ------------------------ | -------------- | ------------------------------------------------------------------------------------------------------- |
| `accepted`        | 200 (form) / 202 (event) | both           | Stored and queued                                                                                       |
| `validation`      | 400                      | both           | Wrong content type, malformed JSON, or over the size limit                                              |
| `unknown_source`  | 403 / 404                | both           | The form isn't registered, the site isn't allowlisted, or the tracking key is missing, wrong or revoked |
| `bot`             | 403                      | form only      | The honeypot was filled, or the bot-check token was missing or invalid                                  |
| `rate_limited`    | 429                      | both           | Too many requests from this IP or this site                                                             |
| `consent_missing` | 403                      | **event only** | Consent was required and the request carried no evidence                                                |

**A form submission is never refused for missing consent.** A submission is an explicit action by the visitor, and form processing rests on a different lawful basis from analytics consent.

## Telling unknown\_source apart

One code, several causes. Use the HTTP status and the request to distinguish them.

| Surface | HTTP | Cause                             | How to confirm                                          |
| ------- | ---- | --------------------------------- | ------------------------------------------------------- |
| Form    | 403  | Origin not allowlisted            | The preflight also returned `403`, with no CORS headers |
| Form    | 404  | The form isn't registered         | The runtime config reports `registered: false`          |
| Event   | 403  | Origin not allowlisted            | Preflight `403`                                         |
| Event   | 403  | Tracking master switch is off     | The runtime config reports `enabled: false`             |
| Event   | 403  | No key, wrong key, or revoked key | Settings → Web Tracking shows a revoked badge           |

<Warning>
  A refused origin's `403` carries **no CORS headers**, so a browser rejects it as a network error and you never see the body. That's why the script writes a named console warning instead, and why the runtime config endpoint — which answers every origin — exists.
</Warning>

## What each one means for your page

| Code              | Do                                                                                   |
| ----------------- | ------------------------------------------------------------------------------------ |
| `accepted`        | Show your thank-you. Keep `submissionId` if you want to correlate later              |
| `validation`      | A bug in your client. Check content type, body shape, and size                       |
| `unknown_source`  | A configuration problem, not a visitor problem. Don't show it to them as their fault |
| `bot`             | Ask them to try again. If it keeps happening on a real form, check widget coverage   |
| `rate_limited`    | Ask them to wait. Don't auto-retry in a loop                                         |
| `consent_missing` | Nothing to show — you shouldn't have sent it                                         |

## The default messages

The script writes these into your status element:

| Code              | Message                                                |
| ----------------- | ------------------------------------------------------ |
| `accepted`        | Thanks! Your submission was received.                  |
| `bot`             | We couldn't verify you're human. Please try again.     |
| `rate_limited`    | Too many attempts. Please wait a moment and try again. |
| `validation`      | Please check the form and try again.                   |
| `unknown_source`  | This form isn't accepting submissions from this page.  |
| `consent_missing` | Please accept cookies to continue.                     |
| anything else     | Something went wrong. Please try again later.          |

A problem the visitor can fix gets that specific message instead — "This field is required.", "Enter a valid email address.", and so on.

## One status that never comes from the server

The script also reports `error` when the request itself failed: a network error, a timeout, or a refused origin whose response carried no readable body. That isn't a server outcome — it means the request didn't complete.

## The pipelines

Knowing the order tells you what a code rules out.

**Form capture**

```
1. origin allowlist        → unknown_source (403, no CORS headers)
2. content type            → validation (400)
3. size limit              → validation (400)
4. JSON object parse       → validation (400)
5. form registered         → unknown_source (404)
6. rate limit              → rate_limited (429)
7. honeypot                → bot (403)
8. bot-check token         → bot (403)
                           → accepted (200)
```

**Event capture**

```
1. origin allowlist        → unknown_source (403, no CORS headers)
2. tracking enabled + key  → unknown_source (403)
3. content type            → validation (400)
4. size limit              → validation (400)
5. rate limit              → rate_limited (429)
6. consent evidence        → consent_missing (403)
7. batch parse (1–20)      → validation (400)
                           → accepted (202)
```

The two mirror each other on purpose, so the surfaces can't report different codes for the same class of problem. Note that `rate_limited` on the event side is returned **before** the consent check, so a throttled request tells you nothing about consent.
