Best Practices
Recommendations for optimizing your sanctions screening workflow with SanctionsWise API.
Threshold Configuration​
Recommended Thresholds by Use Case​
| Use Case | Threshold | Rationale |
|---|---|---|
| High-volume payments | 0.90-0.95 | Minimize false positives, fast throughput |
| Standard KYC | 0.85 | Balanced accuracy and review volume |
| Enhanced Due Diligence | 0.70-0.80 | Catch more potential matches |
| Investigation | 0.60-0.70 | Maximum coverage, manual review expected |
| Watchlist monitoring | 0.80 | Daily batch against customer base |
Dynamic Threshold Strategy​
Consider adjusting thresholds based on context:
def get_threshold(transaction_amount, customer_risk_level):
"""Dynamic threshold based on risk context."""
# High-value transactions get lower threshold (more scrutiny)
if transaction_amount > 100000:
base_threshold = 0.70
elif transaction_amount > 10000:
base_threshold = 0.80
else:
base_threshold = 0.90
# Adjust for customer risk level
risk_adjustments = {
"low": 0.05, # Higher threshold = less scrutiny
"medium": 0.00,
"high": -0.10, # Lower threshold = more scrutiny
"pep": -0.15 # Politically exposed persons
}
return max(0.60, min(0.95, base_threshold + risk_adjustments.get(customer_risk_level, 0)))
Entity Type Optimization​
Always Specify Entity Type​
Providing entity_type improves matching accuracy by 5-10%:
{
"name": "Bank of Russia",
"entity_type": "organization"
}
Supported Types:
individual- Personsorganization- Companies, banks, government entitiesvessel- Ships, boatsaircraft- Planes, helicoptersunknown- Let the engine auto-detect (slower)
Type Detection Helper​
def determine_entity_type(name, context):
"""Determine entity type from context clues."""
# Organization indicators
org_indicators = ["corp", "inc", "llc", "ltd", "bank", "ministry", "government"]
if any(ind in name.lower() for ind in org_indicators):
return "organization"
# Vessel indicators
vessel_indicators = ["vessel", "ship", "mv ", "ss "]
if any(ind in name.lower() for ind in vessel_indicators):
return "vessel"
# Default to individual
return "individual"
Identifier Matching​
Include Identifiers When Available​
Identifiers provide the highest-confidence matches (+15% boost):
{
"name": "John Smith",
"entity_type": "individual",
"identifiers": {
"passport": "AB1234567",
"date_of_birth": "1965-03-15",
"national_id": "123-45-6789"
}
}
Supported Identifier Types​
| Type | Aliases | Example |
|---|---|---|
passport | passport_number, travel_document | AB1234567 |
national_id | ssn, id_number | 123-45-6789 |
tax_id | tin, ein, vat_number | 98-7654321 |
registration | company_number, business_registration | 12345678 |
date_of_birth | dob | 1965-03-15 |
Normalize Identifiers Before Sending​
import re
def normalize_identifier(id_value):
"""Remove formatting from identifiers."""
# Remove spaces, dashes, dots
return re.sub(r'[\s\-\.]', '', str(id_value).upper())
# Example: "AB-123-456" -> "AB123456"
Batch Screening Optimization​
When to Use Batch vs Single​
| Scenario | Recommended | Rationale |
|---|---|---|
| Less than 5 entities | Single requests | Lower latency per entity |
| 5-100 entities | Batch request | Amortized setup cost |
| More than 100 entities | Multiple batch requests | API limit is 100 per batch |
| Real-time payment | Single request | Immediate response needed |
| Daily watchlist check | Batch request | Throughput over latency |
Efficient Batch Processing​
def screen_large_list(entities, batch_size=100):
"""Screen large entity lists efficiently."""
results = []
for i in range(0, len(entities), batch_size):
batch = entities[i:i + batch_size]
response = requests.post(
f"{BASE_URL}/screen/batch",
headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
json={
"entities": batch,
"options": {"threshold": 0.85, "max_results_per_entity": 3}
}
)
batch_results = response.json()
results.extend(batch_results["results"])
# Log progress
print(f"Processed {min(i + batch_size, len(entities))}/{len(entities)}")
return results
Response Handling​
Status-Based Workflow​
def process_screening_result(result):
"""Process screening result based on status."""
screening_id = result["screening_id"]
status = result["status"]
if status == "clear":
# No matches - safe to proceed
log_decision(screening_id, "APPROVED", "No sanctions matches")
return {"action": "proceed", "review_required": False}
elif status == "match":
# High-confidence match - block immediately
match = result["matches"][0]
log_decision(screening_id, "BLOCKED",
f"High-confidence match: {match['matched_name']} ({match['confidence_score']:.0%})")
escalate_to_compliance(result)
return {"action": "block", "review_required": False}
elif status == "potential_match":
# Potential match - queue for review
log_decision(screening_id, "PENDING_REVIEW",
f"{result['match_count']} potential matches")
queue_for_manual_review(result)
return {"action": "hold", "review_required": True}
else:
# Unexpected status - treat as needing review
log_decision(screening_id, "ERROR", f"Unexpected status: {status}")
return {"action": "hold", "review_required": True}
Store Evidence Chain​
Always store the complete response for compliance:
def store_screening_record(result):
"""Store complete screening record for audit trail."""
record = {
"screening_id": result["screening_id"],
"timestamp": result["timestamp"],
"query": result["query"],
"status": result["status"],
"match_count": result["match_count"],
"matches": result["matches"],
"data_freshness": result["data_freshness"],
"processing_time_ms": result["processing_time_ms"]
}
# Store in your audit database
save_to_audit_log(record)
return record
Security Recommendations​
API Key Management​
- Never hardcode API keys - Use environment variables or secrets manager
- Rotate keys regularly - At least quarterly
- Use separate keys per environment - Dev, staging, production
- Monitor API key usage - Alert on unusual patterns
import os
# Load from environment
API_KEY = os.environ.get("SANCTIONSWISE_API_KEY")
if not API_KEY:
raise ValueError("SANCTIONSWISE_API_KEY environment variable not set")
Data Handling​
- Don't log full responses in production - Mask sensitive data
- Encrypt stored screening results at rest
- Implement audit logging for all API calls
- Set data retention policies per regulatory requirements
Testing Recommendations​
Test with Known Entities​
def test_sanctions_screening():
"""Test screening with known OFAC entries."""
# Test clear case
clear_result = screen_entity("John Smith", "individual", 0.85)
assert clear_result["status"] == "clear"
# Test match case
match_result = screen_entity("Vladimir Putin", "individual", 0.60)
assert match_result["status"] in ["match", "potential_match"]
assert match_result["match_count"] > 0
# Test organization
org_result = screen_entity("Bank of Russia", "organization", 0.60)
assert org_result["status"] in ["match", "potential_match"]
Related Documentation​
- API Reference - Complete endpoint documentation
- Matching Algorithms - How matching works
- Evidence Chain - Audit trail documentation
SanctionsWise API is a product of OrchestraPrime LLC