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

# Confirm contact code

> ⚠️ **This call is real**: it confirms a code of the workspace's identity verification, and the last one **decides** the verification.

Confirms the 6-digit code received on that channel and answers **200** with the state. When it was the last required code and the document is in, the verification is decided in this same response — `APPROVED`, or `IN_REVIEW` when a person needs to look at it (**both let you buy**, `canPurchase: true`) — and the `phone_line.verification_updated` webhook event goes out.

A code **expires 10 minutes** after it was sent (400 `CONTACT_CODE_EXPIRED`). A wrong code is 400 `CONTACT_CODE_INVALID` and counts as an attempt; after **5 wrong attempts** the code is locked (429 `CONTACT_CODE_TOO_MANY_ATTEMPTS`) — send a new one with `…/contact/{channel}/send`. A code is used once: retrying a confirm that already succeeded answers `CONTACT_CODE_INVALID` — read the state with `GET /v1/phone-lines/verification` instead. The only body field is `code`; the channel goes in the path. Requires a **tenant-scoped** key, like every `/v1/phone-lines` endpoint (a number-scoped key gets `403 NUMBER_SCOPE_NOT_ALLOWED`). Permission `phone_lines:purchase`, the same as buying: with an OAuth/MCP token only the workspace **OWNER** may call it (`403 PERMISSION_DENIED` otherwise). It is the same verification as the dashboard's (**Phone lines** → **Buy phone lines**): whichever you use, the other sees the result. The response is the verification state object, sent with `Cache-Control: no-store`; the document number and the contacts come back masked.



## OpenAPI

````yaml openapi.json POST /v1/phone-lines/verification/contact/{channel}/confirm
openapi: 3.1.0
info:
  title: Pilot Status API
  version: 1.0.0
  license:
    name: Pilot Status Terms of Service
    url: https://pilotstatus.com.br/terms
  description: >-
    Public REST API for Pilot Status. Authenticate with the `x-api-key: ps_...`
    header (or `x-api-key-id`). Base URL: https://pilotstatus.com.br
servers:
  - url: https://pilotstatus.com.br
security:
  - apiKey: []
  - apiKeyId: []
paths:
  /v1/phone-lines/verification/contact/{channel}/confirm:
    post:
      tags:
        - Phone Lines
      summary: Confirm a contact code
      description: >-
        ⚠️ **This call is real**: it confirms a code of the workspace's identity
        verification, and the last one **decides** the verification.


        Confirms the 6-digit code received on that channel and answers **200**
        with the state. When it was the last required code and the document is
        in, the verification is decided in this same response — `APPROVED`, or
        `IN_REVIEW` when a person needs to look at it (**both let you buy**,
        `canPurchase: true`) — and the `phone_line.verification_updated` webhook
        event goes out.


        A code **expires 10 minutes** after it was sent (400
        `CONTACT_CODE_EXPIRED`). A wrong code is 400 `CONTACT_CODE_INVALID` and
        counts as an attempt; after **5 wrong attempts** the code is locked (429
        `CONTACT_CODE_TOO_MANY_ATTEMPTS`) — send a new one with
        `…/contact/{channel}/send`. A code is used once: retrying a confirm that
        already succeeded answers `CONTACT_CODE_INVALID` — read the state with
        `GET /v1/phone-lines/verification` instead. The only body field is
        `code`; the channel goes in the path. Requires a **tenant-scoped** key,
        like every `/v1/phone-lines` endpoint (a number-scoped key gets `403
        NUMBER_SCOPE_NOT_ALLOWED`). Permission `phone_lines:purchase`, the same
        as buying: with an OAuth/MCP token only the workspace **OWNER** may call
        it (`403 PERMISSION_DENIED` otherwise). It is the same verification as
        the dashboard's (**Phone lines** → **Buy phone lines**): whichever you
        use, the other sees the result. The response is the verification state
        object, sent with `Cache-Control: no-store`; the document number and the
        contacts come back masked.
      operationId: post_phone_lines_verification_contact_channel_confirm
      parameters:
        - name: channel
          in: path
          required: true
          schema:
            type: string
            enum:
              - whatsapp
              - email
          example: email
          description: >-
            The contact channel, **lowercase**: `whatsapp` or `email`. It must
            be in `verification.contact.requiredChannels` (there in upper case).
            Anything else → 400 `INVALID_CONTACT_CHANNEL`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - code
              additionalProperties: false
              properties:
                code:
                  type: string
                  description: The 6-digit code received on that channel.
                  example: '482913'
            example:
              code: '482913'
      responses:
        '200':
          description: >-
            Code confirmed; the verification state (with the decision when it
            was the last code)
          headers:
            Cache-Control:
              description: Always `no-store`.
              schema:
                type: string
                enum:
                  - no-store
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                      - NONE
                      - PENDING_CONTACT
                      - IN_REVIEW
                      - APPROVED
                      - NEEDS_DOCUMENT
                      - REJECTED
                    description: >-
                      `NONE` (never started), `PENDING_CONTACT` (waiting for the
                      contact codes and, if `document.status` says so, a better
                      document), `IN_REVIEW` (finished, waiting for a person —
                      purchase allowed), `APPROVED` (verified — purchase
                      allowed), `NEEDS_DOCUMENT` (the reviewer asked for another
                      document) or `REJECTED` (final — talk to support). The
                      `phone_line.verification_updated` webhook uses the same
                      values for the last four.
                  canPurchase:
                    type: boolean
                    description: >-
                      Whether `POST /v1/phone-lines` is allowed now: `true` for
                      `APPROVED` and `IN_REVIEW`.
                  verification:
                    type: object
                    nullable: true
                    description: '`null` for `NONE`.'
                    properties:
                      id:
                        type: string
                        description: >-
                          Identifies the verification — the `verificationId` of
                          the webhook event, and what support will ask for.
                      documentType:
                        type: string
                        enum:
                          - CNPJ
                          - CPF
                      documentNumber:
                        type: string
                        description: >-
                          **Masked**: `**.***.333/0001-**` (CNPJ) or
                          `***.456.789-**` (CPF). The full number is never
                          returned.
                      legalName:
                        type: string
                        nullable: true
                        description: >-
                          The company name from the public registry (CNPJ).
                          `null` for a CPF, and when the registry could not be
                          consulted.
                      contact:
                        type: object
                        properties:
                          email:
                            type: string
                            description: The contact e-mail, masked (`a***@example.com`).
                          whatsapp:
                            type: string
                            nullable: true
                            description: The contact WhatsApp, masked (`+5511****0000`).
                          requiredChannels:
                            type: array
                            items:
                              type: string
                              enum:
                                - WHATSAPP
                                - EMAIL
                            description: >-
                              `WHATSAPP` and/or `EMAIL`: the codes this
                              verification needs. Frozen when the verification
                              starts. Through the API `EMAIL` is always
                              required.
                          confirmedChannels:
                            type: array
                            items:
                              type: string
                              enum:
                                - WHATSAPP
                                - EMAIL
                            description: The required channels already confirmed.
                          pendingChannels:
                            type: array
                            items:
                              type: string
                              enum:
                                - WHATSAPP
                                - EMAIL
                            description: >-
                              The required channels still waiting for their
                              code.
                      document:
                        type: object
                        properties:
                          status:
                            type: string
                            enum:
                              - RECEIVED
                              - UNREADABLE
                              - UNCLEAR
                              - REQUESTED
                              - MISSING
                            description: >-
                              `RECEIVED` (the document is in), `UNREADABLE` (we
                              found no number in the file), `UNCLEAR` (we found
                              numbers, but none we could trust), `REQUESTED`
                              (the reviewer asked for another document) or
                              `MISSING` (no document yet). For the last four,
                              send a new document with `POST
                              /v1/phone-lines/verification/document`.
                      reviewReason:
                        type: string
                        nullable: true
                        enum:
                          - REGISTRY_UNAVAILABLE
                          - DOCUMENT_CHECK
                          - null
                        description: >-
                          Only while `IN_REVIEW`: `REGISTRY_UNAVAILABLE` (the
                          CNPJ registry could not be consulted) or
                          `DOCUMENT_CHECK` (the document needs a look). `null`
                          otherwise.
                      reviewNote:
                        type: string
                        nullable: true
                        description: >-
                          The reviewer's message, on `REJECTED` and
                          `NEEDS_DOCUMENT` only. `null` otherwise, and it may be
                          `null` there too.
                      createdAt:
                        type: string
                        format: date-time
                        description: ISO 8601 — when this verification was submitted.
                      updatedAt:
                        type: string
                        format: date-time
                        description: ISO 8601 — its last change.
              examples:
                approved:
                  summary: Last code — approved
                  value:
                    status: APPROVED
                    canPurchase: true
                    verification:
                      id: cmg5k0v3r0000vf8example7k
                      documentType: CNPJ
                      documentNumber: '**.***.333/0001-**'
                      legalName: ACME LTDA
                      contact:
                        email: a***@example.com
                        whatsapp: +5511****0000
                        requiredChannels:
                          - WHATSAPP
                          - EMAIL
                        confirmedChannels:
                          - WHATSAPP
                          - EMAIL
                        pendingChannels: []
                      document:
                        status: RECEIVED
                      reviewReason: null
                      reviewNote: null
                      createdAt: '2026-10-01T13:55:02.000Z'
                      updatedAt: '2026-10-01T13:57:31.000Z'
                inReview:
                  summary: Last code — waiting for a person (purchase allowed)
                  value:
                    status: IN_REVIEW
                    canPurchase: true
                    verification:
                      id: cmg5k0v3r0000vf8example7k
                      documentType: CNPJ
                      documentNumber: '**.***.333/0001-**'
                      legalName: ACME LTDA
                      contact:
                        email: a***@example.com
                        whatsapp: +5511****0000
                        requiredChannels:
                          - WHATSAPP
                          - EMAIL
                        confirmedChannels:
                          - WHATSAPP
                          - EMAIL
                        pendingChannels: []
                      document:
                        status: RECEIVED
                      reviewReason: DOCUMENT_CHECK
                      reviewNote: null
                      createdAt: '2026-10-01T13:55:02.000Z'
                      updatedAt: '2026-10-01T14:20:12.000Z'
                onePending:
                  summary: One channel still pending
                  value:
                    status: PENDING_CONTACT
                    canPurchase: false
                    verification:
                      id: cmg5k0v3r0000vf8example7k
                      documentType: CNPJ
                      documentNumber: '**.***.333/0001-**'
                      legalName: ACME LTDA
                      contact:
                        email: a***@example.com
                        whatsapp: +5511****0000
                        requiredChannels:
                          - WHATSAPP
                          - EMAIL
                        confirmedChannels:
                          - WHATSAPP
                        pendingChannels:
                          - EMAIL
                      document:
                        status: RECEIVED
                      reviewReason: null
                      reviewNote: null
                      createdAt: '2026-10-01T13:55:02.000Z'
                      updatedAt: '2026-10-01T13:56:40.000Z'
        '400':
          description: >-
            `INVALID_CONTACT_CHANNEL` (`{channel}` is not `whatsapp` or
            `email`), `INVALID_BODY` (not a JSON object), `UNKNOWN_FIELDS`
            (anything but `code`), `CONTACT_CODE_INVALID` (wrong code — it
            counts as an attempt —, no code on that channel, or `code` is not a
            string), `CONTACT_CODE_EXPIRED` (older than 10 minutes) or
            `CONTACT_CODE_NOT_REQUIRED` (the channel is not in
            `requiredChannels`).
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    description: >-
                      English sentence and Portuguese sentence separated by ` |
                      `.
                  code:
                    type: string
                    description: Stable error code — branch on it, never on the text.
                  details:
                    type: object
                    description: >-
                      Validation errors only: `unknownFields` (array) or
                      `maxLength` (number).
                    additionalProperties: true
              examples:
                invalid:
                  summary: Wrong code
                  value:
                    error: Wrong code. | Código incorreto.
                    code: CONTACT_CODE_INVALID
                expired:
                  summary: Code expired
                  value:
                    error: >-
                      This code has expired. Request a new one. | Este código
                      expirou. Peça um novo.
                    code: CONTACT_CODE_EXPIRED
                notRequired:
                  summary: Channel not required
                  value:
                    error: >-
                      This channel does not need a code. | Este canal não
                      precisa de código.
                    code: CONTACT_CODE_NOT_REQUIRED
                invalidChannel:
                  summary: Unknown channel
                  value:
                    error: >-
                      The channel in the path must be "whatsapp" or "email". | O
                      canal no caminho deve ser "whatsapp" ou "email".
                    code: INVALID_CONTACT_CHANNEL
                unknownFields:
                  summary: Field other than code
                  value:
                    error: >-
                      Unknown field(s): channel. This endpoint accepts only
                      "code"; the channel goes in the path. | Campo(s)
                      desconhecido(s): channel. Este endpoint aceita apenas
                      "code"; o canal vai no caminho.
                    code: UNKNOWN_FIELDS
                    details:
                      unknownFields:
                        - channel
        '401':
          description: >-
            Missing or invalid `x-api-key` / `x-api-key-id` header (or OAuth
            Bearer token). No `code` in this body.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    type: string
              example:
                error: Invalid API key
        '403':
          description: >-
            Number-scoped key or per-number OAuth grant
            (`NUMBER_SCOPE_NOT_ALLOWED` — use the tenant-scoped key); OAuth/MCP
            token whose user is not the workspace OWNER (`PERMISSION_DENIED`) or
            no longer an active member (`WORKSPACE_MEMBERSHIP_REQUIRED`);
            archived workspace (`WORKSPACE_ARCHIVED`). The scope and role errors
            carry an English-only `error`.. `TENANT_REQUIRED`: credential not
            bound to any workspace
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    type: string
              examples:
                numberScope:
                  summary: Number-scoped key
                  value:
                    error: This endpoint requires a tenant-scoped API key
                    code: NUMBER_SCOPE_NOT_ALLOWED
                permission:
                  summary: OAuth/MCP user is not the OWNER
                  value:
                    error: >-
                      Your workspace role does not allow this action
                      (phone_lines:purchase).
                    code: PERMISSION_DENIED
                membership:
                  summary: OAuth/MCP user no longer an active member
                  value:
                    error: >-
                      The user behind this token is not an active member of this
                      workspace.
                    code: WORKSPACE_MEMBERSHIP_REQUIRED
                archived:
                  summary: Archived workspace
                  value:
                    error: >-
                      This workspace has been archived and its API is closed. |
                      Este workspace foi arquivado e sua API está encerrada.
                    code: WORKSPACE_ARCHIVED
                tenantRequired:
                  summary: Credential not bound to a workspace
                  value:
                    error: This credential is not bound to a workspace.
                    code: TENANT_REQUIRED
        '404':
          description: '`VERIFICATION_NOT_FOUND`: nothing was submitted in this workspace.'
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    type: string
              example:
                error: Verification not found. | Verificação não encontrada.
                code: VERIFICATION_NOT_FOUND
        '429':
          description: >-
            `CONTACT_CODE_TOO_MANY_ATTEMPTS`: 5 wrong attempts — send a new code
            with `…/contact/{channel}/send`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    type: string
              example:
                error: >-
                  Too many wrong attempts. Request a new code. | Tentativas
                  erradas demais. Peça um novo código.
                code: CONTACT_CODE_TOO_MANY_ATTEMPTS
        '500':
          description: Unexpected failure. Retry.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  code:
                    type: string
              example:
                error: Unexpected error. | Erro inesperado.
                code: INTERNAL_ERROR
components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: Your ps_ API key
    apiKeyId:
      type: apiKey
      in: header
      name: x-api-key-id
      description: API key id (alternative to x-api-key)

````

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