CorebanqCorebanq Developer Docs
KYCv1KYC Screening

Screening statistics

Aggregate counts over EVERY screening row in the tenant, not the caller's permitted subset. The handler resolves a user id and passes it down, but GetScreeningStats hands the repository only the date range — the id reaches no query and no RbacRecordType is set — so any caller with the endpoint grant sees the whole table's totals. GET /v1/kyc/screening is no better: it asks for CUSTOMER-record scoping through GetAllTotalInput and then applies the permitted customer ids to kyc.screening.id, so it answers with an empty page or an unscoped one. Neither route is a tenant boundary. THE DATE RANGE DOES NOT WORK. The handler runs the full shared query parser and hands the result to the service, which then looks for the keys date_from and date_to. The shared parser never emits those keys — its switch recognises limit, offset, sort, stack, distinct, filter, search, search_text and fill_gaps, plus any search.<field> key, and everything else hits default: return nil. So both are always nil and the statistics are ALWAYS COMPUTED OVER ALL TIME, whatever the caller sends. Every other query parameter is parsed and then discarded too: only the two date keys are read, and neither can arrive. DATE RANGE: THERE IS NO WAY TO ASK FOR ONE. GetScreeningStatsWithQuery reads queryMap["date_from"] and queryMap["date_to"] and passes the parsed pair to GetScreeningStats, so the service is built for a range — but the keys never arrive. parseStandardQueryParameter has cases for limit/offset, sort/stack/distinct/fill_gaps, search/search_text and filter, and everything else falls to default: return nil. ?date_from=2026-01-01 is therefore handled IDENTICALLY to ?banana=1: both are dropped, and both answer 200 with all-time totals. The two are deliberately NOT declared as parameters for that reason — declaring one would be a contract statement the code does not make, and a generated client would emit a dateFrom argument whose caller receives all-time figures rendered as a filtered range. Contrast offset, which IS declared here and on the list route: parseLimitOffset really parses it and ?offset=abc really is a 400 common.invalid_input, so it exists in the request contract even though FetchKYCScreenings never applies it. date_from has no such foothold.

GET
/v1/kyc/screening/stats

Authorization

bearerAuth
AuthorizationBearer <token>

JWT token from the authentication endpoint.

In: header

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/kyc/screening/stats"
{
  "total_screenings": 0,
  "total_matches": 0,
  "match_rate": 0.1,
  "by_entity_type": {
    "property1": 0,
    "property2": 0
  },
  "by_collection": {
    "property1": 0,
    "property2": 0
  },
  "by_status": {
    "property1": 0,
    "property2": 0
  },
  "recent_screenings": [
    {}
  ]
}
{
  "status": 400,
  "message": "Invalid input",
  "code": "common.invalid_input",
  "class": "validation"
}
{
  "status": 401,
  "message": "Unauthorized",
  "code": "common.unauthorized",
  "class": "business"
}
{
  "status": 403,
  "message": "No access to the record",
  "code": "common.rbac_no_rec_access",
  "class": "business"
}
{
  "status": 408,
  "message": "Failed to search KYC screening records",
  "code": "kyc_m.failed_to_search",
  "class": "temporary",
  "retryable": true
}
{
  "status": 429,
  "message": "Rate limit for 203.0.113.7 to POST:/v1/kyc/screening exceeded.",
  "code": "rate_limits_m.exceeded",
  "class": "temporary",
  "retryable": true
}
{
  "status": 500,
  "message": "Failed to retrieve KYC screening record",
  "code": "kyc_m.failed_to_get",
  "class": "business"
}

{
  "overall_status": "unhealthy",
  "message": "Service is shutting down",
  "timestamp": "2026-08-27T15:04:05Z"
}