Pular para o conteúdo principal

Carteira e cartão pela API

Toda cobrança de um workspace — números, plano, linhas telefônicas — sai primeiro da carteira pré-paga e, no que a carteira não cobrir, do cartão salvo. Estes endpoints permitem que o seu sistema abasteça os dois sem mandar o seu cliente ao painel: Nenhuma destas chamadas cobra nada. Elas só emitem a cobrança: uma pessoa paga o Pix, preenche a página hospedada ou confirma o cartão na sua página. A carteira é creditada quando o pagamento é confirmado, e o webhook wallet.topup_paid avisa você.
Para montar um checkout dentro do seu próprio app — campos do cartão na sua página, sem redirecionamento — siga o guia Checkout transparente com Stripe Elements.

Credenciais e permissões

  • Uma credencial de nível de workspace: uma chave de workspace, ou uma chave de conta com x-workspace-id. Uma chave de número, ou um token limitado a alguns números, recebe 403 NUMBER_SCOPE_NOT_ALLOWED.
  • Papel, com uma chave de conta ou um token OAuth/MCP: recarregar e salvar cartão são ações do Proprietário; ler o status de uma recarga (GET /v1/billing/topups/{id}) exige Proprietário ou Administrador. Um papel abaixo disso recebe 403 PERMISSION_DENIED. Uma chave de workspace não tem usuário, então nenhum papel se aplica.
  • Moeda: o valor é sempre na moeda do workspace (BRL ou USD). Não dá para escolher por requisição.

Recarga com Pix

O Pix está disponível em workspaces em BRL. POST /v1/billing/checkout com purpose: "wallet_topup", method: "pix" e o amount:
  • Mostre pix.copyPaste (o código copia e cola) e/ou pix.qrCode (a imagem do QR code como URI data:image/png;base64,…, pronta para um <img src>; pode vir null) para quem vai pagar. Um Pix expira em 1 hora (pix.expiresAt).
  • Guarde o orderId: é com ele que você acompanha o pagamento em GET /v1/billing/topups/{id} e é ele que o webhook wallet.topup_paid informa.
  • Um workspace em USD recebe 400 PIX_NOT_ELIGIBLE. Use cartão.
  • successUrl e cancelUrl não fazem sentido num Pix: enviá-los dá 400 FIELD_NOT_APPLICABLE.

Recarga ou cartão numa página hospedada do Stripe

O mesmo endpoint, com method: "card" (o padrão), responde com uma url para uma página hospedada pelo Stripe. Abra-a no navegador de quem vai pagar.
Resposta (200)
Com purpose: "add_card", a página salva um cartão no workspace, sem cobrar nada. A resposta é { "url": "…", "purpose": "add_card" }. O cartão passa a ser o cartão salvo do workspace, em qualquer plano: ele cobre a carteira em toda cobrança — números, renovações do plano e linhas telefônicas. method não se aplica ao add_card (400 FIELD_NOT_APPLICABLE).
string
obrigatório
wallet_topup ou add_card. Qualquer outro valor → 400 INVALID_PURPOSE.
number
Obrigatório no wallet_topup: um número positivo na moeda do workspace. Ausente, zero, negativo ou não numérico → 400 INVALID_AMOUNT.
string
padrão:"card"
card ou pix, só no wallet_topup. Qualquer outro valor → 400 INVALID_METHOD.
string
Só cartão. Para onde a página hospedada leva o navegador depois de pagar ou salvar.
string
Só cartão. Para onde a página hospedada leva o navegador quando a pessoa desiste.
Os caminhos hospedados com cartão exigem um e-mail de cobrança no workspace; sem ele, a resposta é 400 NO_BILLING_EMAIL. O Pix não exige.

Acompanhar uma recarga

GET https://pilotstatus.com.br/v1/billing/topups/{id}, com o orderId de qualquer recarga — Pix, página hospedada ou Elements.
Resposta (200)
  • Só lê. Não chama nenhum provedor de pagamento, então consultar repetidamente é barato. O pedido passa a paid quando o provedor de pagamento nos confirma o pagamento.
  • Prefira o webhook a consultar em loop. O wallet.topup_paid chega uma vez por pedido creditado. Ele só é enviado depois que o pedido é concluído, então, quando chega, este endpoint já responde paid.
  • Um id de outro workspace recebe o mesmo 404 TOPUP_NOT_FOUND de um id que não existe.

Cartão na sua própria página (Stripe Elements)

Com estes endpoints, os campos do cartão ficam na sua página, feitos com o Payment Element do Stripe. O seu servidor nos pede um intent; a sua página o confirma com o Stripe.js; o seu servidor chama o sync.
Os dados do cartão nunca chegam à Pilot Status, e também nunca devem chegar ao seu servidor. O número do cartão vai do navegador de quem paga direto para o Stripe, dentro dos campos do próprio Stripe. A sua API key fica no seu servidor. Isso mantém os dados de cartão fora dos seus sistemas e dos nossos, e é o que mantém pequeno o seu escopo de PCI DSS. Nunca colete números de cartão em campos seus e nunca os envie para nenhuma API.
Os quatro endpoints:
  • respondem com Cache-Control: no-store — um clientSecret permite a quem o tiver confirmar o intent, então não o guarde em cache nem em log;
  • devolvem a publishableKey com que carregar o Stripe.js, então você não precisa de uma conta Stripe própria;
  • respondem 503 PAYMENTS_NOT_CONFIGURED quando pagamentos com cartão não estão disponíveis do nosso lado. Ofereça Pix (BRL) ou tente de novo mais tarde.

Salvar um cartão

POST https://pilotstatus.com.br/v1/billing/card/setup-intent, sem corpo (ou {}). Qualquer campo dá 400 UNKNOWN_FIELDS.
Resposta (200)
Na sua página, monte o Payment Element com o clientSecret e chame stripe.confirmSetup(...). Quando ele resolver, chame: POST https://pilotstatus.com.br/v1/billing/card/setup-intent/{setupIntentId}/sync
Resposta (200)
  • O sync lê o intent no Stripe; ele nunca confia na sua página. Quando o Stripe diz succeeded, o cartão passa a ser o cartão salvo do workspace, substituindo o anterior.
  • Ainda não concluído (processing, requires_action, …): saved: false, card: null e o status do Stripe. Chame de novo mais tarde, ou conte com o nosso lado: o Stripe também nos avisa, e o cartão é salvo nesse momento.
  • Idempotente. Chamar de novo, ou concorrer com o nosso próprio aviso do Stripe, salva o cartão uma vez só.
  • Um id que não é seti_… → 400 INVALID_SETUP_INTENT_ID. Um id que não criamos para este workspace → 404 SETUP_INTENT_NOT_FOUND.

Recarga com cartão

POST https://pilotstatus.com.br/v1/billing/payment-intents
string
obrigatório
wallet_topup, o único valor hoje. Qualquer outro → 400 INVALID_PURPOSE.
number
obrigatório
Um número positivo na moeda do workspace, arredondado para centavos. Não positivo → 400 INVALID_AMOUNT. Abaixo do mínimo de cobrança com cartão do Stripe → 400 AMOUNT_TOO_SMALL.
boolean
padrão:"false"
true também salva este cartão como o cartão do workspace quando o pagamento for concluído. Se não for booleano → 400 INVALID_FIELD_TYPE.
string
Opcional. Até 255 caracteres, sem caracteres de controle (caso contrário, 400 IDEMPOTENCY_KEY_INVALID). Veja abaixo.
Qualquer outro campo do corpo dá 400 UNKNOWN_FIELDS.
Na sua página, chame stripe.confirmPayment(...) com o clientSecret. Quando ele resolver, chame: POST https://pilotstatus.com.br/v1/billing/payment-intents/{paymentIntentId}/sync
Resposta (200)
  • O sync nunca cobra. Ele não é o /confirm do Stripe: só reconcilia o que o Stripe já decidiu. A carteira é creditada exatamente uma vez, pelo sync ou pelo nosso aviso do Stripe, o que chegar primeiro.
  • Um id que não é pi_… → 400 INVALID_PAYMENT_INTENT_ID. Um id que não criamos como recarga deste workspace → 404 PAYMENT_INTENT_NOT_FOUND.
  • O orderId funciona em GET /v1/billing/topups/{id} como o de qualquer outra recarga.

Idempotency-Key

Envie-a no POST /v1/billing/payment-intents para que uma nova tentativa depois de um timeout não abra um segundo pedido.
  • Ausente: toda chamada cria um novo pedido e um novo payment intent.
  • Mesma chave e mesmo corpo em até 24 horas: você recebe o mesmo orderId, paymentIntentId e clientSecret, com o header Idempotent-Replayed: true. Nada novo é criado.
  • Mesma chave, corpo diferente: 409 IDEMPOTENCY_KEY_REUSED. Use uma chave nova.
  • Mesma chave enquanto a primeira chamada ainda está rodando: 409 IDEMPOTENCY_KEY_IN_USE. Tente de novo em instantes.
  • As chaves são por workspace. Uma chamada que falhou na validação, ou falhou depois, não gasta a chave.

Erros

Os erros trazem um code e uma mensagem error no formato English | Português — também no POST /v1/billing/checkout desde 29/10/2026 (INVALID_PURPOSE, INVALID_AMOUNT, NO_BILLING_EMAIL e CHECKOUT_FAILED eram só em inglês; os códigos e os status não mudaram). Decida pelo code, nunca pela frase. Os endpoints do Elements põem detalhes extras em details (por exemplo, details.unknownFields). Os erros de autenticação (401, 400 WORKSPACE_ID_REQUIRED, 403 WORKSPACE_MEMBERSHIP_REQUIRED) estão descritos em Tipos de chave.

Revendedores e o cartão compartilhado

O workspace de um cliente final de um revendedor pode ser vinculado ao cartão compartilhado (cartão de reserva) do revendedor. Quem controla o vínculo, workspace por workspace, é o revendedor: "shareCard": true no POST /v1/workspaces (ausente ou false: não compartilhado), e PUT / DELETE /v1/workspaces/{id}/shared-card depois; a equipe da Pilot Status só configura qual dos workspaces do revendedor guarda esse cartão. Antes de 29/10/2026, os workspaces criados por um revendedor o herdavam automaticamente. Esse cartão só cobre as cobranças que a plataforma faz — renovações, números, linhas — quando a carteira do workspace não as cobre e o workspace não tem cartão próprio. Ele nunca é usado por estes endpoints: uma recarga aqui é paga por quem paga o Pix, a página hospedada ou o seu formulário do Elements, e o add_card ou um setup intent salvam um cartão no próprio workspace, que passa a ter prioridade sobre o compartilhado. Veja Cobrança para revendedores.