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

# Criar validação de posse de telefone por código no WhatsApp

> Confirma que a pessoa tem em mãos o telefone informado. É assíncrona: o resultado chega quando você confirma o código em `POST /validations/{id}/verify`.

## Ciclo de dois momentos

<Steps>
  <Step title="Você cria a validação">
    O código sai por WhatsApp para o telefone informado.
  </Step>

  <Step title="A pessoa devolve o código">
    Colete na sua interface o código que chegou.
  </Step>

  <Step title="Você confirma o código">
    Chame [`POST /validations/{id}/verify`](/validacoes/confirmar-codigo) com o `id` desta resposta.
  </Step>
</Steps>

### Prazo e tentativas

O código vale 10 minutos e a pessoa tem 3 tentativas. Um dígito errado gasta uma tentativa, não a validação inteira. Passado o prazo, a validação vira `expired`.

Não fixe o tamanho do código na sua interface: o número de dígitos é política do provedor.

### Número fora de alcance

Um celular brasileiro válido nem sempre consegue receber o código pelo WhatsApp, e aí a resposta é [`422 phone_not_reachable`](/erros#phone_not_reachable).


## OpenAPI

````yaml POST /validations/phone-ownership
openapi: 3.1.0
info:
  title: ID ZapSign
  version: 1.0.0
  description: >-
    API de validações de identidade da ID ZapSign. Crie validações, consulte o
    resultado e gerencie suas chaves de API.
  contact:
    name: Suporte ID ZapSign
    url: https://id.zapsign.com.br
    email: support@zapsign.com.br
servers:
  - url: https://api.id.zapsign.com.br/v1
    description: Produção
security:
  - bearerAuth: []
tags:
  - name: Validações
    description: Criação e consulta de validações.
  - name: Chaves de API
    description: Criação, listagem e revogação de chaves de API.
paths:
  /validations/phone-ownership:
    post:
      tags:
        - Validações
      summary: Cria uma validação de posse de telefone por código no WhatsApp.
      description: >-
        Confirma que a pessoa tem em mãos o telefone informado. É assíncrona: o
        resultado chega quando você confirma o código em `POST
        /validations/{id}/verify`.
      operationId: createPhoneOwnershipValidation
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PhoneOwnershipValidationInput'
            examples:
              minimo:
                summary: Mínimo obrigatório
                value:
                  phone: '+5511999998888'
                  consent: true
              com_referencia:
                summary: Com sua referência de correlação
                value:
                  phone: '+5511999998888'
                  consent: true
                  external_id: pedido_12345
      responses:
        '201':
          description: Código enviado e validação criada em estado pending.
          headers:
            Idempotent-Replayed:
              $ref: '#/components/headers/IdempotentReplayed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Validation'
              examples:
                pending:
                  summary: Código enviado, aguardando a pessoa
                  value:
                    object: validation
                    id: val_2W7HJ9RC4XKQ0ZB58NDT3PMFY6
                    type: phone_ownership
                    status: pending
                    result: null
                    reason_code: null
                    external_id: pedido_12345
                    created_at: '2026-08-07T14:00:00Z'
                    resolved_at: null
                    validation_url: null
                    expires_at: '2026-08-07T14:10:00Z'
                    attempts_remaining: 3
                    details: null
        '400':
          $ref: '#/components/responses/InvalidRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: >-
        Torna a operação segura para retentativa. Reenviar a mesma chave repete
        a resposta original em vez de executar a operação de novo.
      schema:
        type: string
        pattern: ^[A-Za-z0-9_-]{1,256}$
        minLength: 1
        maxLength: 256
      example: a3f1c9e2-7b64-4d18-9a02-5c6e8f0b1d34
  schemas:
    PhoneOwnershipValidationInput:
      type: object
      description: Dados para criar uma validação de posse de telefone.
      required:
        - phone
        - consent
      properties:
        phone:
          type: string
          description: Telefone celular brasileiro que recebe o código, em formato E.164.
          example: '+5511999998888'
        consent:
          type: boolean
          const: true
          description: Confirma que o titular consentiu com a consulta. Precisa ser `true`.
        external_id:
          type: string
          pattern: ^[A-Za-z0-9_-]{1,256}$
          minLength: 1
          maxLength: 256
          description: >-
            Sua referência de correlação. Volta em todas as respostas desta
            validação.
          example: pedido_12345
    Validation:
      type: object
      description: Resultado de uma validação.
      required:
        - object
        - id
        - type
        - status
        - result
        - reason_code
        - external_id
        - created_at
        - resolved_at
        - validation_url
        - expires_at
        - attempts_remaining
        - details
      properties:
        object:
          type: string
          enum:
            - validation
          description: Sempre `validation`.
        id:
          type: string
          description: Identificador único da validação.
          example: val_0NQW3MVYJYQ453E95V3V1T76AV
        type:
          type: string
          enum:
            - liveness_document_match
            - liveness_cpf_match
            - cpf_phone_match
            - sim_swap
            - telco_financial_capacity
            - phone_ownership
          description: >-
            Tipo da validação.


            | Valor | O que é |

            | --- | --- |

            | `cpf_phone_match` | Vínculo entre CPF e telefone |

            | `sim_swap` | Troca de chip |

            | `liveness_document_match` | Prova de vida + documento |

            | `phone_ownership` | Posse do telefone, por código no WhatsApp |

            | `liveness_cpf_match` | Prova de vida + CPF |

            | `telco_financial_capacity` | Capacidade financeira via operadora |


            Os quatro primeiros têm endpoint de criação. Os outros dois podem
            aparecer em consultas e listagens de contas que os usam por outros
            canais.
        status:
          type: string
          enum:
            - pending
            - completed
            - failed
            - expired
          description: >-
            Estado atual.


            | Valor | O que é |

            | --- | --- |

            | `pending` | Criada, aguardando o titular concluir o fluxo
            hospedado ou devolver o código |

            | `completed` | Resolvida; veja `result` |

            | `failed` | Não foi possível concluir |

            | `expired` | O prazo venceu sem conclusão |
        result:
          description: >-
            Resultado de negócio. É `null` enquanto `status` for `pending`.


            | Valor | Aparece em |

            | --- | --- |

            | `match` / `no_match` | `cpf_phone_match` |

            | `recent_swap` / `no_recent_swap` | `sim_swap` |

            | `passed` / `not_passed` | `liveness_document_match`,
            `liveness_cpf_match`, `phone_ownership` |

            | `high_capacity` / `moderate_capacity` / `low_capacity` |
            `telco_financial_capacity` |

            | `unknown` | Qualquer tipo, quando a fonte não deu uma resposta
            conclusiva. O motivo vem em `reason_code`. |
          anyOf:
            - type: string
              enum:
                - passed
                - not_passed
                - match
                - no_match
                - recent_swap
                - no_recent_swap
                - high_capacity
                - moderate_capacity
                - low_capacity
                - unknown
            - type: 'null'
        reason_code:
          description: >-
            Por que o resultado foi o que foi. É `null` quando o resultado não
            precisa de explicação. Acompanha `unknown` na maioria dos casos, e
            também `not_passed` quando a validação foi recusada por um motivo
            específico.


            | Valor | O que é |

            | --- | --- |

            | `liveness_failed` | Prova de vida não confirmada |

            | `face_mismatch` | O rosto não corresponde ao documento |

            | `spoof_detected` | Suspeita de fraude na captura |

            | `document_tampered` | Indícios de adulteração no documento |

            | `document_invalid` | Documento inválido ou não aceito |

            | `document_unreadable` | O documento não pôde ser lido |

            | `cpf_reference_not_found` | CPF não encontrado na base de
            referência |

            | `data_unavailable` | Dados indisponíveis na fonte consultada |

            | `phone_not_covered` | Telefone sem cobertura para a consulta |

            | `invalid_code` | O código enviado não confere, e as tentativas
            acabaram |
          anyOf:
            - type: string
              enum:
                - liveness_failed
                - face_mismatch
                - spoof_detected
                - document_tampered
                - document_invalid
                - document_unreadable
                - cpf_reference_not_found
                - data_unavailable
                - phone_not_covered
                - invalid_code
            - type: 'null'
        external_id:
          type:
            - string
            - 'null'
          description: A referência de correlação que você enviou na criação.
        created_at:
          type: string
          format: date-time
          description: Data de criação em ISO-8601.
        resolved_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Data de resolução em ISO-8601. É `null` enquanto não resolvida.
        validation_url:
          type:
            - string
            - 'null'
          description: >-
            URL para onde redirecionar o titular. Preenchida nas validações
            hospedadas; `null` nas síncronas.
        expires_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            Quando a validação vence, em ISO-8601. Preenchida nas validações
            hospedadas e nas que enviam um código; `null` nas síncronas.
        attempts_remaining:
          type:
            - integer
            - 'null'
          description: >-
            Quantas chamadas de verificação ainda serão aceitas. Preenchida nos
            tipos que enviam um código para a pessoa devolver (hoje só
            `phone_ownership`) e `null` nos demais.


            Vale `0` assim que a validação é resolvida, em qualquer desfecho,
            porque nenhuma outra tentativa será aceita.
        details:
          description: >-
            Dados extraídos, agrupados pela chave do `type`. Hoje só
            `liveness_document_match` produz detalhes, e só com `status` igual a
            `completed`. Nos demais casos é `null`.
          anyOf:
            - $ref: '#/components/schemas/ValidationDetails'
            - type: 'null'
    ValidationDetails:
      type: object
      description: >-
        Detalhes por tipo. Contém no máximo uma propriedade, cuja chave é o
        `type` do recurso.
      additionalProperties: false
      properties:
        liveness_document_match:
          $ref: '#/components/schemas/LivenessDocumentMatchDetails'
    ErrorEnvelope:
      type: object
      description: >-
        Formato único de erro. Toda falha da API responde neste formato. A lista
        completa de `code` está em [Erros](/erros).
      required:
        - error
      properties:
        error:
          type: object
          required:
            - type
            - code
            - message
            - doc_url
            - request_id
          properties:
            type:
              type: string
              enum:
                - invalid_request
                - authentication_error
                - not_found
                - api_error
                - idempotency_error
                - payment_error
                - rate_limit_error
              description: >-
                Categoria do erro. Serve para tratar famílias inteiras sem
                enumerar cada `code`.
            code:
              type: string
              description: >-
                Código estável do erro. É o que você deve usar para ramificar no
                seu código.
              example: missing_required_param
            message:
              type: string
              description: >-
                Descrição legível, em inglês. É para diagnóstico, não para
                mostrar ao usuário final.
            param:
              type: string
              description: O campo que causou o erro, quando aplicável.
              example: cpf
            doc_url:
              type: string
              description: Link direto para este código nesta documentação.
              example: https://docs.id.zapsign.com.br/missing_required_param
            request_id:
              type: string
              description: Identificador da requisição. Inclua no contato com o suporte.
              example: req_3PK3TA2H0MEMTDSAPREHSY4QC4
    LivenessDocumentMatchDetails:
      type: object
      description: >-
        Dados lidos do documento e da pessoa. Os dez campos vêm sempre, e cada
        um é `null` quando o provedor não conseguiu extraí-lo.
      additionalProperties: false
      required:
        - full_name
        - sex
        - cpf
        - date_of_birth
        - document_type
        - document_number
        - document_country
        - document_version
        - document_issued_at
        - document_expires_at
      properties:
        full_name:
          type:
            - string
            - 'null'
          description: Nome completo como consta no documento.
        sex:
          description: >-
            Sexo como consta no documento. Valores fora de `M` e `F` viram
            `null`.
          anyOf:
            - type: string
              enum:
                - M
                - F
            - type: 'null'
        cpf:
          type:
            - string
            - 'null'
          pattern: ^\d{11}$
          description: CPF lido do documento, só dígitos.
        date_of_birth:
          type:
            - string
            - 'null'
          format: date
          description: Data de nascimento no formato `YYYY-MM-DD`.
        document_type:
          description: >-
            Categoria do documento, independente do país emissor. Tipos que não
            caem no mapa viram `other`.


            | Valor | O que é |

            | --- | --- |

            | `id_card` | Documento de identidade |

            | `drivers_license` | Carteira de motorista |

            | `passport` | Passaporte |

            | `other` | Outro tipo reconhecido pelo provedor |
          anyOf:
            - type: string
              enum:
                - id_card
                - drivers_license
                - passport
                - other
            - type: 'null'
        document_number:
          type:
            - string
            - 'null'
          description: Número como consta no documento.
        document_country:
          type:
            - string
            - 'null'
          description: País emissor em ISO 3166-1 alpha-2, por exemplo `BR`.
        document_version:
          type:
            - string
            - 'null'
          description: Versão ou modelo do documento, segundo o provedor.
        document_issued_at:
          type:
            - string
            - 'null'
          format: date
          description: Data de emissão no formato `YYYY-MM-DD`.
        document_expires_at:
          type:
            - string
            - 'null'
          format: date
          description: >-
            Vencimento do documento no formato `YYYY-MM-DD`, ou `null` se não
            expira. Não confundir com o `expires_at` da raiz, que é o prazo da
            sessão hospedada.
  headers:
    IdempotentReplayed:
      description: >-
        Vem com o valor `true` quando a resposta é a repetição de uma requisição
        idempotente anterior, e não uma criação nova.
      schema:
        type: string
        enum:
          - 'true'
  responses:
    InvalidRequest:
      description: Requisição inválida (corpo, parâmetro ou chave de idempotência).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: invalid_request
              code: missing_required_param
              message: A required parameter is missing.
              param: cpf
              doc_url: https://docs.id.zapsign.com.br/missing_required_param
              request_id: req_3PK3TA2H0MEMTDSAPREHSY4QC4
    Unauthorized:
      description: Chave de API ausente ou inválida.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: authentication_error
              code: invalid_api_key
              message: The API key provided is invalid.
              doc_url: https://docs.id.zapsign.com.br/invalid_api_key
              request_id: req_3PK3TA2H0MEMTDSAPREHSY4QC4
    PaymentRequired:
      description: Saldo insuficiente para executar a validação.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: payment_error
              code: insufficient_balance
              message: Insufficient balance for this validation.
              doc_url: https://docs.id.zapsign.com.br/insufficient_balance
              request_id: req_3PK3TA2H0MEMTDSAPREHSY4QC4
    IdempotencyConflict:
      description: >-
        Outra requisição com a mesma chave de idempotência ainda está em
        andamento.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: idempotency_error
              code: idempotency_in_progress
              message: A request with the same Idempotency-Key is still in progress.
              doc_url: https://docs.id.zapsign.com.br/idempotency_in_progress
              request_id: req_3PK3TA2H0MEMTDSAPREHSY4QC4
    PayloadTooLarge:
      description: Corpo da requisição maior que o limite permitido.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: invalid_request
              code: request_too_large
              message: The request body is too large.
              doc_url: https://docs.id.zapsign.com.br/request_too_large
              request_id: req_3PK3TA2H0MEMTDSAPREHSY4QC4
    UnprocessableEntity:
      description: Valor semanticamente inválido (regra de negócio).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: invalid_request
              code: invalid_cpf
              message: The value provided is invalid.
              param: cpf
              doc_url: https://docs.id.zapsign.com.br/invalid_cpf
              request_id: req_3PK3TA2H0MEMTDSAPREHSY4QC4
    TooManyRequests:
      description: Limite de requisições excedido. Veja [Limites de uso](/limites-de-uso).
      headers:
        Retry-After:
          description: Segundos a aguardar antes de tentar de novo. Presente em todo 429.
          schema:
            type: integer
            minimum: 1
        RateLimit-Limit:
          description: Teto de requisições da classe para a conta. Só no 429 de taxa.
          schema:
            type: integer
            minimum: 1
        RateLimit-Remaining:
          description: Requisições restantes na janela atual. Só no 429 de taxa.
          schema:
            type: integer
            minimum: 0
        RateLimit-Reset:
          description: >-
            Segundos até o limite se restabelecer por completo. Só no 429 de
            taxa.
          schema:
            type: integer
            minimum: 0
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: rate_limit_error
              code: rate_limit_exceeded
              message: Too many requests. Please retry after the indicated delay.
              doc_url: https://docs.id.zapsign.com.br/rate_limit_exceeded
              request_id: req_3PK3TA2H0MEMTDSAPREHSY4QC4
    InternalError:
      description: Erro interno inesperado.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              type: api_error
              code: api_error
              message: An internal error occurred.
              doc_url: https://docs.id.zapsign.com.br/api_error
              request_id: req_3PK3TA2H0MEMTDSAPREHSY4QC4
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        Sua chave de API no cabeçalho `Authorization: Bearer sk_...`. Crie e
        revogue chaves no painel ou pelos endpoints de Chaves de API.

````