Screen API

Real-time sanctions, PEP, and transaction screening for regulated payment flows — one call at onboarding, one call per payment. Every decision returns machine-readable evidence and a durable audit ID.

https://api.celmus.comBase URL · TLS 1.2+

The API is JSON over HTTPS. Two calls cover most integrations: POST /v1/screen/entity for a party (a customer at onboarding, a beneficiary), and POST /v1/screen/transaction for a payment (both parties + corridor + amount). Both return a single decisionclear alert hold review — with the matches and rationale behind it.

Authentication

Every request carries a platform-issued API key in the Authorization header as a bearer token. Keys are environment-scoped: ssk_test_… for sandbox, ssk_live_… for production. Never expose a key in a browser or mobile client — call from your server.

Authorization: Bearer ssk_live_YOUR_SECRET_KEY

An unrecognized, revoked, or wrongly-scoped key fails closed with 401 / 403. The screening scope is screen:entity (it authorizes both the entity and transaction endpoints).

Quickstart

Screen a person by name. A clean name returns clear; a designated name returns hold with the matching lists.

# Screen a customer at onboarding
curl https://api.celmus.com/v1/screen/entity \
  -H "authorization: Bearer $SCREEN_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "subject": { "entity_type": "individual", "name": "Jane Ordinary" },
    "options": { "tenant_jurisdiction": "CA" }
  }'

Screen an entity

POST/v1/screen/entity

Screen a single party — an onboarding customer, a beneficiary, a counterparty — against sanctions, PEP, and (where an ownership graph is loaded) OFAC-50% derived blocking. Pass options.tenant_jurisdiction so the PEP determination can resolve foreign vs. domestic.

Request

FieldTypeNotes
subject.entity_typeenumindividual · organization · vessel · aircraft · crypto_address
subject.namestringRequired. Any script — Cyrillic, Arabic, CJK, etc. are romanized and matched.
subject.dobstringOptional. YYYY, YYYY-MM, or YYYY-MM-DD (partial dates supported).
subject.nationalitiesstring[]ISO 3166-1 alpha-2.
subject.addressesobject[]{ country, city?, postal_code?, lines?[] }
subject.identifiersobject[]{ id_type, issuing_country?, value } — passport, national_id, tax_id, lei, …
options.tenant_jurisdictionstringYour own jurisdiction (ISO-2). Resolves the PEP foreign/domestic + 5-year clock.
options.determination_triggerenumaccount_opening · periodic_review · trigger_fact

Response

{
  "audit_id": "7c1f…",          // durable evidence reference
  "decision": "hold",          // clear | alert | hold | review
  "matches": [{
    "entity_id": "…",
    "canonical_name": "Vladimir PUTIN",
    "score_adjusted": 98,
    "action": "block_eligible_hold",
    "restriction": "blocked",
    "programs": ["UKRAINE-EO14024"],
    "lists": [{ "source_short_code": "OFAC-SDN", "score": 98 }],
    "pep": { "is_pep": true, "classification": "foreign_pep", "edd_required": true },
    "derived": null          // present for OFAC-50% derived blocks
  }],
  "coverage_warnings": [],      // non-empty = screened against reduced coverage
  "capsule": { "capsule_id": "…", "state": "pending" },
  "compliance_profiles": [{ "profile_code": "CA-FINTRAC", "version": 1 }]
}
Fail closed

A review decision is not a match — it's an explicit refusal to assert the party is clear (a verification disagreement, or a sanctions source was stale/unavailable). Treat review like hold: stop and have a human look. A clear is the only verdict that moves money without review.

Screen a transaction

POST/v1/screen/transaction

Screen a payment: both parties are run through the entity engine, then transaction-level rules apply — ministerial directives (a transfer to/from a directive jurisdiction), travel-rule completeness, and the PEP threshold-transaction trigger. Amounts are normalized to the threshold currency before comparison.

Request

{
  "transaction": {
    "reference": "rmt_9f2c",
    "amount": 2500, "currency": "CAD",
    "direction": "outbound",        // inbound | outbound | internal
    "rail": "eft",                  // eft | wire | vc | prepaid | domestic
    "origin_country": "CA", "destination_country": "PH",
    "originator":  { "name": "Juan Dela Cruz", "account": "…", "address_present": true },
    "beneficiary": { "name": "Maria Santos", "addresses": [{ "country": "PH" }] }
  },
  "options": { "tenant_jurisdiction": "CA" }
}

For rail: "vc" add vasp_identified + wallet_present on each party (IVMS-101). For non-USD amounts pass transaction.fx_rates (units of USD per 1 unit, e.g. { "EUR": 1.08 }) so thresholds compare correctly.

Response (extra fields)

  "decision": "hold",
  "parties": [{ "role": "beneficiary", "decision": "clear", "is_pep": false }],
  "ministerial_directives": [{ "country": "IR", "directive": "IRAN-2020", "leg": "destination" }],
  "travel_rule": { "applicable": true, "complete": false, "missing": ["beneficiary.account"] },
  "pep_triggers": [],          // intl EFT/VC ≥ $100k involving a PEP
  "fx": { "normalized": { "amount": 1840, "currency": "USD" }, "approximate": false }

Decisions & actions

The top-level decision is the worst outcome across all matches and rules. Each match also carries an action describing what that specific hit warrants.

DecisionMeaning
clearNo hit, coverage confirmed. Safe to proceed automatically.
alertA discretionary hit (PEP, adverse media) or incomplete travel-rule data. Review, don't block.
holdA sanctions / asset-freeze hit or a ministerial directive. Stop the payment, route to a human.
reviewCould not confirm a clear (verification disagreement or reduced coverage). Treat as hold.
Match actionRestriction it reflects
block_eligible_holdblocked · directive_restricted · correspondent_restricted — funds-restricting.
alert_onlyPEP, watchlist, or an opaque-ownership flag — surfaces, never auto-blocks.
risk_signalAdverse media, jurisdiction risk, sub-50% control risk — informational.

The pep block

Present on a match when the party is a politically exposed person or a family member / close associate of one. It carries the FINTRAC determination — never merge it with the sanctions score in your UI; it's a separate obligation (enhanced due diligence, not a freeze).

FieldNotes
classificationforeign_pep (perpetual) · domestic_pep / hio (5-yr clock) · family_member · close_associate
statusactive · within_5y · lapsed · perpetual
edd_requiredWhether enhanced due diligence is owed.
determination_pendingtrue when tenant_jurisdiction was omitted — foreign/domestic couldn't be finalized, so it's never silently cleared.

The derived block

Present when a party is not directly listed but inherits a restriction because blocked persons own ≥ 50% of it (the OFAC 50% Rule). Populated once an ownership graph is loaded for your program.

FieldNotes
aggregate_pctAggregate blocked ownership (≥ 50 = derived block, inclusive).
chainThe ownership path behind the derivation, for the rationale.
opaque_intermediarytrue = an unresolved owner could tip it over 50% — flagged, never a clean pass.
control_riskSub-50% blocked ownership with control indicia — a non-blocking signal.

Errors

Errors use standard HTTP status codes with a application/problem+json body: { "title", "status", "detail" }. A screening failure (source unavailable, evidence unwritable) returns 503 and no decision — retry; never treat a failed screen as a clear.

StatusMeaning
400Malformed JSON body.
401Missing or invalid API key.
402Billing suspended — new screens paused (audit/export remain available).
403No tenant context, or the key lacks the required scope.
422Validation error — the detail lists each problem.
503Screening or evidence recording unavailable — retry, do not proceed.

Other endpoints

Beyond the two screening calls, the same key + platform expose the compliance workflow. Ask us for the full reference when you need them.

  • POST /v1/cdd/collect — beneficial-ownership capture (25%+ owners + control person + certification).
  • POST /v1/314a/scan — FinCEN 314(a) segregated, ephemeral match (subjects never persisted).
  • GET /v1/reporting/deadlines, /v1/reporting/events — the regulatory-reporting matrix (LPEPR, OFAC blocking, SAR/STR) with filing deadlines.
  • GET /v1/audit/cases — exam-production case search over the WORM evidence log.
  • GET /v1/coverage — coverage-truth: which lists, how fresh, what's unavailable.

Running in shadow mode

Running in shadow mode

Call the API in parallel with your existing controls and log what celmus would decide — it never blocks a live payment. Keep the call server-side, time-bounded, and wrapped so an error or timeout is swallowed. When the comparison data holds, flip specific checks to enforcing. Start with a ssk_test_ key; the endpoints, shapes, and decisions are identical in production.