Developer guide
UN sanctions API in Node.js: screen and review UN candidates
Use the UN sanctions API in Node.js for listed people and entities. Keep UN references and aliases with results; vessels need separate coverage.

On this page
Screen UN-listed identities from Node.js
Call the API from your onboarding backend after validating the customer facts you already hold. Save the operation ID before the request and link the returned screening ID to the customer; use that ID to open the retained evidence for review.
Display the UN reference, committee or regime, relevant aliases and comparison evidence for each candidate. SanctionsKit owns the source ingestion and matching step, so this integration does not download or parse XML.
Prepare the synthetic exercise
Create a local response fixture with a weak UN alias and an unresolved identity assessment. Use the synthetic sandbox subject to exercise your Node.js review presentation. The alias should remain evidence to investigate, not an automatic reason to declare the customer identical to a listed person.
| 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 Node.js client discovers un-consolidated and submits the documented subject. There is no XML parsing step in this workflow. Keep committee references and alias quality in the authorized evidence view rather than flattening candidates into a list of matching strings.
// 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 UN sanctions scope before production
un-consolidated concerns the UN Security Council Consolidated List of people and entities. This selection does not include the separate 1718 designated-vessels list. Show the committee or regime context when reviewing measures; different entries do not necessarily carry identical restrictions.
| Check | Required evidence |
|---|---|
| GET /api/v1/sources | Production key with sources:read; response data array |
| id | un-consolidated |
| 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 un-consolidated in the production request
| Setting | Production request |
|---|---|
| Key | Separate production credential with screenings:write |
| Coverage | sources: ["un-consolidated"]; 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": [
"un-consolidated"
],
"retention": "standard"
}Interpret UN sanctions evidence in this workflow
Retain the permanent UN reference with data.id and data.coverage. Display weak versus strong alias information alongside assessment, evidence and conflicts. A weak alias alone does not establish identity, and national implementation may affect the subsequent business review.
| Fields in data.matches[] | Review use |
|---|---|
| record.id and record.designations[].legalReferences | Keep the permanent UN reference with the candidate and use the published references to review the listing. |
| record.list and record.designations[].programs | Show the committee or regime context; entries are not all subject to identical measures. |
| record.names[].quality and record.birthDates | Retain weak versus strong aliases and alternative or partial dates. A weak alias by itself does not establish identity. |
- 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 |
- Keep weak-alias quality visible in a synthetic candidate view.
- Reject a vessel-coverage assumption for a people-and-entities source.
- Preserve the UN reference while routing an unresolved candidate to review.
Sources and references
Authority and technical sources
SanctionsKit documentation
Frequently asked questions
Why does the Node.js UN example avoid downloading XML?
It is an API integration: SanctionsKit supplies source ingestion and matching, while your backend submits the subject and handles review. The Node.js code therefore validates coverage and response evidence rather than parsing government files.
Does un-consolidated include all UN vessel listings for Node.js?
No. This source covers the consolidated individuals and entities list and does not include the separate 1718 designated-vessels list. Check the source capabilities and required vessel coverage explicitly for a vessel workflow.
Can a weak UN alias alone establish identity in my Node.js app?
No. Keep the alias quality and other identity evidence visible. A weak alias on its own does not establish that your customer is the listed person. Send unresolved candidates to review with their committee and source references.
What should a Node.js review screen do with a weak UN alias?
Show its quality label and the other available evidence. A weak alias alone does not establish identity, so preserve an unresolved review state when the customer facts do not support a reliable conclusion.