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

# Configuração para o navegador

> O que o vext.js pergunta antes de tokenizar. Você normalmente não chama esta rota.

<Note>
  **Você normalmente não chama esta rota.** O [`vext.js`](/api-reference/cartao/vext-js) o faz por
  você, uma vez por página.

  Ela está documentada porque é exatamente o que a sua chave `pk_` alcança — e saber o que uma chave
  pública consegue fazer não deveria exigir ler o nosso código.
</Note>

É a única rota da API que aceita chave publicável. Ela não cria cobrança, não lê venda e não move
dinheiro: devolve o endereço de tokenização, a chave da adquirente desta loja, as bandeiras aceitas
e os limites de parcelamento.

<Warning>
  Ela **não diz por que** o cartão está indisponível — só que está.

  A razão é o público. Quem pergunta aqui é o navegador de um comprador qualquer, com uma chave que
  está no código-fonte de uma página pública: o motivo entregue a ele transformaria o endpoint num
  painel de status nosso, consultável por qualquer um.

  O motivo é acionável para **você**, e está em
  [`GET /v1/installments`](/api-reference/cobrancas/parcelas), atrás da chave secreta — em
  `unavailable_reason`.
</Warning>

A grade de parcelas **com preço** também não vem aqui: ela depende do valor, e mora em
[`GET /v1/installments`](/api-reference/cobrancas/parcelas), atrás da chave secreta. Entregá-la ao
navegador deixaria qualquer um ler a taxa contratada de uma loja a partir do HTML dela.


## OpenAPI

````yaml api-reference/openapi.yaml GET /v1/client_config
openapi: 3.1.0
info:
  title: Vext - API de pagamentos PIX com split
  version: 1.0.0
  summary: Cobranças PIX, estornos e saldo do vendedor.
  description: |
    API pública do gateway. Todo endpoint desta versão vive sob `/v1`.

    ## Dinheiro é sempre CENTAVOS INTEIROS

    Não há exceção. `amount`, `platform_fee`, `net_amount`, `refunded_amount`,
    `fee_returned`, `seller_debit`, `balance`, `available`, `reserved`, `debt`
    e `withdrawable` são inteiros em centavos de real.

    | Valor       | Envie / receba |
    |-------------|----------------|
    | R$ 1,00     | `100`          |
    | R$ 10,50    | `1050`         |
    | R$ 1.234,56 | `123456`       |

    Enviar `10.50` devolve `422`. A recusa é deliberada: `10.50` chega ao
    servidor como ponto flutuante, `10.50 * 100` não é exatamente `1050` em
    binário, e o centavo perdido só aparece na conciliação do mês. Diante de
    um decimal preferimos recusar a adivinhar a intenção - com dinheiro,
    adivinhar sai caro.

    ## Autenticação

    Toda chamada leva a chave no header:

    ```
    Authorization: Bearer sk_live_SUA_CHAVE_SECRETA
    ```

    A chave é criada em **Desenvolvedores** no painel e o valor em claro
    aparece **uma única vez**, na criação - guardamos apenas um hash
    SHA-256. Perdeu, gere outra.

    Cada chave carrega escopos (`charges:read`, `charges:write`,
    `refunds:write`, `balance:read`, `cards:manage`). Chamar um endpoint
    fora do escopo devolve `403`, e não `401`: a chave está certa, faltou
    permissão.

    Chaves de teste usam o prefixo `sk_test_`. A diferença é visível a olho
    nu de propósito - uma chave de teste colada em produção precisa ser
    reconhecível antes de alguém passar a tarde investigando.

    ## Idempotência

    Envie `Idempotency-Key` em todo `POST`. A conexão que cai depois da
    requisição e antes da resposta deixa você sem saber se cobrou; repetir
    com a mesma chave resolve isso sem risco de cobrar duas vezes.

    - **mesma chave, mesmo corpo** → a resposta gravada, com
      `Idempotent-Replayed: true`. A cobrança não é recriada.
    - **mesma chave, corpo diferente** → `409 idempotency_key_reused`.
      Devolver a cobrança antiga faria você acreditar que cobrou o valor
      novo.
    - **mesma chave, primeira ainda em voo** → `409
      idempotency_key_in_progress`. Repita em instantes.

    A ordem dos campos no JSON não invalida o retry. A chave vale por 24
    horas e é sua: dois clientes diferentes podem usar `pedido-1` sem
    colidir.

    ## Erros

    Todo erro - inclusive os que não previmos - tem a mesma forma:

    ```json
    {
      "error": {
        "type": "invalid_request_error",
        "code": "amount_below_minimum",
        "message": "…",
        "details": {}
      }
    }
    ```

    Trate pelo `code`, nunca pela `message`: o código é contrato, o texto
    pode ser reescrito a qualquer momento.
  contact:
    name: Suporte Vext
    url: https://wa.me/5511936185272
servers:
  - url: https://api.usevext.com
    description: Produção
security:
  - apiKey: []
tags:
  - name: Cobranças
    description: Criação, consulta e estorno de cobranças, no PIX e no cartão.
  - name: Saldo
    description: Quanto o vendedor tem e quanto pode sacar.
  - name: Cartão no navegador
    description: |
      O que o navegador do comprador precisa para virar um formulário de
      cartão: a credencial de tokenização e o token do desafio de
      autenticação. O número do cartão nunca passa por esta API.
  - name: Cartões
    description: |
      Os cartões que os compradores deixaram guardados, para cobrar de novo
      sem pedir nada a eles.
paths:
  /v1/client_config:
    get:
      tags:
        - Cartão no navegador
      summary: Configuração para o navegador
      description: |
        O que o `vext.js` pergunta antes de tokenizar. **Você normalmente não
        chama esta rota** - a biblioteca o faz por você, uma vez por página.

        Está documentada porque é o que a sua `pk_` alcança, e porque saber o
        que uma chave pública consegue fazer não deveria exigir ler o nosso
        código: ela devolve o endereço de tokenização, a chave pública da
        adquirente desta loja, as bandeiras aceitas e os limites de
        parcelamento. Não cria cobrança, não lê venda, não move dinheiro.

        Ela **não diz por que** o cartão está indisponível - só que está.
        Aqui quem pergunta é o navegador de um comprador qualquer, e o motivo
        entregue a qualquer visitante transformaria o endpoint num painel de
        status nosso. O motivo está em `GET /v1/installments`, atrás da chave
        secreta.

        A grade de parcelas com preço também não vem: ela depende do valor, e
        mora em `GET /v1/installments`, atrás da chave secreta.
      operationId: getClientConfig
      responses:
        '200':
          description: |
            A configuração. Com `available: false`, nenhuma credencial vem
            preenchida.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClientConfig'
        '401':
          description: Chave publicável ausente, inválida ou revogada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: |
            O domínio de onde a chamada veio não está na allowlist desta
            chave. Cadastre-o no painel.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
      security:
        - publishableKey: []
components:
  schemas:
    ClientConfig:
      type: object
      description: |
        O que o `vext.js` recebe. **Todo campo está sempre presente**, e os
        anuláveis vêm `null` quando o cartão está indisponível - nenhuma
        credencial é entregue nesse caso.

        Note o que **não** está aqui, e não vai estar: o motivo da
        indisponibilidade e a grade de parcelas com preço. Esta resposta é
        lida por um navegador qualquer, e os dois seriam informação da loja
        vazando para quem abre a página dela.
      required:
        - object
        - available
        - tokenization
        - three_d_secure
        - brands
        - max_installments
        - min_amount
      properties:
        object:
          type: string
          const: client_config
        available:
          type: boolean
        tokenization:
          type: object
          required:
            - endpoint
            - public_key
          properties:
            endpoint:
              type:
                - string
                - 'null'
              format: uri
            public_key:
              type:
                - string
                - 'null'
              description: |
                A chave pública da adquirente que atende esta loja - não a sua
                `pk_` da Vext. A biblioteca usa as duas: a sua para perguntar
                aqui, esta para tokenizar.
        three_d_secure:
          type: object
          required:
            - supported
          properties:
            supported:
              type: boolean
        brands:
          type: array
          items:
            type: string
            enum:
              - visa
              - mastercard
              - elo
              - hipercard
              - amex
              - diners
              - discover
              - aura
        max_installments:
          type:
            - integer
            - 'null'
          minimum: 1
          maximum: 12
        min_amount:
          oneOf:
            - $ref: '#/components/schemas/Cents'
            - type: 'null'
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - type
            - code
            - message
            - details
          properties:
            type:
              type: string
              description: |
                Família do erro. Responde "de quem é o problema e o que
                fazer": `invalid_request_error` se corrige mudando a
                chamada, `authentication_error` trocando a chave, e
                `api_error` é nosso - repita, não reescreva.
              enum:
                - authentication_error
                - permission_error
                - invalid_request_error
                - idempotency_error
                - not_found_error
                - rate_limit_error
                - api_error
                - card_error
            code:
              type: string
              description: |
                Código estável do erro. **Trate por ele.** A `message` pode
                ser reescrita a qualquer momento; o código, não.
            message:
              type: string
              description: Explicação em português, para leitura humana.
            details:
              type: object
              description: |
                Sempre objeto, mesmo vazio. Em `validation_failed`, traz os
                campos recusados e suas mensagens.
              additionalProperties: true
    Cents:
      type: integer
      format: int64
      description: |
        Valor monetário em **centavos inteiros** de real. R$ 10,00 = `1000`.
        Nunca decimal, nunca string.
  responses:
    RateLimited:
      description: |
        Requisições demais.

        Há três limites, por minuto: um por IP antes da autenticação, e
        depois um de leitura e um de escrita, ambos por CHAVE - o excesso
        de uma integração não consome a cota das outras do mesmo
        vendedor. A escrita é bem mais apertada porque cada cobrança
        criada vira uma chamada ao provedor.

        `Retry-After` diz em quantos segundos voltar. Os cabeçalhos
        `X-RateLimit-*` vêm em TODA resposta, não só nesta: são eles que
        permitem desacelerar antes de bater no limite.
      headers:
        Retry-After:
          description: Segundos até a cota ser reposta.
          schema:
            type: integer
        X-RateLimit-Limit:
          description: Requisições permitidas por minuto neste balde.
          schema:
            type: integer
        X-RateLimit-Remaining:
          description: Quantas ainda cabem no minuto corrente.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            cota:
              summary: too_many_requests
              value:
                error:
                  type: rate_limit_error
                  code: too_many_requests
                  message: Muitas requisições. Tente novamente em 42 segundos.
                  details:
                    retry_after: 42
            provedor:
              summary: provider_rate_limited
              value:
                error:
                  type: rate_limit_error
                  code: provider_rate_limited
                  message: >-
                    Limite de requisições do provedor atingido. Tente novamente
                    em instantes.
                  details: {}
    InternalError:
      description: |
        Erro inesperado do nosso lado. Repita com a mesma
        `Idempotency-Key` - ela é o que torna repetir seguro.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            interno:
              value:
                error:
                  type: api_error
                  code: internal_error
                  message: >-
                    Erro interno. Se persistir, informe o horário da chamada ao
                    suporte.
                  details: {}
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: sk_live_… | sk_test_…
      description: |
        Chave secreta do vendedor, criada no painel em **Desenvolvedores**.

        O valor em claro existe uma única vez, na criação. Guardamos só o
        hash SHA-256 - nem o suporte consegue recuperá-lo, o que é o ponto:
        um dump do nosso banco não permite cobrar em nome de ninguém.

        A chave vai no **servidor**. Colocá-la no navegador do comprador a
        entrega a qualquer pessoa que abra a página.
    publishableKey:
      type: apiKey
      in: header
      name: X-Vext-Publishable-Key
      description: |
        Chave **publicável**, criada no painel em **Desenvolvedores →
        Navegador**. É a única credencial que pode ir para o navegador do
        comprador, e a única que o `vext.js` usa.

        Ela não carrega escopos: o que ela alcança é decidido pela rota, e
        hoje é uma só - `GET /v1/client_config`. Não cria cobrança, não lê
        venda, não move dinheiro.

        Sendo pública por natureza - ela fica no código-fonte de uma página
        que qualquer um abre -, a proteção dela não é o segredo: é a
        **allowlist de domínios**. Cadastre os domínios das suas páginas de
        pagamento, e uma chamada de qualquer outra origem é recusada. Sem
        isso, copiar a chave do seu HTML bastaria para tokenizar cartão em
        nome da sua loja de outro site.

        Na prática você quase nunca a manda à mão: `Vext('pk_live_...')`
        cuida disso.

````