Skip to main content

Overview

Face Search is a 1:N biometric search: Didit takes the largest detected face in your user_image and searches it against your application’s face search index — faces enrolled from verification sessions, saved standalone API calls, direct user-profile face uploads, and your block/allow lists. It is distinct from Face Match, which is 1:1 — Face Match compares a selfie against the portrait on a specific document, while Face Search asks “have we seen this person anywhere before?”. Two behaviors are worth understanding before you parse the report:
  • Only blocklist matches decline. face_search.status is Declined only when a FACE_IN_BLOCKLIST or POSSIBLE_FACE_IN_BLOCKLIST warning fires. Duplicate matches alone return Approved — inspect matches and warnings, not just status.
  • Optional persistence and enrollment. When save_api_request=true (the default), the call is persisted as an API-type session and the searched face is enrolled into your face search index. Faces enrolled by Face Search calls are excluded from future Face Search results, so repeated searches never match each other. Pass save_api_request=false for a one-shot query: nothing is stored and the face is not enrolled.

Where the results appear

Face Search is a standalone API. The search results are returned synchronously, in the POST /v3/face-search/ response only, as a singular face_search object at the top level:
POST /v3/face-search/  ──▶  { "request_id": …, "face_search": { … }, "vendor_data": …, "metadata": …, "created_at": … }
There is no plural face_searches[] array anywhere — the plural-array convention used by id_verifications[], aml_screenings[], and liveness_checks[] on the session decision endpoint does not apply. When save_api_request=true (the default), the call is also persisted as an API-type session:
  • Business Console — the session appears in your sessions list with the search image, matches, and warnings.
  • GET /v3/session/{sessionId}/decision/ — retrievable using the returned request_id as the session id. The persisted data surfaces as a FACE_SEARCH entry in features[] and as a liveness_checks[] item carrying the stored matches (with freshly signed match_image_url values) and warningsnot as a face_search object.
When save_api_request=false, request_id is a transient correlation UUID: the session is never stored, so it cannot be fetched from the Console or the decision endpoint.

Schema

See Face Search in the Data Models reference for the canonical field tables. The shape below reflects what the endpoint actually returns at runtime.
interface FaceSearchResponse {
  request_id: string;            // UUID — the persisted session id when save_api_request=true
  face_search: {
    status: 'Approved' | 'Declined';
    total_matches: number;       // 0-5, length of matches[]
    matches: FaceSearchMatch[];  // capped at 5, ordered by similarity
    user_image: {                // always present
      entities: {
        bbox: [number, number, number, number];
        confidence: number;      // 0-1, one entry per detected face
      }[];
      best_angle: 0 | 90 | 180 | 270;
    };
    warnings: Warning[];
  };
  vendor_data: string | null;    // echoed from the request
  metadata: object | null;       // echoed from the request
  created_at: string;            // ISO 8601, e.g. "2026-06-12T01:04:42.763237+00:00"
}

interface FaceSearchMatch {
  session_id: string | null;     // UUID of the matched session; null for non-session matches
  session_number: number | null; // Console-friendly number; null for non-session matches
  similarity_percentage: number; // 0-100
  source: 'session' | 'imported' | 'list_entry';
  vendor_data: string | null;    // the matched session's (or imported profile's) vendor_data
  verification_date: string | null;  // "YYYY-MM-DDThh:mm:ssZ"; null for list_entry matches
  user_details: {
    full_name: string | null;
    document_type: string | null;
    document_number: string | null;
  } | null;                      // null when the matched session has no document data
  match_image_url: string;       // signed URL when save_api_request=true; raw storage path otherwise
  status: 'Approved' | 'Declined' | 'In Review' | null;  // the matched session's status
  is_blocklisted: boolean;
  is_allowlisted: boolean;
  api_service: 'ID_VERIFICATION' | 'FACE_MATCH' | 'AGE_ESTIMATION' | 'POA' | 'AML'
             | 'PASSIVE_LIVENESS' | 'DATABASE_VALIDATION' | 'PHONE_VERIFICATION'
             | 'EMAIL_VERIFICATION' | null;  // null when the match comes from a workflow session
}

interface Warning {
  risk: string;
  feature: 'LIVENESS';           // always LIVENESS on this endpoint
  additional_data: object | null;
  log_type: 'error' | 'warning' | 'information';
  short_description: string;
  long_description: string;
}
Field notes:
  • matches[] is capped at 5 entries above the similarity floor, ordered by descending similarity (search_type=blocklisted_or_approved ranks blocklisted entries first, then allowlisted, then similarity).
  • source tells you where the matched face was enrolled from: session — a verification session or a saved standalone API call; imported — a face uploaded directly to a user profile; list_entry — a face attached to one of your lists. session_id, session_number, status, and api_service are null for imported and list_entry matches.
  • api_service is the matched session’s standalone API type (uppercase, e.g. PASSIVE_LIVENESS) and null when the matched face came from a regular workflow verification session.
  • user_details comes from the matched session’s document verification. For source: imported matches it is populated from the user profile instead (full_name set, document_type/document_number null); it is null when no identity data exists (for example liveness-only sessions).
  • Warnings use a reduced shape compared with workflow warnings: there is no node_id. The documented response contract (FaceSearchResponseSerializer) declares the five fields risk, additional_data, log_type, short_description, long_description; the runtime payload additionally carries feature, which is always "LIVENESS" here. See Face Search warnings.
  • Same-user matches are not excluded — unlike workflow duplicate detection, the standalone endpoint does not filter out prior sessions with the same vendor_data, so a returning legitimate user matches their own earlier sessions.

Status values

The top-level face_search.status is derived from warnings[]. The match-level status reflects the matched session’s verification outcome and is independent.
ValueWhen it appears
ApprovedNo blocklist warning fired. Includes the case where duplicate matches were found — duplicates are informational signals, not declines.
DeclinedA FACE_IN_BLOCKLIST or POSSIBLE_FACE_IN_BLOCKLIST warning fired — the search image matched a face in your face blocklist.
Note: when the search image contains no detectable face the API returns HTTP 400 with {"error": "No face detected in the image"} before a face_search object is built — there is no Declined status for that case. Insufficient credits return HTTP 403 before any image processing.

Examples

Approved — duplicate match, no blocklist

{
  "request_id": "5e0c3a1f-7b2d-4c8e-9f10-2a3b4c5d6e7f",
  "face_search": {
    "status": "Approved",
    "total_matches": 1,
    "matches": [
      {
        "session_id": "1f2e3d4c-5b6a-7980-1122-334455667788",
        "session_number": 1024,
        "similarity_percentage": 87.42,
        "source": "session",
        "vendor_data": "user-9",
        "verification_date": "2025-11-20T09:15:00Z",
        "user_details": {
          "full_name": "Jane Marie Doe",
          "document_type": "Passport",
          "document_number": "X1234567"
        },
        "match_image_url": "https://<media-host>/face/9c8b7a6d/reference.jpg?signature=...",
        "status": "Approved",
        "is_blocklisted": false,
        "is_allowlisted": false,
        "api_service": null
      }
    ],
    "user_image": {
      "entities": [
        {
          "bbox": [40, 40, 120, 120],
          "confidence": 0.732973
        }
      ],
      "best_angle": 0
    },
    "warnings": [
      {
        "risk": "DUPLICATED_FACE",
        "feature": "LIVENESS",
        "additional_data": {
          "duplicated_session_id": "1f2e3d4c-5b6a-7980-1122-334455667788",
          "duplicated_session_number": 1024,
          "api_service": null
        },
        "log_type": "information",
        "short_description": "Duplicated face from other approved session",
        "long_description": "The system identified a duplicated face from another approved session, requiring further investigation."
      }
    ]
  },
  "vendor_data": "user-123",
  "metadata": null,
  "created_at": "2026-06-12T01:04:42.763237+00:00"
}

Declined — blocklist match

{
  "request_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
  "face_search": {
    "status": "Declined",
    "total_matches": 1,
    "matches": [
      {
        "session_id": "882c42d5-8a4d-4d20-8080-a22f57822c86",
        "session_number": 323442,
        "similarity_percentage": 99.99,
        "source": "session",
        "vendor_data": "user-1",
        "verification_date": "2025-01-01T00:00:00Z",
        "user_details": {
          "full_name": "Jane Marie Doe",
          "document_type": "ID",
          "document_number": "X1234567"
        },
        "match_image_url": "https://<media-host>/face/3f6a1c2e/reference.jpg?signature=...",
        "status": "Approved",
        "is_blocklisted": true,
        "is_allowlisted": false,
        "api_service": "PASSIVE_LIVENESS"
      }
    ],
    "user_image": {
      "entities": [
        {
          "bbox": [40, 40, 120, 120],
          "confidence": 0.732973
        }
      ],
      "best_angle": 0
    },
    "warnings": [
      {
        "risk": "FACE_IN_BLOCKLIST",
        "feature": "LIVENESS",
        "additional_data": {
          "blocklisted_session_id": "882c42d5-8a4d-4d20-8080-a22f57822c86",
          "blocklisted_session_number": 323442,
          "api_service": "PASSIVE_LIVENESS"
        },
        "log_type": "error",
        "short_description": "Face in blocklist",
        "long_description": "The system identified a face in the blocklist, which means the face is not allowed to be verified."
      }
    ]
  },
  "vendor_data": "user-123",
  "metadata": null,
  "created_at": "2026-06-12T01:04:42.763237+00:00"
}
When save_api_request=true, match_image_url values are signed, short-lived URLs — re-fetch the session decision endpoint with the returned request_id to get fresh ones. When save_api_request=false, they are internal storage paths and are not directly downloadable. Store only the matched session id, status, and similarity_percentage on your side — do not retain the underlying biometric image.