Developer guide
UK sanctions API in Node.js: FCDO coverage and customer checks
Call the UK sanctions API from Node.js with native fetch. Select the FCDO UK Sanctions List and keep source references with each customer check.

On this page
Use FCDO coverage in a Node.js integration
Search your application’s configuration, fixtures and UI labels for legacy OFSI Consolidated List references. The UK Sanctions List publication explains the current government source and the January 2026 closure of the old consolidated list. A successful request to an old URL is not evidence of current coverage.
When using SanctionsKit, select the documented uk-sanctions source only after confirming availability. Keep its source identity with the result and create a new operation when changing coverage. Do not preserve an old source label merely because the surrounding JavaScript request code still works.
Prepare the synthetic exercise
Use the sandbox request to test a Node.js application migrating from an older OFSI-labeled integration. Keep old labels in historical results, but verify that the new operation records its selected source independently. Changing a UI label alone does not change which list a request checks.
| Requirement | Value or action |
|---|---|
| Runtime | Use Node.js 22 or later on a trusted server or CLI. This implementation uses the built-in fetch client and Node standard-library modules, so no SDK or npm dependency is required. |
| Client file | Save the client as screen.mjs. |
| Screening endpoint | POST /api/v1/screenings |
| SANCTIONSKIT_API_KEY | Sandbox key; screenings:write and results:read scopes |
| SCREENING_OPERATION_ID | Persist one operation ID; reuse only with the identical request body |
| request.json | Mira Calder: invented subject; sandbox@1: synthetic records |
{
"subject": {
"name": "Mira Calder",
"entityType": "person",
"birthDate": "1984"
},
"package": "sandbox@1",
"retention": "standard"
}Implement the Node.js request boundary
The native-fetch client discovers production sources and verifies uk-sanctions before submission. When migrating configuration, compare that exact source ID with the result’s coverage rather than checking whether the human-readable name contains UK or OFSI.
// Node.js 22+. Run on your trusted backend; no npm dependencies.
import { readFileSync } from 'node:fs';
const base = 'https://www.sanctionskit.com/api/v1';
async function main() {
const key = process.env.SANCTIONSKIT_API_KEY;
const operation = process.env.SCREENING_OPERATION_ID;
if (!key || !operation) throw new Error('Set your key and operation ID.');
const request = JSON.parse(readFileSync(process.argv[2] ?? 'request.json', 'utf8'));
const sandbox = request.package === 'sandbox@1';
const sourceIds = sandbox ? ['sandbox-synthetic'] : request.sources;
if (!Array.isArray(sourceIds) || !sourceIds.length) throw new Error('Select your sources.');
async function api(path, body) {
const response = await fetch(base + path, {
method: body ? 'POST' : 'GET', redirect: 'error', signal: AbortSignal.timeout(25000),
headers: { Authorization: 'Bearer ' + key,
...(body ? { 'Content-Type': 'application/json', 'Idempotency-Key': operation } : {}) },
...(body ? { body: JSON.stringify(body) } : {}),
});
const envelope = await response.json();
if (!response.ok) {
const code = /^[a-z0-9_]{1,80}$/.test(envelope?.error?.code ?? '') ? envelope.error.code : 'unknown';
throw new Error('API HTTP ' + response.status + ': ' + code);
}
return envelope?.data;
}
if (!sandbox) {
const sources = await api('/sources');
if (!Array.isArray(sources)) throw new Error('Invalid discovery response.');
for (const id of sourceIds) {
const found = sources.filter(source => source?.id === id);
const source = found[0];
if (found.length !== 1 || source.availability !== 'available' || source.fresh !== true ||
!Array.isArray(source.capabilities) || !source.capabilities.includes(request.subject.entityType)) throw new Error('Required source unavailable or incompatible.');
}
}
const result = await api('/screenings', request);
if (typeof result?.id !== 'string' || !result.id || result.environment !== (sandbox ? 'sandbox' : 'production') ||
!['potential_match', 'no_match'].includes(result.status) ||
!Array.isArray(result.matches) || (result.status === 'no_match') !== (result.matches.length === 0) ||
!Array.isArray(result.coverage) || result.coverage.length !== sourceIds.length ||
sourceIds.some(id => result.coverage.filter(item => item.sourceId === id && item.fresh === true).length !== 1)) {
throw new Error('Incomplete screening result.');
}
// Store result.id with your customer reference; use results:read to retrieve the evidence.
console.log(JSON.stringify({ id: result.id, status: result.status,
candidateCount: result.matches.length, sources: result.coverage.map(item => item.sourceId) }));
}
await main().catch(error => {
console.error((/^API HTTP [0-9]{3}: [a-z0-9_]{1,80}$/.test(error.message) ||
error.message === 'Required source unavailable or incompatible.')
? error.message : 'Screening incomplete. Check credentials, request and source coverage.');
process.exitCode = 1;
});Run the client
node screen.mjs request.jsonConfirm UK sanctions scope before production
The current government source is the FCDO UK Sanctions List. An old OFSI download or successful response from a retired integration is not a substitute for current coverage. Keep the UK list reference and stated measures visible when reviewing the candidate.
| Check | Required evidence |
|---|---|
| GET /api/v1/sources | Production key with sources:read; response data array |
| id | uk-sanctions |
| availability / fresh | available / true |
| capabilities | Contains the requested subject.entityType |
| currentVersion | Active version; retrieved_at and last_checked_at are not publication dates |
| lastImport | May describe an import that is not the active version |
| Missing or ineligible source | Inspect disabledReason; required coverage stays incomplete |
| Displayed client | Performs production discovery before screening |
curl --fail-with-body --max-time 25 --header "Authorization: Bearer $SANCTIONSKIT_API_KEY" "https://www.sanctionskit.com/api/v1/sources"Select uk-sanctions in the production request
| Setting | Production request |
|---|---|
| Key | Separate production credential with screenings:write |
| Coverage | sources: ["uk-sanctions"]; omit package |
| Operation ID | New ID for the changed body and environment |
| Usage | Production access and screening allowance apply |
| Subject | Only lawfully held, known identifying facts; no invented date precision |
{
"subject": {
"name": "Mira Calder",
"entityType": "person",
"birthDate": "1984"
},
"sources": [
"uk-sanctions"
],
"retention": "standard"
}Interpret UK sanctions evidence in this workflow
Store data.coverage beside the new operation so a reviewer can distinguish it from earlier OFSI-labeled history. data.id links to retained evidence; a later rename of an application field must not rewrite which source an earlier screening actually used.
| Fields in data.matches[] | Review use |
|---|---|
| record.id and record.sourceId | Retain the UK record reference and source identity when connecting the candidate to your customer. |
| record.names and birthDates | Compare original names and partial dates without inventing missing days or resolving similar names automatically. |
| record.designations | Display the available programs, legal references, effective dates and published wording beside the identity evidence. |
- data.id; data.coverage; data.versions; supplied disclaimer
- data.matches[]: record, assessment, evidence, conflicts
- GET /api/v1/results/{id}: results:read scope
- Retained evidence and case review
Verify the application behavior
| Condition | Next action |
|---|---|
| 401 / 403 | Correct credentials or scopes before retrying |
| coverage_unavailable / stale_coverage | Keep required coverage incomplete; no silent source substitution |
| Timeout after submission | Recover with the original Idempotency-Key and identical body |
| 409 | Investigate an operation-key conflict |
| 429 / 503 | Follow the specific error and Retry-After; an allowance cap may require a plan or usage change |
Contract and safe diagnostic references
| Reference | Use |
|---|---|
| error.code / error.requestId | Safe recovery diagnostics; never log the submitted subject, key or full candidate response |
| potential_match / no_match | Completed comparison outcomes; neither is an automatic business approval |
- Leave a legacy OFSI display label in a fixture and verify selection still uses uk-sanctions.
- Reject a response whose coverage names a different source.
- Keep a prior result unchanged when the new source configuration is saved.
Sources and references
Authority and technical sources
SanctionsKit documentation
Frequently asked questions
Must Node.js parse the UK government download for this tutorial?
No. The Node.js 22 client calls SanctionsKit and validates source discovery. Your work is the application boundary and review workflow; this example does not require an XML parser or government-feed import job.
Can an old OFSI label remain in a Node.js result history?
Preserve truthful historical context rather than rewriting earlier evidence. New checks should record the selected uk-sanctions source and current list context, so reviewers can distinguish a migration from a simple display-name change.
How does the Node.js UK example check source availability?
Before a production screening, it discovers the source and checks availability, freshness, and supported subject type. It then verifies the returned coverage includes the requested source. The API still checks eligibility when the screening arrives.
How can I test a UK source migration without a live subject?
Use invented subjects and mock the source-discovery and coverage responses. Confirm that the new source ID is required and that historical result labels remain intact. Those tests do not establish live source availability.