{"openapi":"3.1.0","info":{"title":"APZWatch API","version":"1.0.0","description":"AML screening API for persons, entities, wallets, addresses, and identifiers. Base path /api/v1. Deprecation policy: breaking changes require a new major version path (/api/v2) with at least 6 months' notice via Deprecation/Sunset headers.","contact":{"name":"APZOR Pty LTD","url":"https://apzwatch.apzportal.apzor.com"}},"servers":[{"url":"https://apzwatch.apzportal.apzor.com/api/v1"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"azw_live_… / azw_test_…"}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":8,"maxLength":255,"pattern":"^[A-Za-z0-9._~\\-:]+$"},"description":"Scoped per organisation. Same key + same body returns the original result; same key + different body → 409."},"RequestId":{"name":"X-Request-Id","in":"header","required":false,"schema":{"type":"string","minLength":8,"maxLength":128}}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"requestId":{"type":"string"},"details":{"type":"object"}},"required":["code","message","requestId"]}}},"ListSnapshot":{"type":"object","properties":{"jurisdiction":{"type":"string"},"listName":{"type":"string"},"listCode":{"type":"string"},"versionId":{"type":"string"},"effectiveDate":{"type":"string","format":"date"},"publishedAt":{"type":"string","format":"date-time"},"fetchedAt":{"type":"string","format":"date-time"},"recordCount":{"type":"integer"},"contentHash":{"type":"string","pattern":"^sha256:[a-f0-9]{64}$"},"sourceUrl":{"type":"string","nullable":true}}},"ScreenStatus":{"type":"string","enum":["clear","review","block_recommended"]},"ScreenResult":{"type":"object","description":"Live screen outcome. coverageGaps lists reference datasets not yet ingested (honest signalling).","properties":{"resultId":{"type":"string"},"status":{"$ref":"#/components/schemas/ScreenStatus"},"subjectType":{"type":"string"},"riskScore":{"type":"integer","minimum":0,"maximum":100,"description":"Org-weighted composite from hit categories × match bands"},"hits":{"type":"array","items":{"type":"object"}},"hitsMeta":{"type":"object","properties":{"returned":{"type":"integer"},"total":{"type":"integer"},"hasMore":{"type":"boolean"},"limit":{"type":"integer"}}},"listVersions":{"type":"array","items":{"$ref":"#/components/schemas/ListSnapshot"}},"coverageGaps":{"type":"array","items":{"type":"string"},"description":"e.g. pep_catalogue, adverse_media_corpus, compromised_credential_corpus"},"policy":{"type":"object"},"createdAt":{"type":"string","format":"date-time"},"disclaimer":{"type":"string"}}},"MatchBand":{"type":"string","enum":["strong","likely","possible","unlikely"],"description":"Multi-factor band. Exact name alone is never strong — needs DOB, country, ID, or wallet corroboration."}}},"security":[{"bearerAuth":[]}],"paths":{"/health":{"get":{"security":[],"summary":"Liveness","responses":{"200":{"description":"OK"}}}},"/health/ready":{"get":{"security":[],"summary":"Readiness (DB + list freshness)","responses":{"200":{"description":"Ready"},"503":{"description":"Degraded or unavailable"}}}},"/me":{"get":{"summary":"Organisation + key scopes","responses":{"200":{"description":"OK"},"401":{"description":"Unauthorized"}}}},"/usage":{"get":{"summary":"Usage meters vs plan quotas","responses":{"200":{"description":"OK"}}}},"/lists":{"get":{"summary":"Current auditable list snapshots","responses":{"200":{"description":"OK"}}}},"/lists/{code}":{"get":{"summary":"List detail + recent sync history","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","enum":["ofac_sdn","un_sc","eu_fsf","uk_sanctions","za_tfs"]}}],"responses":{"200":{"description":"Current snapshot and sync history"},"404":{"description":"Unknown list code"}}}},"/screens/person":{"post":{"summary":"Screen a person","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["fullName"],"properties":{"fullName":{"type":"string"},"givenName":{"type":"string"},"familyName":{"type":"string"},"dateOfBirth":{"type":"string"},"nationality":{"type":"string","minLength":2,"maxLength":2},"countryOfResidence":{"type":"string","minLength":2,"maxLength":2},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"passportNumber":{"type":"string"},"nationalId":{"type":"string"},"externalReference":{"type":"string"},"categories":{"type":"array","items":{"type":"string","enum":["sanctions","terror","pep","adverse_media","compromised","watch"]}}}}}}},"responses":{"200":{"description":"Screen result"},"409":{"description":"Idempotency conflict"},"422":{"description":"Validation error"},"429":{"description":"Rate limited"}}}},"/screens/entity":{"post":{"summary":"Screen a legal entity","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["legalName"],"properties":{"legalName":{"type":"string"},"country":{"type":"string","minLength":2,"maxLength":2},"registrationNumber":{"type":"string"}}}}}},"responses":{"200":{"description":"Screen result","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ScreenResult"}}}}}}}}},"/screens/wallet":{"post":{"summary":"Screen a digital-asset wallet","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Screen result (coverageGaps empty for wallet)"}}}},"/screens/address":{"post":{"summary":"Screen a postal address","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Screen result (OFAC + ZA-TFS addresses)"}}}},"/screens/compromised":{"post":{"summary":"Screen an identifier","description":"Returns coverageGaps including compromised_credential_corpus until a licensed pack is ingested.","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Screen result"}}}},"/screens/person/batch":{"post":{"summary":"Batch person screens (max 20)","description":"Each subject meters and persists independently. Optional per-subject idempotencyKey.","responses":{"200":{"description":"{ count, results[] }"},"422":{"description":"Validation"}}}},"/screens/{resultId}":{"get":{"summary":"Retrieve stored screen evidence","parameters":[{"name":"resultId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Stored result"},"404":{"description":"Not found"}}}},"/screens/{resultId}/hits":{"get":{"summary":"Paginate persisted hits (cursor = last rank)","parameters":[{"name":"resultId","in":"path","required":true,"schema":{"type":"string"}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":25}}],"responses":{"200":{"description":"Hit page"},"404":{"description":"Not found"}}}},"/ai/adverse-media":{"post":{"summary":"Adverse-media multi-signal discovery + advisory summary","description":"With search:true (default when excerpts omitted), discovers articles via local cache + Google News RSS + trusted RSS + GDELT (GDELT is one signal, not sole source). Results are ranked by source reliability × severity × entity-match confidence. Persists into adverse_media_articles for repeatable audits. Advisory only — never a sanctions/PEP determination. AI_ENABLED_GLOBAL=false → 503. Prefer excerpts[] for deterministic CI.","responses":{"200":{"description":"Articles + themes + confidenceScore + signals meta + optional AI summary (may be degraded)"},"503":{"description":"AI globally disabled"}}}},"/ai/hits/{hitId}/assist":{"post":{"summary":"Hit explanation assist (advisory)","description":"Requires ai:assist. Explains why a persisted hit matched; never a determination. AI_ENABLED_GLOBAL=false → 503.","parameters":[{"name":"hitId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Advisory explanation"},"404":{"description":"Hit not found"},"503":{"description":"AI globally disabled"}}}},"/cases":{"get":{"summary":"List cases","description":"Requires results:read.","responses":{"200":{"description":"OK"}}}},"/cases/{caseId}":{"get":{"summary":"Get case detail","responses":{"200":{"description":"OK"}}},"patch":{"summary":"Dispose / close a case","description":"Emits case.disposition webhook.","responses":{"200":{"description":"OK"}}}},"/cases/{caseId}/export":{"get":{"summary":"Export case evidence pack","responses":{"200":{"description":"JSON pack"}}}},"/audit/export":{"get":{"summary":"Export audit log","responses":{"200":{"description":"JSON events"}}}},"/graph/{profileId}":{"get":{"summary":"List relationship neighbors for an entity profile","description":"Requires results:read. Edges are derived (e.g. sharesAddressWith) after list ingest.","responses":{"200":{"description":"Neighbors"}}}},"/monitor":{"get":{"summary":"List continuous-monitoring watches","description":"Requires monitor:manage.","responses":{"200":{"description":"OK"}}},"post":{"summary":"Create watch + initial screen","description":"Requires monitor:manage. subjectType person|entity|wallet. Rescreened after list ingest; status worsenings emit monitor.alert.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["subjectType","input"],"properties":{"subjectType":{"type":"string","enum":["person","entity","wallet"]},"input":{"type":"object"},"externalReference":{"type":"string"}}}}}},"responses":{"201":{"description":"Created with baseline screen"}}}},"/monitor/{watchId}":{"post":{"summary":"Force rescreen a watch","description":"Requires monitor:manage.","responses":{"200":{"description":"OK"}}},"delete":{"summary":"Disable a watch","description":"Requires monitor:manage.","responses":{"200":{"description":"Disabled"}}}},"/webhooks":{"get":{"summary":"List webhooks","responses":{"200":{"description":"OK"}}},"post":{"summary":"Create webhook","description":"Requires webhooks:manage. Events are HMAC-signed on delivery (includes monitor.alert).","responses":{"201":{"description":"Created"}}}}}}