> ## 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.

# Anexar documento

> Anexa um documento (RG, CNH, comprovante de residência etc.) a um cliente
já cadastrado. Reenviar o mesmo `type` para o mesmo cliente substitui o
documento anterior (não-rejeitado) desse tipo.

Requer escopo `customers:write` (o mesmo usado por `POST /customers` —
não existe um escopo `documents:write` separado).

Limite de arquivo: **4MB**. Tipos aceitos: `jpg`, `jpeg`, `png`, `webp`,
`heic`, `heif`, `tiff`, `bmp`, `pdf`.


## Quando usar

Use este endpoint para anexar um documento (RG, CNH, comprovante de residência etc.) a um cliente já cadastrado — via [POST /customers](/api-reference/createCustomer) ou pelo portal de onboarding. Ideal para integrações que fazem o cadastro e a captura de documentos em etapas separadas, sem depender do cliente final preencher o formulário web.

## Corpo da requisição

O corpo é `multipart/form-data`, não JSON — evita o overhead de \~33% de um payload base64 e mantém `POST /customers` livre de mudança de contrato.

| Campo  | Obrigatório | Descrição                               |
| ------ | ----------- | --------------------------------------- |
| `file` | Sim         | Arquivo do documento                    |
| `type` | Sim         | Tipo do documento (ver catálogo abaixo) |

## Limite de arquivo: 4MB

Serverless Functions da Vercel têm um teto de 4.5MB de tamanho de request, fixo em nível de infraestrutura. Arquivos maiores que 4MB são rejeitados com `400 file_too_large` antes de chegar no Storage.

## Catálogo de `type`

Tipos base, aceitos em qualquer tenant:

```
identidade_frente, identidade_verso, cnh_frente, cnh_verso, passaporte,
comprovante_endereco, selfie, contrato_social, comprovante_cnpj,
procuracao, balanco, outro
```

Tenants com tipos de documento customizados configurados também podem enviar esses tipos adicionais. Um `type` fora desse conjunto retorna `400 invalid_document_type`.

## Reenvio do mesmo tipo

Enviar o mesmo `type` novamente para o mesmo cliente substitui o documento anterior (desde que não esteja `rejeitado`) — só um documento ativo por tipo.

Se o documento existente desse `type` estiver `rejeitado`, o comportamento é diferente: o antigo **não** é removido, e o novo upload passa a coexistir com ele — os dois aparecem em `documents[]` de [GET /customers/{id}](/api-reference/getCustomer), um `rejeitado` e um `pendente`. Não é possível, via API, apagar o documento rejeitado antigo depois disso.

## Exemplo

```bash theme={null}
curl -X POST https://admin.kycert.com.br/api/v1/customers/CUSTOMER_ID/documents \
  -H "x-api-key: $KYCERT_API_KEY" \
  -F "type=comprovante_endereco" \
  -F "file=@/caminho/para/comprovante.pdf"
```

```json theme={null}
{
  "document_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "object": "document",
  "type": "comprovante_endereco",
  "status": "pendente",
  "file_name": "comprovante.pdf",
  "mime_type": "application/pdf",
  "created_at": "2026-06-12T14:00:00Z",
  "livemode": true
}
```

## O que este endpoint não faz

* **Não retorna o arquivo nem gera signed URL** — baixar o documento continua exclusivo do dashboard.
* **Não aprova nem rejeita documentos** — todo documento entra como `pendente`; a revisão manual é feita no dashboard.
* **Não lista documentos em volume** — use o campo `documents` de [GET /customers/{id}](/api-reference/getCustomer) para confirmar recebimento e status.

## 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`, `invalid_id` (`{id}` da URL ausente ou inválido), `invalid_multipart`, `invalid_file`, `invalid_document_type`, `file_too_large`, `customer_not_found`, `storage_failed`, `db_failed`.


## OpenAPI

````yaml POST /api/v1/customers/{id}/documents
openapi: 3.0.3
info:
  title: kycert API
  version: '2026-06-03'
  description: >
    API KYC/AML para corretoras de câmbio autorizadas 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
paths:
  /api/v1/customers/{id}/documents:
    post:
      tags:
        - Customers
      summary: Anexar documento a um cliente
      description: >
        Anexa um documento (RG, CNH, comprovante de residência etc.) a um
        cliente

        já cadastrado. Reenviar o mesmo `type` para o mesmo cliente substitui o

        documento anterior (não-rejeitado) desse tipo.


        Requer escopo `customers:write` (o mesmo usado por `POST /customers` —

        não existe um escopo `documents:write` separado).


        Limite de arquivo: **4MB**. Tipos aceitos: `jpg`, `jpeg`, `png`, `webp`,

        `heic`, `heif`, `tiff`, `bmp`, `pdf`.
      operationId: uploadCustomerDocument
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: ID do cliente retornado pelo POST /customers
        - name: x-kycert-api-version
          in: header
          required: false
          schema:
            type: string
            example: '2026-06-03'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
                - type
              properties:
                file:
                  type: string
                  format: binary
                  description: Arquivo do documento (máx. 4MB)
                type:
                  type: string
                  description: >
                    Tipo do documento. Catálogo base:

                    `identidade_frente`, `identidade_verso`, `cnh_frente`,
                    `cnh_verso`,

                    `passaporte`, `comprovante_endereco`, `selfie`,
                    `contrato_social`,

                    `comprovante_cnpj`, `procuracao`, `balanco`, `outro`.

                    Tenants com tipos customizados configurados também são
                    aceitos.
                  example: comprovante_endereco
      responses:
        '201':
          description: Documento anexado com sucesso
          headers:
            X-Request-Id:
              schema:
                type: string
                format: uuid
            X-Kycert-Api-Version:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentUploadResponse'
              example:
                document_id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
                object: document
                type: comprovante_endereco
                status: pendente
                file_name: comprovante.pdf
                mime_type: application/pdf
                created_at: '2026-06-12T14:00:00Z'
                livemode: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/CustomerNotFound'
        '500':
          $ref: '#/components/responses/InternalError'
      x-codeSamples:
        - lang: cURL
          label: cURL
          source: >
            curl -X POST
            https://admin.kycert.com.br/api/v1/customers/CUSTOMER_ID/documents \
              -H "x-api-key: $KYCERT_API_KEY" \
              -F "type=comprovante_endereco" \
              -F "file=@/caminho/para/comprovante.pdf"
        - lang: Node
          label: Node.js
          source: |
            const form = new FormData()
            form.set('type', 'comprovante_endereco')
            form.set('file', fileBlob, 'comprovante.pdf')

            const res = await fetch(
              `https://admin.kycert.com.br/api/v1/customers/${customerId}/documents`,
              { method: 'POST', headers: { 'x-api-key': process.env.KYCERT_API_KEY }, body: form },
            )
            const { document_id, status } = await res.json()
            console.log(document_id, status)
components:
  schemas:
    DocumentUploadResponse:
      type: object
      properties:
        document_id:
          type: string
          format: uuid
        object:
          type: string
          enum:
            - document
        type:
          type: string
        status:
          type: string
          enum:
            - pendente
          description: >-
            Todo documento entra como pendente — aprovação/rejeição é exclusiva
            do dashboard
        file_name:
          type: string
        mime_type:
          type: string
        created_at:
          type: string
          format: date-time
        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:
    BadRequest:
      description: Requisição inválida — corrija os dados e tente novamente
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            missing_subject:
              summary: subject ausente
              value:
                error:
                  type: invalid_request_error
                  code: missing_subject
                  message: subject é obrigatório.
                  param: subject
            invalid_document:
              summary: CPF/CNPJ inválido
              value:
                error:
                  type: invalid_request_error
                  code: invalid_document
                  message: CPF (11 dígitos) ou CNPJ (14 dígitos) inválido.
                  param: subject.doc
            subject_type_mismatch:
              summary: tipo incompatível com template
              value:
                error:
                  type: invalid_request_error
                  code: subject_type_mismatch
                  message: Este template espera subject.type pj, recebido pf.
                  param: subject.type
            template_not_found:
              summary: template não encontrado
              value:
                error:
                  type: invalid_request_error
                  code: template_not_found
                  message: Template não encontrado.
                  param: template_id
            missing_default_template:
              summary: sem template_id e sem default configurado (feat-324)
              value:
                error:
                  type: invalid_request_error
                  code: missing_default_template
                  message: >-
                    Nenhum template padrão configurado para este tipo de
                    cliente. Configure um template padrão em Configurações →
                    Bureau no dashboard, ou envie template_id explicitamente.
                  param: template_id
            invalid_json:
              summary: JSON malformado
              value:
                error:
                  type: invalid_request_error
                  code: invalid_json
                  message: JSON inválido.
                  param: null
    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
    Forbidden:
      description: Escopo insuficiente para esta operação
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              type: authorization_error
              code: insufficient_scope
              message: Esta chave não tem permissão para criar runs.
              param: null
    CustomerNotFound:
      description: Cliente não encontrado ou não pertence ao tenant
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              type: invalid_request_error
              code: customer_not_found
              message: Cliente não encontrado.
              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

````