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

# Verificação facial avulsa

> Roda Face Match e/ou Passive Liveness sem vincular o resultado a
um cliente cadastrado — análoga a um bureau run avulso. Consome o mesmo
saldo do tenant dono da chave. `customer_id` nunca é atribuído por esta
rota; a persistência é só para reconciliação de billing/suporte.

Requer escopo `biometria:write` e o módulo "Verificação facial" ativo
para o tenant (ativação em duas camadas — módulo no master + config no
tenant — nenhuma acionável via API).

Síncrona: o resultado volta na própria resposta HTTP. Diferente do
fluxo automático de `POST /customers`/`POST /customers/{id}/documents`,
esta rota **não** dispara o webhook `biometric_check.completed`.


## Quando usar

Use este endpoint para rodar Face Match e/ou Passive Liveness **sem** vincular o resultado a um cliente cadastrado — útil para validar biometria antes de decidir se vale a pena criar o cadastro, ou para fluxos que não passam por [POST /customers](/api-reference/createCustomer)/[POST /customers/{id}/documents](/api-reference/uploadCustomerDocument) (o par selfie+identidade não dispara a verificação automática — ver [Verificação Facial](/verificacao-facial) para os dois fluxos lado a lado).

Requer escopo `biometria:write` e o módulo "Verificação facial" ativo para o tenant — ativação em duas camadas (módulo no master + configuração no tenant), nenhuma acionável via esta ou qualquer outra chamada de API.

## Corpo da requisição

O corpo é `multipart/form-data`.

| Campo | Obrigatório | Descrição |
| - | - | - |
| `user_image` | Sim | Imagem de referência do rosto do titular |
| `ref_image` | Somente se `checks` incluir `face_match` | Segunda imagem, para comparação com `user_image` |
| `checks` | Não | `"face_match"`, `"passive_liveness"` ou `"face_match,passive_liveness"`. Default: `face_match` se `ref_image` foi enviado, senão `passive_liveness` |

Formato de imagem: JPG, PNG, WEBP ou TIFF. Tamanho máximo: **5MB por imagem** — acima disso, `400 image_too_large`. Formato fora da lista retorna `400 invalid_image_format`.

## Checks disponíveis

| Check | O que verifica | Exige `ref_image`? |
| - | - | - |
| `face_match` | Se o rosto em `user_image` é a mesma pessoa de `ref_image` | Sim |
| `passive_liveness` | Se `user_image` é de uma pessoa real ao vivo (anti-spoofing) — sem exigir gestos ou vídeo | Não |

Pedir `face_match` sem `ref_image` retorna `400 missing_ref_image`.

## Exemplo

```bash theme={null}
curl -X POST https://admin.kycert.com.br/api/v1/verificacoes/facial \
  -H "x-api-key: $KYCERT_API_KEY" \
  -F "checks=face_match,passive_liveness" \
  -F "user_image=@/caminho/para/selfie.jpg" \
  -F "ref_image=@/caminho/para/documento.jpg"
```

```json theme={null}
{
  "object": "facial_verification",
  "results": {
    "face_match": {
      "status": "Approved",
      "score": 92.4,
      "warnings": [],
      "request_id": "a1b2c3d4e5f67890"
    },
    "passive_liveness": {
      "status": "Approved",
      "score": 88.1,
      "warnings": [],
      "request_id": "b2c3d4e5f6a78901"
    }
  },
  "livemode": true
}
```

## Resposta parcial

A resposta **nunca é tudo-ou-nada**. Se os dois checks forem pedidos e só um falhar no provedor, o resultado do que teve sucesso (já cobrado) volta normalmente — só aquela entrada específica de `results` vira `{status: "Error", message: "..."}`:

```json theme={null}
{
  "object": "facial_verification",
  "results": {
    "face_match": {
      "status": "Approved",
      "score": 92.4,
      "warnings": [],
      "request_id": "a1b2c3d4e5f67890"
    },
    "passive_liveness": {
      "status": "Error",
      "message": "Falha ao processar este check com o provedor."
    }
  },
  "livemode": true
}
```

A resposta só escala para `502 biometric_check_failed` quando **nenhum** dos checks pedidos teve sucesso.

## O que este endpoint não faz

* **Não vincula o resultado a um cliente** — `customer_id` é sempre `null` na persistência interna; esta rota nunca cria nem atualiza um `customer`. Para o fluxo que vincula ao cadastro, veja [Verificação Facial](/verificacao-facial).
* **Não tem `Idempotency-Key`** — reenviar a mesma chamada roda (e cobra) a verificação de novo.
* **Não dispara o webhook `biometric_check.completed`** — a chamada é síncrona e o resultado já volta na própria resposta HTTP. Assinar esse webhook não traz nenhum evento para verificações feitas por este endpoint (o evento cobre apenas o fluxo automático de `POST /customers`/`POST /customers/{id}/documents` — ver [Webhooks](/webhooks)).

## Erros

Formato completo do envelope e estratégia de retry em [Tratamento de erros](/guias/erros). Os códigos deste endpoint: `missing_api_key`, `invalid_api_key`, `insufficient_scope`, `channel_disabled`, `module_disabled`, `invalid_multipart`, `missing_user_image`, `invalid_checks`, `missing_ref_image`, `invalid_image_format`, `image_too_large`, `billing_suspended`, `internal_error`, `biometric_check_failed`.


## OpenAPI

````yaml POST /api/v1/verificacoes/facial
openapi: 3.0.3
info:
  title: kycert API
  version: '2026-06-03'
  description: >
    API KYC/AML para empresas reguladas pelo Banco Central do Brasil.

    Permite rodar verificações de bureau em CPF ou CNPJ e receber o resultado
    via webhook.


    **Fluxo principal:**

    ```

    POST /runs → 202 (run_id) → [bureau processa 5–30s] → webhook entregue →
    agir

    ```
  contact:
    name: kycert
    url: https://kycert.com.br
  license:
    name: Proprietário
    url: https://kycert.com.br
servers:
  - url: https://admin.kycert.com.br
    description: Produção (sk_live_...)
  - url: https://admin.kycert.com.br
    description: Sandbox (sk_test_...)
security:
  - ApiKeyHeader: []
  - BearerToken: []
tags:
  - name: Runs
    description: Execução de bureau para CPF ou CNPJ
  - name: Customers
    description: Gestão de clientes do tenant
  - name: Verificação Facial
    description: Face Match e Passive Liveness
paths:
  /api/v1/verificacoes/facial:
    post:
      tags:
        - Verificação Facial
      summary: Verificação facial avulsa
      description: |
        Roda Face Match e/ou Passive Liveness sem vincular o resultado a
        um cliente cadastrado — análoga a um bureau run avulso. Consome o mesmo
        saldo do tenant dono da chave. `customer_id` nunca é atribuído por esta
        rota; a persistência é só para reconciliação de billing/suporte.

        Requer escopo `biometria:write` e o módulo "Verificação facial" ativo
        para o tenant (ativação em duas camadas — módulo no master + config no
        tenant — nenhuma acionável via API).

        Síncrona: o resultado volta na própria resposta HTTP. Diferente do
        fluxo automático de `POST /customers`/`POST /customers/{id}/documents`,
        esta rota **não** dispara o webhook `biometric_check.completed`.
      operationId: verificarFacial
      parameters:
        - name: x-kycert-api-version
          in: header
          required: false
          schema:
            type: string
            example: '2026-06-03'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/VerificarFacialRequest'
      responses:
        '200':
          description: |
            Verificação processada. A resposta nunca é "tudo ou nada" — quando
            os dois checks são pedidos e só um falha no provedor, o outro
            (já rodado e já cobrado) é retornado normalmente; só escala para
            `502` quando NENHUM check pedido teve sucesso.
          headers:
            X-Request-Id:
              schema:
                type: string
                format: uuid
            X-Kycert-Api-Version:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FacialVerificationResponse'
              example:
                object: facial_verification
                results:
                  face_match:
                    status: Approved
                    score: 92.4
                    warnings: []
                    request_id: a1b2c3d4e5f67890
                  passive_liveness:
                    status: Approved
                    score: 88.1
                    warnings: []
                    request_id: b2c3d4e5f6a78901
                livemode: true
        '400':
          description: Requisição inválida — corrija os dados e tente novamente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalid_multipart:
                  summary: corpo não é multipart/form-data válido
                  value:
                    error:
                      type: invalid_request_error
                      code: invalid_multipart
                      message: Corpo multipart/form-data inválido.
                      param: null
                missing_user_image:
                  summary: user_image ausente
                  value:
                    error:
                      type: invalid_request_error
                      code: missing_user_image
                      message: user_image é obrigatório e deve ser um arquivo.
                      param: user_image
                invalid_checks:
                  summary: checks com valor fora de face_match/passive_liveness
                  value:
                    error:
                      type: invalid_request_error
                      code: invalid_checks
                      message: >-
                        checks deve conter apenas "face_match" e/ou
                        "passive_liveness".
                      param: checks
                missing_ref_image:
                  summary: face_match pedido sem ref_image
                  value:
                    error:
                      type: invalid_request_error
                      code: missing_ref_image
                      message: ref_image é obrigatório para o check face_match.
                      param: ref_image
                invalid_image_format:
                  summary: formato de imagem não suportado
                  value:
                    error:
                      type: invalid_request_error
                      code: invalid_image_format
                      message: user_image deve ser JPG, PNG, WEBP ou TIFF.
                      param: user_image
                image_too_large:
                  summary: imagem acima de 5MB
                  value:
                    error:
                      type: invalid_request_error
                      code: image_too_large
                      message: user_image excede o tamanho máximo de 5MB.
                      param: user_image
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          description: Escopo, canal ou módulo insuficiente para esta operação
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                insufficient_scope:
                  value:
                    error:
                      type: authorization_error
                      code: insufficient_scope
                      message: Esta chave não tem permissão para verificação facial.
                      param: null
                channel_disabled:
                  value:
                    error:
                      type: authorization_error
                      code: channel_disabled
                      message: Canal API está desabilitado para este tenant.
                      param: null
                module_disabled:
                  summary: módulo "Verificação facial" não ativo para o tenant
                  value:
                    error:
                      type: authorization_error
                      code: module_disabled
                      message: >-
                        Módulo de verificação facial não está habilitado para
                        este tenant.
                      param: null
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          description: >-
            Falha ao processar com o provedor — nenhum dos checks pedidos teve
            sucesso
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  type: server_error
                  code: biometric_check_failed
                  message: >-
                    Falha ao processar verificação facial com o provedor. Tente
                    novamente.
                  param: null
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: >
            curl -X POST https://admin.kycert.com.br/api/v1/verificacoes/facial
            \
              -H "x-api-key: $KYCERT_API_KEY" \
              -F "checks=face_match,passive_liveness" \
              -F "user_image=@/caminho/para/selfie.jpg" \
              -F "ref_image=@/caminho/para/documento.jpg"
        - lang: Node
          label: Node.js
          source: >
            const form = new FormData()

            form.set('checks', 'face_match,passive_liveness')

            form.set('user_image', userImageBlob, 'selfie.jpg')

            form.set('ref_image', refImageBlob, 'documento.jpg')


            const res = await
            fetch('https://admin.kycert.com.br/api/v1/verificacoes/facial', {
              method: 'POST',
              headers: { 'x-api-key': process.env.KYCERT_API_KEY },
              body: form,
            })

            const { results } = await res.json()

            console.log(results.face_match?.status,
            results.passive_liveness?.status)
components:
  schemas:
    VerificarFacialRequest:
      type: object
      required:
        - user_image
      properties:
        user_image:
          type: string
          format: binary
          description: >-
            Imagem de referência do rosto do titular (JPG, PNG, WEBP ou TIFF,
            até 5MB)
        ref_image:
          type: string
          format: binary
          description: |
            Segunda imagem para comparação — obrigatória quando `checks` inclui
            `face_match` (mesmas regras de formato/tamanho de `user_image`).
        checks:
          type: string
          description: >
            `"face_match"`, `"passive_liveness"` ou
            `"face_match,passive_liveness"`.

            Default: `face_match` se `ref_image` foi enviado, senão
            `passive_liveness`.
          example: face_match,passive_liveness
    FacialVerificationResponse:
      type: object
      properties:
        object:
          type: string
          enum:
            - facial_verification
        results:
          type: object
          description: >
            Chaveado pelos checks pedidos (`face_match`, `passive_liveness`).

            Cada entrada é `{status: Approved|Declined, score, warnings,
            request_id}`

            ou, se aquele check específico falhou no provedor, `{status: Error,
            message}`.
          additionalProperties:
            type: object
            properties:
              status:
                type: string
                enum:
                  - Approved
                  - Declined
                  - Error
              score:
                type: number
                nullable: true
                description: Presente quando status não é Error
              warnings:
                type: array
                items:
                  type: object
                  additionalProperties: true
                  description: >-
                    Shape por item não documentado publicamente pelo provedor
                    (ex. `risk`, `feature`) — tratar como opaco, sem parsear
                    campos individuais.
                description: >-
                  Presente quando status não é Error. Nunca string — sempre
                  array de objetos.
              request_id:
                type: string
                description: Presente quando status não é Error
              message:
                type: string
                description: Presente apenas quando status é Error
        livemode:
          type: boolean
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            type:
              type: string
              enum:
                - invalid_request_error
                - authentication_error
                - authorization_error
                - billing_error
                - server_error
            code:
              type: string
              description: Código específico do erro
            message:
              type: string
              description: Descrição legível do erro
            param:
              type: string
              nullable: true
              description: Campo que causou o erro (quando aplicável)
  responses:
    Unauthorized:
      description: API key ausente ou inválida
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            missing_api_key:
              value:
                error:
                  type: authentication_error
                  code: missing_api_key
                  message: API key ausente.
                  param: null
            invalid_api_key:
              value:
                error:
                  type: authentication_error
                  code: invalid_api_key
                  message: API key inválida ou inativa.
                  param: null
    PaymentRequired:
      description: Saldo insuficiente ou billing suspenso
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              type: billing_error
              code: billing_suspended
              message: Saldo insuficiente para realizar a consulta.
              param: null
    InternalError:
      description: Falha interna — tente novamente com backoff exponencial
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              type: server_error
              code: internal_error
              message: Erro interno. Tente novamente.
              param: null
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: x-api-key
      description: API key no header x-api-key (recomendado)
    BearerToken:
      type: http
      scheme: bearer
      description: API key como Bearer token no header Authorization

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.