Skip to main content
Glama
argus-screen

argus-screen

by sect0rial
README.md
# argus-screen

Screen Terminal is an HTTP API that checks a name against PEP, sanctions, entity, enforcement and watchlist records.
This repo holds the API spec, short client examples and a small MCP server. The screening engine and the data are not in this repo.

- API docs: https://screen.unbackoffice.com/docs
- Coverage, meaning every country and list and when each last refreshed: https://screen.unbackoffice.com/coverage
- Tiers: https://screen.unbackoffice.com/tiers
- Free key: https://screen.unbackoffice.com/key (sign in with Google or an email link)

## Try it

```sh
export SCREEN_API_KEY=...   # your key

curl -s https://screen.unbackoffice.com/v1/screen \
  -H "X-API-Key: $SCREEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Vladimir Putin", "dob": "1952-10-07", "country": "RU"}'
```
No key yet? The home page at https://screen.unbackoffice.com has a shared demo key in a ready-to-run curl command (5 calls a day per IP).

## Endpoints

Base URL `https://screen.unbackoffice.com`. Send the key in the `X-API-Key` header.

| Method | Path | Body |
|---|---|---|
| POST | `/v1/screen` | `name` (required, 2 to 200 chars, any script), `dob`, `country` (ISO-3166 alpha-2), `aliases` (up to 8), `city` |
| POST | `/v1/screen/batch` | `{"queries": [ ...up to 50 of the above... ]}` |

`dob` is best as `YYYY-MM-DD`. `DD/MM/YYYY`, `7 Oct 1952`, `YYYY-MM` and `YYYY` are also read. An unreadable date is ignored, never counted as a mismatch.
Companies and vessels go in `name` too.

The full spec is in [`openapi.json`](openapi.json).

## Response

Trimmed. Real responses carry more per match: positions, other names, sources, an explanation and the evidence.

```json
{
  "result_band": "strong",
  "match_count": 1,
  "sanctions_band": "strong",
  "pep_band": "strong",
  "watchlist_band": "none",
  "by_type": {
    "sanction": {"band": "strong", "match_count": 1, "top_score": 1.0, "top_name": "Vladimir Vladimirovich Putin"},
    "pep":      {"band": "strong", "match_count": 1, "top_score": 1.0, "top_name": "Vladimir Vladimirovich Putin"},
    "watchlist": {"band": "none", "match_count": 0, "top_score": null, "top_name": null}
  },
  "matches": [
    {
      "person_id": "…",
      "name": "Vladimir Vladimirovich Putin",
      "country": "RU",
      "date_of_birth": "1952-10-07",
      "match_score": 1.0,
      "match_band": "strong",
      "risk_types": ["sanction", "pep"],
      "pep_classification": "sanctioned",
      "matched_on": ["name", "country", "dob"],
      "signals": {"matched_name_form": "canonical"},
      "weak_aliases": [],
      "generated_aliases": []
    }
  ],
  "dob_check": {"input": "1952-10-07", "status": "ok", "read_as": "1952-10-07", "note": null},
  "screened_at": "2026-11-10T09:30:00Z",
  "data_as_of": "2026-11-10T01:00:00Z"
}
```

- Bands, highest first: `strong` (treat as a hit), `possible` (send to an analyst), `weak` (low similarity or a conflicting detail such as date of birth), `none`.
- `result_band` is the highest band of any match. Route on `sanctions_band`, `pep_band` and `watchlist_band`, because a sanctions hit can rank below a stronger PEP namesake.
- `by_type` gives the band, count and top match for each type: sanction, pep, rca, watchlist, debarment, regulatory, adverse_media, soe, other.
- `matches` is best first. `match_score` runs from 0 to 1.
- Sanctions hits are shown at every band so you can review them under your own policy.
- `screened_at` is when the screen ran. `data_as_of` is how fresh the lists were.

A batch returns `count`, `with_matches`, `with_sanctions_matches`, `with_pep_matches` and `results`, one result per name in order.

A match is a lead to review. It is not a finding about the person.

## Limits

- A free key has 100 screens with a work email, 25 with a personal one. They don't expire or reset. One name is one screen, so a batch of 20 uses 20.
- A batch is all or nothing. If the key can't cover every name, nothing is screened or charged.
- Invalid requests (`422`) are not charged.
- Rate limits: 30 requests a minute per key and 60 a minute per IP. Over that you get `429`.
- More screens: https://screen.unbackoffice.com/tiers

| Status | Meaning |
|---|---|
| 401 | Missing, invalid or revoked key |
| 402 | No screens left on the key |
| 403 | The key can't call that endpoint |
| 422 | Invalid request, e.g. a one-letter name or more than 50 names in a batch |
| 429 | Too many requests. Wait and retry |
| 503 | Temporarily unavailable. Retry shortly |

Errors are JSON with a `detail` field, e.g. `{"detail": "invalid X-API-Key"}`.

## Examples

Both read `SCREEN_API_KEY`, screen one name and print the bands and the top matches.

```sh
# Python 3.9+, needs requests
pip install requests
python examples/python/screen.py "Vladimir Putin" --dob 1952-10-07 --country RU

# TypeScript, Node 18+, no dependencies
npx tsx examples/typescript/screen.ts "Vladimir Putin" 1952-10-07 RU
```

## MCP server

A stdio MCP server that wraps the API, so an MCP client can screen names. It runs straight from GitHub with `npx`.
Each name it screens uses one screen from your key.

Tools:

- `screen_name`: `name`, optional `dob`, `country`, `kind` (`person`, `company` or `vessel`) and `full`. Returns the bands and the top matches, or the whole response with `full: true`.
- `screen_batch`: `queries`, 1 to 50 items with the same fields as `screen_name`.

`kind` is not an API field. It only changes what is sent, as on the website: for a company or vessel the date of birth is left out, and for a vessel the country too.

**Claude Code**

```sh
claude mcp add argus-screen -e SCREEN_API_KEY=your-key -- npx -y github:sect0rial/argus-screen
```

**Claude Desktop** (`claude_desktop_config.json`) and **Cursor** (`~/.cursor/mcp.json`, or `.cursor/mcp.json` in a project) take the same block:

```json
{
  "mcpServers": {
    "argus-screen": {
      "command": "npx",
      "args": ["-y", "github:sect0rial/argus-screen"],
      "env": { "SCREEN_API_KEY": "your-key" }
    }
  }
}
```

Restart the client after editing the file.

From a clone:

```sh
npm install
npm run build          # compiles mcp/src to mcp/dist (mcp/dist is committed, so npx needs no build step)
npm run verify         # starts the server, runs initialize and tools/list, calls screen_name once
```

`SCREEN_API_URL` overrides the base URL. It defaults to `https://screen.unbackoffice.com`.

## openapi.json

Saved from the live service and pretty-printed:

```sh
curl -s https://screen.unbackoffice.com/openapi.json | python3 -m json.tool --indent 2 --no-ensure-ascii > openapi.json
```

Fetched on 10 November 2026. The live document is the reference if the two differ.

## What is not here

The screening engine, the matching rules and the list data run on Screen Terminal's servers. They are not in this repo and not under its licence.

## Licence

The code in this repo is MIT licensed. See [LICENSE](LICENSE).

TDQS

A4.4/5.0

Scored across 2 tools

Disambiguation5/5

The two tools are clearly differentiated: screen_name handles one name, while screen_batch handles up to 50 names in a single call. Their descriptions explicitly state the scope and endpoint, leaving no plausible confusion about which tool to use.

Naming Consistency5/5

Both names follow a consistent snake_case pattern with the shared `screen_` prefix. The suffixes distinguish single-name versus batch operations predictably.

Tool Count4/5

Two tools are reasonable for a narrow screening API, with a clean single-versus-batch split. However, the surface is quite thin and could feel limited if agents expect quota checks or a way to retrieve prior screening results.

Completeness4/5

The core screening workflow is covered for both individual and bulk names, including result bands and top matches. Minor gaps remain, such as checking remaining quota, retrieving a prior screening by ID, or handling batch status separately.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues