Docs/Octet Browser/Use Cases/Block trades from sanctioned countries

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

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 does. The page starts the collector, and waits for the collection to finish before it submits the trade:

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:

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.

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.

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.

TermsPrivacy© 2026 Understone, Inc. d/b/a Octet