Screening API

Perform real-time screening of individuals and organizations against global sanctions, watchlists, politically exposed persons (PEPs), and adverse media sources.

Live Mode Only

Real screening checks are only performed in live mode. Test mode returns mock results for development purposes.

What Gets Screened

  • Sanctions: Lists including OFAC, UN, EU, UK HMT, DFAT
  • Watchlists: Regulatory enforcement, disqualified directors
  • PEPs: Politically Exposed Persons and their relatives (RCAs)
  • Adverse Media: Negative news, fraud allegations, financial crime

How Screening Works

Screening is an asynchronous process that follows this workflow:

1. Submit
POST /screening/
Submit the entity for screening
2. Process
asynchronously
Potential risks are found
3. Get Results
Webhook or GET
Review risk profiles
One Screening Per Entity

Each entity can only be screened once. Subsequent requests return 409 Conflict with existing screening details.

Screen Entity

Submit an existing entity for KYC/AML screening. Screening runs asynchronously (usually a few seconds) with results delivered via webhook or by polling the results endpoint.

POST /api/v1/screening/
Idempotency Required

Include an Idempotency-Key header (16-255 characters) to ensure screening requests are processed exactly once. Use a unique key per entity to prevent duplicate screenings.

Request Body

Parameter Type Required Description
entity_id uuid Yes ID of existing entity to screen. Entity must belong to your tenant.
webhook_url string No Optional webhook URL for completion notification. Use tenant webhooks if not provided.

Success Response (202 Accepted)

{
  "screening_id": "62ee34a4-28f0-4a83-a016-7441e7900d7e",
  "entity_id": "716ce8e4-8c1f-4093-b4c7-9e897c2bb7cc",
  "name": "Test Company Ltd",
  "entity_type": "company",
  "screening_status": "pending",
  "message": "Test screening initiated (no credits charged, mock data will be returned)",
  "test_mode": true
}

Error Response - Already Screened (409 Conflict)

{
  "error": "Entity already screened",
  "detail": "Entity has existing screening with status: completed",
  "screening_id": "62ee34a4-28f0-4a83-a016-7441e7900d7e",
  "entity_id": "550e8400-e29b-41d4-a716-446655440000",
  "screened_at": "2026-01-15T10:30:00Z"
}

Get Screening Results

Retrieve screening results for an entity. Returns all screenings performed on the entity along with risk profiles (matches) detected by ComplyAdvantage.

GET /api/v1/screening/{entity_id}/

Response Structure

{
  "screening": {
    "id": "f00354f7-7762-4903-9200-4cf3342d1e59",
    "entity": {
      "id": "0f0e0942-83c9-4504-9b1e-aac9ac341602",
      "name": "John Doe",
      "entity_type": "individual"
    },
    "status": "completed",
    "risk_score": 3.5,
    "screening_type": "initial",
    "is_test_data": false,
    "creator_profile": {
      "id": "5c4fdd14-93c8-4653-9c2b-a45f58654599",
      "name": "Jane Smith",
      "email": "[email protected]"
    },
    "decider_profile": {
      "id": "5c4fdd14-93c8-4653-9c2b-a45f58654599",
      "name": "Jane Smith",
      "email": "[email protected]"
    },
    "created_at": "2026-01-07T14:35:36.258555+00:00",
    "updated_at": "2026-01-09T14:04:11.489236+00:00",
    "cases": [
      {
        "id": "c8f16c1d-8e55-4ade-afe4-dbc472f49a31",
        "title": "Case for John Doe - Onboarding",
        "status": "closed",
        "decision": "acceptable_risk",
        "assigned_to": null,
        "closed_by": {
          "id": "5c4fdd14-93c8-4653-9c2b-a45f58654599",
          "name": "Jane Smith",
          "email": "[email protected]"
        },
        "closed_at": "2026-01-09T14:04:11.448360+00:00",
        "created_at": "2026-01-07T14:35:39.224120+00:00",
        "updated_at": "2026-01-09T14:04:11.448618+00:00",
        "alerts": [
          {
            "id": "5bc52924-8a4f-48ae-acaa-13027deae060",
            "risk_profiles": [
              {
                "id": "f67f165d-0d1e-4eb7-81e6-3494a260dcd4",
                "name": "John Doe - Adverse Media, PEP",
                "entity_type": "individual",
                "status": "false_positive",
                "matching_score": 0.3,
                "matching_name": "John Doe",
                "match_type": [
                  "aka_exact",
                  "name_variations_removal"
                ],
                "dates_of_birth": [
                  "1975",
                  "1975-06-21"
                ],
                "also_known_as": [
                  "Johnathan Doe",
                  "John Doe",
                  "John Doe Jr.",
                  "John R. Doe"
                ],
                "all_related_countries": [
                  "AU",
                  "BR",
                  "IN",
                  "IT",
                  "MX",
                  "NG",
                  "US"
                ],
                "has_pep": true,
                "has_sanctions": false,
                "has_adverse_media": true,
                "has_watchlists": false
              }
            ]
          }
        ]
      }
    ]
  }
}

Risk Profile Fields (Summary View)

Field Type Description
id string (UUID) Unique risk profile identifier
name string Name of matched entity with risk types (e.g., "John Doe - PEP, Adverse Media")
entity_type string Type of entity: individual or company
status string Review status: not_reviewed, in_review, false_positive, true_positive
matching_score float Confidence score (0.0-100.0). Higher = stronger match. >80 = high confidence, <50 = likely false positive
matching_name string Exact name from watchlist/database that triggered the match
match_type array How match was made: name_exact, aka_exact, name_variations_removal, etc.
dates_of_birth array Known dates of birth for the matched entity (YYYY or YYYY-MM-DD format)
also_known_as array Aliases and alternative names for the matched entity
all_related_countries array Country codes (ISO 2-letter) associated with the risk
has_pep boolean True if PEP data exists (politically exposed person)
has_sanctions boolean True if sanctions data exists
has_watchlists boolean True if watchlist data exists
has_adverse_media boolean True if adverse media articles exist
Detailed View Available

Use GET /screening/{entity_id}/detailed/ to get full PEP, sanctions, media, and watchlist JSON data. Or use GET /risk-profiles/{id}/ to get individual risk profile with all sensitive data.

Understanding Results

Screening results are organized in a hierarchy. Understanding this structure is essential for processing results correctly.

Result Hierarchy

Screening (1 per entity)
├─ screening_id: UUID
├─ status: "completed" | "pending" | "failed"
├─ risk_score: 0-10 (initial risk assessment)
└─ Cases (0+ per screening)
    ├─ case_id: UUID
    ├─ title: "Case for [Entity] - Onboarding"
    ├─ status: "open" | "in_review" | "closed"
    └─ Alerts (1+ per case)
        ├─ alert_id: UUID
        └─ RiskProfiles (1+ per alert - the actual matches)
            ├─ id: UUID
            ├─ name: "John Smith - PEP, Sanctioned"
            ├─ matching_score: 0-100 (confidence)
            ├─ matching_name: "SMITH, John Alexander"
            ├─ aml_types: ["sanction", "pep"]
            ├─ countries: ["US", "GB"]
            ├─ dates_of_birth: ["1960-03-15"]
            ├─ has_pep: true
            ├─ has_sanctions: true
            ├─ has_watchlist: false
            └─ has_media: false
Focus on Risk Profiles

RiskProfiles are what matter - they represent actual matches from sanctions lists, PEP databases, or adverse media. Cases and Alerts are organizational containers.

Interpreting Risk Profiles

Field Purpose How to Use
matching_score Match confidence (0-100) >80 = high confidence, <50 = likely false positive
matching_name Name from watchlist Compare to your entity name to verify match quality
aml_types Types of risks found "sanction" = reject, "pep" = enhanced due diligence
has_sanctions Quick sanction check If true, typically auto-reject
has_pep Quick PEP check If true, trigger enhanced due diligence
dates_of_birth Known DOBs for match Cross-reference with your entity's DOB
also_known_as Aliases/AKAs Check if any match your entity's variations

Case Management

Cases are review containers created when screening finds potential matches. Update case status and decisions as you review the associated risk profiles.

Test/Live Mode Segregation

Test API keys can only access cases created from test screenings. Live API keys can only access cases from live screenings. This ensures complete data segregation between test and production environments.

Get Case Details

GET /api/v1/cases/{case_id}/

Retrieve full details for a specific case including status, decision, and assignment information.

Update Case

PATCH /api/v1/cases/{case_id}/

Request Body

Parameter Type Required Description
status string No Case status: open, in_review, closed
decision string No Final decision: no_risk, acceptable_risk, unacceptable_risk
Note: Setting a decision automatically closes the case and marks the screening as completed. No need to explicitly set status.

Batch Screening

Screen multiple existing entities in a single request (up to 10,000). Entities that have already been screened are automatically skipped. Perfect for periodic rescreening or bulk onboarding.

POST /api/v1/screening/batch/
Idempotency Required

Include an Idempotency-Key header (16-255 characters) to ensure batch screening jobs are processed exactly once. Use a unique key per batch operation.

Request Body

Parameter Type Required Description
entity_ids array Yes Array of entity UUIDs to screen (1-10,000). Duplicate IDs are automatically removed.
webhook_url string No Webhook URL for batch completion notification
webhook_progress boolean No If true, receive progress webhooks during processing. Default: false

Success Response (202 Accepted)

{
  "job_id": "a8d8f1e2-4c5e-4f6a-9b2c-3d4e5f6a7b8c",
  "status": "pending",
  "total_count": 2,
  "estimated_completion": "2026-01-28T10:45:00Z",
  "webhook_url": "https://your-domain.com/webhooks/batch-complete",
  "skipped_count": 1,
  "skipped_entity_ids": ["770e0622-e41d-34f6-c938-648877662222"],
  "message": "Screening 2 entities. Skipped 1 already screened."
}

Error Response (409 Conflict)

{
  "error": "All entities already screened",
  "already_screened_count": 3,
  "already_screened_entity_ids": [
    "550e8400-e29b-41d4-a716-446655440000",
    "660f9511-f30c-23e5-b827-537766551111",
    "770e0622-e41d-34f6-c938-648877662222"
  ]
}

Check Batch Status

Poll the batch job status endpoint to check progress:

GET /api/v1/screening/batch/{job_id}/
import time

job_id = "a8d8f1e2-4c5e-4f6a-9b2c-3d4e5f6a7b8c"

# Poll until complete
while True:
    response = requests.get(
        f'https://api.kycgenie.com/api/v1/screening/batch/{job_id}/',
        headers={'Authorization': 'Bearer your_api_key'}
    )
    
    status_data = response.json()
    print(f"Progress: {status_data['progress_percentage']}%")
    print(f"Processed: {status_data['processed_count']}/{status_data['total_count']}")
    
    if status_data['status'] in ['completed', 'failed']:
        break
    
    time.sleep(5)  # Wait 5 seconds before next poll
const jobId = 'a8d8f1e2-4c5e-4f6a-9b2c-3d4e5f6a7b8c';

// Poll until complete
while (true) {
  const response = await fetch(
    `https://api.kycgenie.com/api/v1/screening/batch/${jobId}/`,
    { headers: { 'Authorization': 'Bearer your_api_key' } }
  );
  
  const statusData = await response.json();
  console.log(`Progress: ${statusData.progress_percentage}%`);
  console.log(`Processed: ${statusData.processed_count}/${statusData.total_count}`);
  
  if (['completed', 'failed'].includes(statusData.status)) {
    break;
  }
  
  await new Promise(r => setTimeout(r, 5000));  // Wait 5 seconds
}

Batch Status Response

{
  "job_id": "a8d8f1e2-4c5e-4f6a-9b2c-3d4e5f6a7b8c",
  "status": "processing",
  "total_count": 3,
  "processed_count": 2,
  "failed_count": 0,
  "progress_percentage": 66.67,
  "created_at": "2026-01-28T10:30:00Z",
  "started_at": "2026-01-28T10:30:15Z",
  "completed_at": null,
  "webhook_url": "https://your-domain.com/webhooks/batch-complete"
}
Performance & Limits

Batch screening processes approximately 2 entities per minute. Maximum batch size is 10,000 entities. For larger datasets, split into multiple batches.