# Block trades from sanctioned countries

Your web app lets users place trades. Your obligations say a trade must not go through when the user is in certain countries. On this page, your backend fetches the verdict for the user's session when they submit a trade, and applies that rule.

## What you need

- Your edge running, and the collector on your trade page. The [Quickstart](/docs/browser/getting-started/quickstart/) sets up both.
- A read token. See [Credentials](/docs/browser/integration/credentials/#create-a-read-token).
- `verify-verdict.mjs` from [Verify the Signed Token](/docs/browser/integration/verify-token/#nodejs).

## 1. Collect while the trade page is open

When your server renders the trade page, it creates a `sessionRef`, gets a one-time challenge for it from Octet, and keeps the `sessionRef` against the user's session. It writes `EDGE_URL`, `sessionRef` and `challenge` into the page, as the [Quickstart](/docs/browser/getting-started/quickstart/) does. The page starts the collector, and waits for the collection to finish before it submits the trade:

```js
octet.start({ apiUrl: EDGE_URL, sessionRef, challenge });

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

## 2. Decide on your backend

When the trade arrives, look up the `sessionRef` you stored for this user's session. Fetch the verdict, verify its token, and decide from the verified claims:

```js
import { verifyVerdictToken } from './verify-verdict.mjs';

// Example list only. Build yours from your own obligations,
// and list every territory code your rule covers.
const BLOCKED = new Set(['IR', 'KP']);

async function tradeDecision(sessionRef) {
  let claims;
  try {
    // fetchVerdict is the function from the Quickstart.
    const verdict = await fetchVerdict(sessionRef);
    if (!verdict) return 'hold'; // no verdict for this session
    claims = await verifyVerdictToken(verdict.token, sessionRef);
  } catch (err) {
    return 'hold'; // the fetch failed, or the token didn't verify
  }

  const { country, alarm } = claims;
  if (alarm === 'medium') return 'hold';    // masked: country unreliable
  if (country === undefined) return 'hold'; // no country
  if (BLOCKED.has(country)) return 'block'; // on your list
  if (alarm === 'high') return 'hold';      // masked
  return 'allow';
}
```

`hold` stands for your own next step, such as a KYC check or a manual review. The order of the checks matters:

- At `medium`, the connection appears masked and `country` is not reliable, so the trade is held whatever `country` says.
- At `high`, `country` is still the country the user is in, so the list check runs before the masked check. A user in a listed country is blocked, and any other `high` session is held.
- An absent `country` means Octet could not determine one. Treat it as unknown, not as a pass.

Decide from the verified claims, not from the unsigned fields beside the token.

:::draft[To confirm before publishing]

The order of the checks is a policy choice: at `medium`, this draft holds the trade even when `country` is on the list. Confirm that is the example we want to show.

:::

## What each case returns

| The session | `country` | `alarm` | Decision |
|---|---|---|---|
| A user in Germany | `DE` | `none` | `allow` |
| A traveller, or a device set up for another country | the country Octet found | `low` | `allow`, unless `country` is on your list |
| A user on a VPN or proxy | not reliable | `medium` | `hold` |
| A user in a country on your list | for example `IR` | `none`, `low` or `high` | `block` |
| Octet could not determine a country | absent | any | `hold` |
| No verdict for the session, or the token didn't verify | | | `hold` |

`country` is a country-level result. A session in a sanctioned region inside a country, such as Crimea, comes back as `UA`. If your obligations cover a region like that, don't rely on the verdict alone. See [Sanctioned regions](/docs/browser/concepts/verdicts/#sanctioned-regions).

## 3. Keep a record

Store the token with each decision, so an auditor can check later what your backend saw. See [Keep Verdicts for Audit](/docs/browser/integration/keep-verdicts/).

## Related

- [Verdicts](/docs/browser/concepts/verdicts/): how to read `country`, `confidence` and `alarm`.
- [Fetch the Verdict](/docs/browser/integration/fetch-verdict/): the request, the responses and the timeouts.
- [Go-Live Checklist](/docs/browser/integration/go-live-checklist/).
