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

# Forms

> Subscribe from a static site, a React application or a server, without a token.

A list's form takes a `POST` from any site, without a token. It is the only way to subscribe someone straight from a browser. The guide [Subscription forms](/contacts/subscription-forms) explains how to switch it on and gives the HTML ready to copy.

## The address

```
POST https://lekalao.example.com/subscribe/{the list's id}
```

It is shown in the list's **Settings** tab. It answers `404` until the form is switched on.

## The fields

| Field                     |                                                                            |
| ------------------------- | -------------------------------------------------------------------------- |
| `email`                   | Required.                                                                  |
| `first_name`, `last_name` | Optional.                                                                  |
| `locale`                  | The language this person reads in: `fr`, `en`, `pt-BR`…                    |
| `tags`                    | An array, 20 at most. Only tags **already created** on the list are added. |
| `website`                 | The bot trap: it must stay empty.                                          |

As `application/x-www-form-urlencoded`, `multipart/form-data` or `application/json`.

## The JSON answer

With `Accept: application/json`, the answer is JSON instead of a redirect:

| Code  | Body                                                                                        |
| ----- | ------------------------------------------------------------------------------------------- |
| `200` | `{"status": "subscribed"}`, or `{"status": "pending"}` when the confirmation has been sent. |
| `403` | The site it came from is not among the list's **allowed domains**.                          |
| `404` | The form is switched off.                                                                   |
| `422` | Invalid address (`errors.email`), or the plan's subscriber limit is reached.                |
| `429` | More than 10 submissions a minute from the same IP address.                                 |

A blocked address answers `pending`, like an ordinary subscription: the form never reveals that an address is on the suppression list.

## React

```tsx theme={null}
import { useState } from 'react';

const ACTION =
    'https://lekalao.example.com/subscribe/9d5c2a1e-8f3b-4c7a-9e21-3b8f0c6d4a12';

export function Newsletter() {
    const [state, setState] = useState<
        'idle' | 'sending' | 'pending' | 'subscribed' | 'error'
    >('idle');

    async function subscribe(event: React.FormEvent<HTMLFormElement>) {
        event.preventDefault();
        setState('sending');

        const form = new FormData(event.currentTarget);
        const response = await fetch(ACTION, {
            method: 'POST',
            headers: { Accept: 'application/json' },
            body: form,
        });

        if (!response.ok) return setState('error');

        const { status } = await response.json();
        setState(status);
    }

    if (state === 'pending')
        return <p>Check your inbox to confirm.</p>;
    if (state === 'subscribed') return <p>Thank you, you are subscribed.</p>;

    return (
        <form onSubmit={subscribe}>
            <input
                type="email"
                name="email"
                required
                placeholder="you@example.com"
            />
            <input type="hidden" name="locale" value={navigator.language} />
            <input
                type="text"
                name="website"
                tabIndex={-1}
                autoComplete="off"
                hidden
            />
            <button disabled={state === 'sending'}>Subscribe</button>
            {state === 'error' && <p>That did not work. Try again in a moment.</p>}
        </form>
    );
}
```

## From a server

Your server can call the form, but every subscription would then come from the same IP address and hit the limit of 10 a minute. On the server, use the [API](/developers/subscribers) with a token.

## Allowed domains

When the list names some, Lekalao compares the browser's `Origin` header (or `Referer`). Subdomains are accepted: `mybakery.example` covers `www.mybakery.example`. A call without those headers, from `curl` for example, is refused.
