# Early Verdict

With the early verdict, your backend can read a provisional verdict as soon as the first part of a collection reaches Octet, and the final verdict a few seconds later. Use the provisional verdict to keep a low-risk step moving, and read the final verdict before a gated action.

The early verdict is turned on per license. To get it, ask Octet. There is no collector option for it. A license without it works as described in the rest of these docs: one report per session, and version 3 tokens.

## What a provisional verdict means

A provisional verdict with `alarm` `none` means only that none of Octet's server-side checks fired. It never means the browser is clean. Its `confidence` is at most `0.7`.

| Verdict | What you can do with it |
|---|---|
| Provisional | Show a page or start a sign-up. Not enough on its own for a gated action, such as a first deposit, a withdrawal, or a KYC or geofencing gate. Read the final verdict first, with `?stage=final&waitMs=...`. |
| Final | Your decision for that session. |
| Final and incomplete | The last word for that session, but no stronger than a provisional verdict. The usual cause is a page that navigated away, so await `settled()` before navigating. |

If you use Octet for geofencing, wait for the final verdict, or don't use the early verdict.

## How it works

For a license with the early verdict, the collector sends each collection to your edge in two reports:

1. `ready()` resolves once the first report is in. From then on, your backend can fetch the provisional verdict.
2. The second report follows a few seconds later, and Octet then issues the final verdict.
3. If the second report never arrives, Octet closes the session as final and incomplete 8 seconds after the first report, with the provisional verdict's values. A missing second report never makes the verdict calmer. The usual cause is a page that navigated away before the second report was sent. See [Let the second report finish](#let-the-second-report-finish).

Which collector you run also matters:

- In `passive` mode, a session has a single report and goes straight to a final verdict.
- Collectors v1.6.0 to v1.8.x also send two reports for a license with the early verdict, but `ready()` resolves only after both, and they have no `settled()`.
- Collectors older than v1.6.0 send a single report.

`verify()` still resolves only after both reports.

## Fetch the verdict

Before a gated action, fetch the final verdict with `stage=final` and a `waitMs`:

```bash
curl -s "https://geo.octetproof.com/v1/verdict/$SESSION_REF?stage=final&waitMs=5000" \
  -H "Authorization: Bearer $OCTET_READ_TOKEN"
```

To read the provisional verdict, use `stage=provisional`:

| `stage` | Returns |
|---|---|
| `provisional` | The newest verdict for the session: the provisional verdict, or the final verdict once it exists. Read `stage` in the token to tell which. |
| `final`, or no `stage` | The final verdict. While only the provisional verdict exists, Octet answers `404` with `{ "status": "pending" }`. With a `waitMs` of up to `10000`, Octet waits for the final verdict first, as for any verdict. |

| Error | When |
|---|---|
| `400` `{ "error": "stage_not_allowed" }` | You sent `stage` for a license without the early verdict. |
| `400` `{ "error": "invalid_stage" }` | `stage` is not `provisional` or `final`. |

The rest of the request and the responses are as in [Fetch the Verdict](/docs/browser/integration/fetch-verdict/).

## Let the second report finish

The second report is sent after `ready()` resolves. If the page navigates away first, for example when the login form submits, the browser can cancel it, and the session closes as incomplete.

Call [`settled()`](/docs/browser/reference/collector-api/#settled), in collector v1.9.1 and later, before the page navigates away. It resolves once the collection has sent everything, and it doesn't reject for a failed collection. It rejects only if `start()` was never called:

```js
form.addEventListener('submit', async (event) => {
  event.preventDefault();
  try { await octet.ready(); } catch (err) { console.warn('octet', err); }
  try { await octet.settled(); } catch (err) { console.warn('octet', err); }
  form.submit();
});
```

## The signed token

A license with the early verdict gets version 4 tokens, with `typ` `octet-browser-verdict+jwt;v=4`. They carry every version 3 claim with the same meaning, are signed with the same keys, and are checked the same way. They add three claims:

| Claim | Type | Meaning |
|---|---|---|
| `stage` | string | `provisional` or `final`. In every version 4 token. |
| `revision` | integer | `1` or more, in every version 4 token. It counts up within one `sessionRef`, and a token with a higher `revision` replaces one with a lower `revision`. |
| `incomplete` | boolean | `true` when the second report never arrived. Only in a token with `stage` `final`, and absent otherwise. |

A session produces one of these:

- A provisional token, then a final one.
- A provisional token, then a final one with `incomplete` `true`.
- A single final token, in `passive` mode or from a collector older than v1.6.0.

Check `stage` before a gated action: a provisional token is not a final verdict. `octet-verify` 1.8.0 and later check version 4 tokens. See [Verify the Signed Token](/docs/browser/integration/verify-token/).

## Where to go next

- [Fetch the Verdict](/docs/browser/integration/fetch-verdict/). The request, the responses and how long a verdict lasts.
- [Verdicts](/docs/browser/concepts/verdicts/). How to read `country`, `confidence` and `alarm`.
- [Collector API](/docs/browser/reference/collector-api/). `ready()`, `settled()` and `verify()`.
