# Get the code, the links and the headers of a message

> Every message that arrives comes with its one-time code, its links and its headers already extracted, with nothing to configure.
> https://facteur.eu/en/help/extraction

A test almost never needs the body of an email. It needs **one thing inside the
body**: a six-digit code, a confirmation link, a token. We extract it on reception and
you read it from a field. There is nothing to configure.

## The fields you get

| Field | What it carries |
|---|---|
| `otp` | The one-time code, when the message contains a single obvious one |
| `codes` | The other digit or character runs that look like codes |
| `links` | Every link in the message: its `href`, **unescaped**, and its `text` |
| `headers` | The headers, lowercased, so you can find your own |
| `text.body` / `html.body` | Both versions of the body, as sent |

## Following a link without unescaping it yourself

A link written inside HTML carries entities: `&amp;` where the URL has a `&`. A test
that follows a still-escaped link asks the server for a URL whose second parameter is
called `amp;next`. It then gets a page that is not the one it expected, and no error
says so.

The `href` values the API returns are unescaped. You can hand
`message.links[0].href` straight to `page.goto()` or `cy.visit()`.

## When `otp` is empty and `codes` is filled

`otp` is populated when the message contains **one** clear candidate.

As soon as there are several, say a confirmation code and a six-digit order number, we
leave `otp` empty and put both candidates in `codes`. Guessing which one is right
would give you a test that passes until the day the order number changes length.

When that happens, point at the value in a real message and let the platform write the
pattern: that is the subject of [Pointers](pointeurs).

## Matching a message to the run that triggered it

Set a header on the email your application sends (`X-Test-Id`, `X-Correlation-Id`),
and read it back as sent:

```js
const message = messages.find((m) => m.headers['x-test-id'] === runId);
```

Read the key **lowercased**, always. A header is case-insensitive on the wire:
comparing the key as you sent it gives you a test that works until the day your
sending library changes its capitalisation.

## Known limits

- **We do not guess your format.** A house identifier looks like nothing known. Use a
  pointer, see [Pointers](pointeurs).
- **We do not read the contents of attachments.** You get their name, size and
  checksum, see [Reading from tests](lire-un-message).
- **Nothing is extracted from a refused message.** Past your quota the message is
  neither stored nor parsed, see [Quotas and retention](quotas-et-retention).
- **Extraction happens after the SMTP acknowledgement.** A message therefore exists
  for a few milliseconds with no code and no links. Read the `status` field before the
  extracted ones, see [Reading from tests](lire-un-message).
