> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Face Match

> Compare two face images (1:1) and get back a similarity `score` (0–100) plus an `Approved`/`Declined` `status`.

**How it works.** Both images are decoded and analyzed by a biometric model (set `rotate_image=true` if captures may be sideways or upside down), then a similarity score between the two faces is computed. A score **strictly above** `face_match_score_decline_threshold` (default `30`) returns `status: "Approved"`; a score at or below it returns `Declined` with a `LOW_FACE_MATCH_SIMILARITY` warning. If no face pair can be scored at all, the call still returns `200` with `status: "Declined"` and a `NO_REFERENCE_IMAGE` warning — `score` is `null` on the default save path, or `0` with `save_api_request=false`. Submit one clear, front-facing face per image for best results.

**Billing.** Each `200` response consumes one Face Match API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any image processing.

**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, both images are stored as the session's face-match source/target images, and a `status.updated` webhook is emitted to your configured webhook endpoints. When `false`, nothing is stored and `request_id` is a one-off correlation UUID that cannot be looked up later.

**Face search index.** This endpoint never enrolls faces into your face search index and never runs blocklist or duplicate screening — use `POST /v3/passive-liveness/` or `POST /v3/face-search/` for those.

**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload, no session is persisted, and no credits are consumed.

**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{"detail": "You do not have permission to perform this action."}`) — this API never returns `401`.

**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.



## OpenAPI

````yaml POST /v3/face-match/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: Identity verification API. Authenticate with x-api-key header.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/face-match/:
    post:
      tags:
        - Standalone APIs
      summary: Face Match
      description: >-
        Compare two face images (1:1) and get back a similarity `score` (0–100)
        plus an `Approved`/`Declined` `status`.


        **How it works.** Both images are decoded and analyzed by a biometric
        model (set `rotate_image=true` if captures may be sideways or upside
        down), then a similarity score between the two faces is computed. A
        score **strictly above** `face_match_score_decline_threshold` (default
        `30`) returns `status: "Approved"`; a score at or below it returns
        `Declined` with a `LOW_FACE_MATCH_SIMILARITY` warning. If no face pair
        can be scored at all, the call still returns `200` with `status:
        "Declined"` and a `NO_REFERENCE_IMAGE` warning — `score` is `null` on
        the default save path, or `0` with `save_api_request=false`. Submit one
        clear, front-facing face per image for best results.


        **Billing.** Each `200` response consumes one Face Match API credit
        (standalone APIs have no free tier). When the organization's balance
        cannot cover the call, the endpoint returns `403` with the
        not-enough-credits error before any image processing.


        **Session persistence (`save_api_request`, default `true`).** When
        `true`, the call is persisted as an API-type session: it appears in the
        Business Console, the returned `request_id` is a real session id you can
        pass to `GET /v3/session/{sessionId}/decision/`, both images are stored
        as the session's face-match source/target images, and a `status.updated`
        webhook is emitted to your configured webhook endpoints. When `false`,
        nothing is stored and `request_id` is a one-off correlation UUID that
        cannot be looked up later.


        **Face search index.** This endpoint never enrolls faces into your face
        search index and never runs blocklist or duplicate screening — use `POST
        /v3/passive-liveness/` or `POST /v3/face-search/` for those.


        **Sandbox.** Sandbox API keys skip all processing and billing: after
        request validation (malformed input still returns `400`), the endpoint
        returns a static `Approved` mock payload, no session is persisted, and
        no credits are consumed.


        **Authentication.** Send your application's API key in the `x-api-key`
        header. Missing or invalid credentials return `403` (`{"detail": "You do
        not have permission to perform this action."}`) — this API never returns
        `401`.


        **Rate limit.** Shared write budget of 300 requests/min per API key
        across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.
      operationId: post_v3face-match
      parameters: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - user_image
                - ref_image
              properties:
                user_image:
                  type: string
                  format: binary
                  description: >-
                    Live or candidate face image to verify (e.g. a selfie).
                    Submit a single front-facing photo with the subject's face
                    clearly visible and well lit. Allowed extensions: `tiff`,
                    `jpg`, `jpeg`, `png`, `webp`. Maximum upload size: **5 MB**
                    (larger files are rejected with `400`). Images are
                    automatically compressed to ~0.5 MB before processing, so
                    very high resolutions do not improve accuracy.
                ref_image:
                  type: string
                  format: binary
                  description: >-
                    Reference face image to compare against (e.g. an ID document
                    portrait or a previously enrolled photo). Same format and
                    size limits as `user_image`.
                face_match_score_decline_threshold:
                  type: number
                  format: float
                  default: 30
                  minimum: 0
                  maximum: 100
                  description: >-
                    Similarity threshold (0–100) that drives the response
                    `status`. A computed score **at or below** this value sets
                    `status` to `Declined` and adds a
                    `LOW_FACE_MATCH_SIMILARITY` warning; a score strictly above
                    it returns `Approved`. Default `30` is permissive — raise it
                    (e.g. `50`–`60`) for stricter verification. Values outside
                    0–100 return `400`.
                  example: 50
                rotate_image:
                  type: boolean
                  default: false
                  description: >-
                    When `true`, the service tries the input images in 90-degree
                    increments (0, 90, 180, 270) and uses the orientation that
                    yields the best face detection — useful for mobile captures
                    with missing EXIF orientation. Adds latency, so only enable
                    it if you cannot guarantee upright images.
                  example: false
                save_api_request:
                  type: boolean
                  default: true
                  description: >-
                    When `true` (default), persists the call as an API-type
                    session — visible in the Business Console, retrievable via
                    `GET /v3/session/{sessionId}/decision/` using the returned
                    `request_id`, and announced through a `status.updated`
                    webhook. When `false`, nothing is stored and `request_id` is
                    a transient UUID for response correlation only.
                  example: true
                vendor_data:
                  type: string
                  description: >-
                    Optional opaque string (your internal user id, email, UUID…)
                    stored on the persisted session and echoed back in the
                    response. Use it to correlate API calls with your own
                    records and to filter sessions later.
                  example: user-123
                metadata:
                  type: object
                  additionalProperties: true
                  description: >-
                    Optional JSON object stored with the session (when
                    `save_api_request=true`) and echoed back in the response. In
                    multipart requests, send it as a JSON-encoded string field
                    (e.g. `metadata={"flow":"login"}`) — it is parsed into an
                    object.
                  example:
                    flow: login
                    device_id: abc123
            example:
              user_image: (binary JPEG/PNG of the live selfie)
              ref_image: (binary JPEG/PNG of the reference photo)
              face_match_score_decline_threshold: 50
              rotate_image: false
              save_api_request: true
              vendor_data: user-123
              metadata:
                flow: login
      responses:
        '200':
          description: >-
            Face match computed. `face_match.score` is a 0–100 float (two
            decimals); `status` is `Approved` when the score is strictly above
            `face_match_score_decline_threshold`, otherwise `Declined` with an
            explanatory warning. A low similarity or unscorable pair still
            returns `200` — inspect `face_match.status` and
            `face_match.warnings`, not just the HTTP code. When
            `save_api_request=true`, `request_id` is the persisted session id.
          content:
            application/json:
              examples:
                Approved:
                  summary: Same person — score above the threshold
                  value:
                    request_id: f9f5a777-8002-44c6-baaf-128bcfd6e226
                    face_match:
                      status: Approved
                      score: 99.41
                      user_image:
                        entities:
                          - bbox:
                              - 661
                              - 728
                              - 1688
                              - 2188
                            confidence: 0.732973
                            age: 26.91
                            gender: male
                            race: null
                        best_angle: 0
                      ref_image:
                        entities:
                          - bbox:
                              - 653
                              - 745
                              - 1691
                              - 2185
                            confidence: 0.722082
                            age: 27
                            gender: male
                            race: null
                        best_angle: 0
                      warnings: []
                    vendor_data: user-123
                    metadata:
                      flow: login
                    created_at: '2026-06-12T01:04:42.763237+00:00'
                Declined - low similarity:
                  summary: Score at or below the decline threshold
                  value:
                    request_id: 7cdda24e-311a-46d3-a3bb-e5d9b564ce73
                    face_match:
                      status: Declined
                      score: 24.87
                      user_image:
                        entities:
                          - bbox:
                              - 661
                              - 728
                              - 1688
                              - 2188
                            confidence: 0.732973
                            age: 26.91
                            gender: male
                            race: null
                        best_angle: 0
                      ref_image:
                        entities:
                          - bbox:
                              - 156
                              - 234
                              - 679
                              - 898
                            confidence: 0.717775
                            age: 41.2
                            gender: male
                            race: null
                        best_angle: 0
                      warnings:
                        - risk: LOW_FACE_MATCH_SIMILARITY
                          feature: FACEMATCH
                          additional_data: null
                          log_type: error
                          short_description: Low face match similarity
                          long_description: >-
                            The facial features of the provided image don't
                            closely match the reference image, suggesting a
                            potential identity mismatch.
                    vendor_data: user-123
                    metadata: null
                    created_at: '2026-06-12T01:08:19.859116+00:00'
              schema:
                type: object
                properties:
                  request_id:
                    type: string
                    format: uuid
                    description: >-
                      Persisted session id when `save_api_request=true` (usable
                      with `GET /v3/session/{sessionId}/decision/`); otherwise a
                      transient correlation UUID.
                  face_match:
                    type: object
                    properties:
                      status:
                        type: string
                        enum:
                          - Approved
                          - Declined
                        description: >-
                          `Approved` when the similarity score is strictly above
                          `face_match_score_decline_threshold` and a score could
                          be computed; `Declined` otherwise (a warning explains
                          why).
                      score:
                        type: number
                        format: float
                        nullable: true
                        minimum: 0
                        maximum: 100
                        description: >-
                          Similarity score from 0 to 100 — the model's
                          confidence that both images depict the same person.
                          Rounded to two decimals only when `save_api_request`
                          is true; with `save_api_request=false` the raw float
                          is returned. `null` when no face pair could be scored
                          (with the default `save_api_request=true`); with
                          `save_api_request=false` the same condition can
                          surface as `score: 0`. Either way a
                          `NO_REFERENCE_IMAGE` warning is present and `status`
                          is `Declined`.
                        example: 99.41
                      user_image:
                        type: object
                        description: Face-detection results for the analyzed images.
                        properties:
                          entities:
                            type: array
                            items:
                              type: object
                              properties:
                                bbox:
                                  type: array
                                  items:
                                    type: integer
                                  minItems: 4
                                  maxItems: 4
                                  description: >-
                                    Bounding box of the detected face as
                                    `[x_min, y_min, x_max, y_max]` pixel
                                    coordinates in the processed image.
                                  example:
                                    - 661
                                    - 728
                                    - 1688
                                    - 2188
                                confidence:
                                  type: number
                                  format: float
                                  minimum: 0
                                  maximum: 1
                                  description: Face-detection confidence (0–1).
                                  example: 0.732973
                                age:
                                  type: number
                                  format: float
                                  description: >-
                                    Model-estimated age of the detected face, in
                                    years. Informational only — it does not
                                    affect the face-match decision.
                                  example: 26.91
                                gender:
                                  type: string
                                  description: >-
                                    Model-predicted gender of the detected face
                                    (`male` or `female`). Informational only.
                                  example: male
                                race:
                                  type: string
                                  nullable: true
                                  description: >-
                                    Reserved field — always `null` in responses
                                    from this endpoint.
                                  example: null
                            description: One entry per detected face.
                          best_angle:
                            type: integer
                            description: >-
                              Rotation (degrees: 0, 90, 180, or 270) that
                              produced the best face detection. Only non-zero
                              when `rotate_image=true` corrected the
                              orientation.
                            example: 0
                      ref_image:
                        type: object
                        description: >-
                          Face-detection results for the reference image, same
                          shape as `user_image`.
                        properties:
                          entities:
                            type: array
                            items:
                              type: object
                              properties:
                                bbox:
                                  type: array
                                  items:
                                    type: integer
                                  minItems: 4
                                  maxItems: 4
                                  description: >-
                                    Bounding box of the detected face as
                                    `[x_min, y_min, x_max, y_max]` pixel
                                    coordinates in the processed image.
                                  example:
                                    - 661
                                    - 728
                                    - 1688
                                    - 2188
                                confidence:
                                  type: number
                                  format: float
                                  minimum: 0
                                  maximum: 1
                                  description: Face-detection confidence (0–1).
                                  example: 0.732973
                                age:
                                  type: number
                                  format: float
                                  description: >-
                                    Model-estimated age of the detected face, in
                                    years. Informational only — it does not
                                    affect the face-match decision.
                                  example: 26.91
                                gender:
                                  type: string
                                  description: >-
                                    Model-predicted gender of the detected face
                                    (`male` or `female`). Informational only.
                                  example: male
                                race:
                                  type: string
                                  nullable: true
                                  description: >-
                                    Reserved field — always `null` in responses
                                    from this endpoint.
                                  example: null
                            description: One entry per detected face.
                          best_angle:
                            type: integer
                            example: 0
                      warnings:
                        type: array
                        description: >-
                          Empty on a clean approval. `NO_REFERENCE_IMAGE`
                          (`error`) — no face pair could be scored;
                          `LOW_FACE_MATCH_SIMILARITY` (`error`) — score at or
                          below the decline threshold. Any of these sets
                          `status` to `Declined`.
                        items:
                          type: object
                          properties:
                            risk:
                              type: string
                              enum:
                                - NO_REFERENCE_IMAGE
                                - LOW_FACE_MATCH_SIMILARITY
                              description: Machine-readable risk code.
                            feature:
                              type: string
                              enum:
                                - FACEMATCH
                              description: >-
                                Feature that raised the warning. Always
                                `FACEMATCH` on this endpoint.
                            additional_data:
                              type: object
                              nullable: true
                              additionalProperties: true
                              description: Always `null` for face-match warnings.
                            log_type:
                              type: string
                              enum:
                                - error
                                - warning
                                - information
                              description: >-
                                Severity. `error` warnings drive `status` to
                                `Declined`; `warning` and `information` entries
                                are advisory and never decline on their own.
                            short_description:
                              type: string
                              description: Human-readable one-line summary of the risk.
                            long_description:
                              type: string
                              description: Human-readable explanation of the risk.
                  vendor_data:
                    type: string
                    nullable: true
                    description: Echo of the `vendor_data` you sent, or `null`.
                  metadata:
                    type: object
                    nullable: true
                    additionalProperties: true
                    description: Echo of the `metadata` object you sent, or `null`.
                  created_at:
                    type: string
                    format: date-time
                    description: >-
                      ISO 8601 timestamp (UTC) of when the response was
                      generated, e.g. `2026-06-12T01:04:42.763237+00:00`.
        '400':
          description: >-
            Validation error. Returned when a required image is missing, exceeds
            5 MB, has an unsupported extension, or when an option is out of
            range. The body is DRF's standard field-error envelope: one array of
            messages per offending field.
          content:
            application/json:
              examples:
                Missing required image:
                  summary: '`ref_image` (or `user_image`) not included in the form data'
                  value:
                    ref_image:
                      - No file was submitted.
                Unsupported file extension:
                  summary: File extension outside tiff/jpg/jpeg/png/webp
                  value:
                    user_image:
                      - >-
                        File extension “txt” is not allowed. Allowed extensions
                        are: tiff, jpg, jpeg, png, webp.
                File too large:
                  summary: Upload exceeds the 5 MB limit
                  value:
                    user_image:
                      - File size should not exceed 5 MB
                Threshold out of range:
                  summary: '`face_match_score_decline_threshold` outside 0–100'
                  value:
                    face_match_score_decline_threshold:
                      - Ensure this value is less than or equal to 100.
        '403':
          description: >-
            Permission denied. Returned when the `x-api-key` header is missing,
            malformed, revoked, or belongs to another environment — and also
            when the calling organization's balance cannot cover the call.
            Authentication failures return `403` with `{"detail": ...}`; this
            API never returns `401`. Credit shortfalls return `403` with
            `{"error": ...}` before any image processing happens.
          content:
            application/json:
              examples:
                Missing or invalid API key:
                  summary: No `x-api-key` header, or the key is invalid/revoked
                  value:
                    detail: You do not have permission to perform this action.
                Not enough credits:
                  summary: Organization balance cannot cover the call
                  value:
                    error: >-
                      You don't have enough credits to perform this request.
                      Please top up at https://business.didit.me
        '429':
          description: >-
            Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget
            of 300 write requests per minute per API key. The response carries
            `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`,
            and `Retry-After` headers.
          content:
            application/json:
              examples:
                Write rate limit exceeded:
                  summary: More than 300 write requests in the current minute
                  value:
                    detail: >-
                      Write request rate limit exceeded. You can make up to 300
                      requests per minute.
              schema:
                type: object
                properties:
                  detail:
                    type: string
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl -X POST 'https://verification.didit.me/v3/face-match/' \
              -H 'x-api-key: YOUR_API_KEY' \
              -F 'user_image=@./selfie.jpg' \
              -F 'ref_image=@./id_portrait.jpg' \
              -F 'face_match_score_decline_threshold=50' \
              -F 'save_api_request=true' \
              -F 'vendor_data=user-123'
        - lang: python
          label: Python
          source: >-
            import requests


            url = 'https://verification.didit.me/v3/face-match/'

            headers = {'x-api-key': 'YOUR_API_KEY'}


            with open('selfie.jpg', 'rb') as user_f, open('id_portrait.jpg',
            'rb') as ref_f:
                files = {
                    'user_image': ('selfie.jpg', user_f, 'image/jpeg'),
                    'ref_image': ('id_portrait.jpg', ref_f, 'image/jpeg'),
                }
                data = {
                    'face_match_score_decline_threshold': 50,
                    'save_api_request': 'true',
                    'vendor_data': 'user-123',
                }
                resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)

            resp.raise_for_status()

            body = resp.json()

            print('status:', body['face_match']['status'])

            print('score:', body['face_match']['score'])
        - lang: javascript
          label: JavaScript
          source: >-
            import fs from 'node:fs';


            const form = new FormData();

            form.append('user_image', new
            Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');

            form.append('ref_image', new
            Blob([fs.readFileSync('./id_portrait.jpg')]), 'id_portrait.jpg');

            form.append('face_match_score_decline_threshold', '50');

            form.append('save_api_request', 'true');

            form.append('vendor_data', 'user-123');


            const response = await
            fetch('https://verification.didit.me/v3/face-match/', {
              method: 'POST',
              headers: { 'x-api-key': 'YOUR_API_KEY' },
              body: form,
            });


            if (!response.ok) throw new Error(`Face match failed:
            ${response.status}`);

            const body = await response.json();

            console.log('status:', body.face_match.status, 'score:',
            body.face_match.score);
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````