AtmosPay
Entrar

API v1

A referência da API pública: autenticação, escopos, endpoints, erros e o checklist de integração.

1. Visão geral

A API pública AtmosPay v1 permite que parceiros vendam produtos e atuem sobre assinaturas e faturas dos clientes da plataforma. Use-a para construir áreas do cliente externas, dashboards customizados ou automações que envolvam o ciclo de vida de uma venda.

Três usos. Cobrar: vender um produto do seu catálogo (POST /orders), assinar uma oferta recorrente (POST /subscriptions) ou cobrar um valor livre sem produto (POST /charges). Gerenciar: consultar e atuar em assinaturas e faturas já existentes. Relatar: GET /sales lista toda venda em que você participa (como vendedor, afiliado ou coprodutor), com ganho e UTMs, e GET /products devolve os produto_id/oferta_id usados nas rotas de cobrança.

As três rotas de cobrança aceitam PIX e cartão de crédito (forma_pagamento: "pix" | "cartao"). Sem o campo, a cobrança sai em PIX, e um card_token ou cartao enviado sem ele é ignorado. Envie forma_pagamento sempre, nos dois métodos.

A escolha não muda só a forma de pagar, muda quando você sabe o resultado: no PIX a cobrança nasce pendente e a confirmação chega pelo webhook; no cartão, em POST /orders e POST /charges, ela é síncrona: o 201 já traz aprovado (ou em_analise, se o antifraude segurou; raramente pendente, se a autorização ainda está processando, e aí o desfecho chega pelo webhook). Em POST /subscriptions normalmente não: o gateway costuma criar a assinatura sem expor a cobrança do primeiro ciclo, então o 201 vem pendente e a confirmação chega por webhook. A recusa, essa sim, é síncrona nas três rotas: volta como 402.

Para cartão, o caminho recomendado é tokenizar no seu front, direto no endpoint de tokenização (tokens_url de GET /api/v1/config), e enviar só o card_token: assim PAN e CVV não passam pelo seu servidor nem pelo nosso. A chave pública de tokenização também vem de GET /api/v1/config. Não a hardcode: ela muda entre sandbox e produção.

Base URL
https://www.atmospay.com/api/v1
Formato
application/json
OpenAPI 3.1 spec
GET /api/v1/openapi

Importável em Postman, Insomnia ou Stoplight para gerar SDK automaticamente.

2. Autenticação

Toda requisição precisa de uma API key no header Authorization:

Authorization: Bearer atmospay_live_<sua_chave>

Se o seu cliente HTTP só deixa configurar um header com nome próprio (n8n, alguns conectores), use a alternativa equivalente:

X-API-Key: atmospay_live_<sua_chave>

Como obter uma chave

  1. Acesse Developers → API keys
  2. Clique em Nova chave
  3. Identifique-a com um nome (ex: "Createfy Production")
  4. Copie a chave imediatamente: ela só aparece uma vez. Após fechar o modal, apenas o prefixo permanece visível.
  5. Configure no servidor da sua aplicação (env var, cofre de senhas). Nunca exponha em código client-side.

Uma chave pode ter data de expiração (passada a data, ela responde 401 "API key expirada"), mas as criadas pelo painel hoje não expiram: valem até serem revogadas na mesma tela.

Que dados a chave enxerga: cada chave é vinculada a uma única organização. As ações (cobrar, cancelar, trocar cartão ou método, gerar fatura ou PIX, reativar) valem só para o que essa organização vendeu. Algumas leituras (GET /subscriptions/{id}, /subscriptions/{id}/invoices, GET /subscriptions?papel=, /sales e /products) também mostram o que ela coproduz ou afilia, com os dados do cliente reduzidos e o documento null. Fora disso a resposta é 404, nunca 403.

Escopos

São hierárquicos: read ⊂ write ⊂ charge (charge inclui os outros dois). Uma chave sem o escopo que a rota exige recebe 403 insufficient_scope. Cada card da seção 4 mostra o escopo da rota ao lado do path.

readTodas as leituras: GET /config, /products, /products/{id}, /customers, /sales, /sales/{id}, /orders/{id}, /charges/{id}, /subscriptions, /subscriptions/{id}, /subscriptions/{id}/invoices, /invoices e /invoices/{id}; e POST /subscriptions/{id}/reactivate, que só devolve o link do checkout, sem efeito
writePOST /subscriptions/{id}/cancel
chargePOST /orders, /charges, /subscriptions, /invoices/{id}/pix, /subscriptions/{id}/generate-invoice, /subscriptions/{id}/card e /subscriptions/{id}/change-payment-method
Acesso totalO padrão. Consulta, cancela e cobra (todos os escopos). As chaves criadas antes de 29/10/2026 são todas assim.
Somente leituraEscopo read: só as leituras acima. Cancelar e cobrar respondem 403 insufficient_scope. No MCP, só as ferramentas de leitura.
O nível é escolhido ao criar a chave em Developers → API keys (Acesso total ou Somente leitura) e não muda depois: para trocar, crie outra chave e revogue a antiga. Integração que só lê (painel, BI, conciliação) deve usar somente leitura. Guarde a chave de acesso total como se ela pudesse cobrar, porque pode.

Erros de autenticação

Resposta 401. A mensagem diz qual o problema: chave ausente ou fora do formato, prefixo inválido, chave inexistente, revogada ou expirada.

{
  "error": {
    "type": "authentication_error",
    "message": "API key inválida"
  }
}

3. Gestão da assinatura: o caminho

Para mostrar ao cliente a assinatura dele e deixá-lo gerenciá-la sem sair da sua plataforma, você precisa de um id (o subscription_id) e das rotas abaixo. O detalhe de cada uma está na seção 4.

1. Obter o id da assinatura (e guardar)

  • Criou pela API (POST /subscriptions)? É o id do 201.
  • O cliente assinou no checkout AtmosPay? No 1º pagamento aprovado chega o webhook payment.approved com data.assinatura.primeira_cobranca = true: o id está em data.assinatura_id (o subscription.created, que sai junto, traz o mesmo campo).
  • Não guardou? GET /subscriptions?external_id=<seu id> ou GET /customers?email=<e-mail>.

Guarde o id junto da conta do cliente no seu banco: todas as rotas abaixo usam ele.

2. Mostrar a assinatura: GET /subscriptions/{id}

pendenteCriada, mas o 1º pagamento ainda não foi confirmado. Não libere o acesso. Sem pagamento, acaba cancelada.
ativaEm vigor. No PIX ela continua ativa mesmo com fatura vencida: quem diz se há atraso é cobranca.em_atraso.
pausadaSó no cartão: a renovação foi recusada. Saídas: trocar o cartão (/card, que já tenta a cobrança de novo) ou passar para PIX (/change-payment-method). Sem regularizar, vira expirada.
expiradaEncerrada por falta de pagamento. Para voltar, o cliente faz um novo checkout (/reactivate).
canceladaEncerrada pelo cliente, pelo painel, por esta API, pelo gateway ou ao fim dos ciclos contratados. Para voltar, /reactivate.

"Está em atraso?" é o bloco cobranca: em_atraso (bloqueie o acesso por ele), dias_atraso e total_em_aberto. proxima_cobranca é a data do próximo ciclo, não "pago até". Não decida atraso por ela.

3. Faturas

  • Listar: GET /subscriptions/{id}/invoices (a 1ª fatura e todas as renovações, cada uma com status e vencimento). As renovações se listam por aqui: elas não copiam o metadata da assinatura, então GET /invoices?external_id= não as encontra.
  • Pagar uma fatura pendente (PIX): POST /invoices/{id}/pix devolve pix.qrcode, pix.copia_cola e pix.expira_em (o QR vale 1 hora; expirado, chame de novo). A fatura de renovação continua pendente e pagável mesmo depois do vencimento; enquanto não for paga, a assinatura fica com cobranca.em_atraso = true.

4. Ações

CancelarPOST /subscriptions/{id}/cancel
502 = o gateway não confirmou e a assinatura NÃO foi cancelada; repita
Trocar o cartãoPOST /subscriptions/{id}/card
assinatura de cartão; na pausada, já tenta a cobrança recusada no cartão novo
PIX ↔ cartãoPOST /subscriptions/{id}/change-payment-method
mantém o mesmo subscription_id
ReativarPOST /subscriptions/{id}/reactivate
expirada ou cancelada: devolve o checkout_url para uma nova assinatura
Antecipar faturaPOST /subscriptions/{id}/generate-invoice
só PIX; depois gere o QR com POST /invoices/{id}/pix

5. Ser avisado das mudanças (webhooks)

  • payment.approved: todo pagamento aprovado. data.assinatura.primeira_cobranca diz se é a 1ª cobrança (true) ou uma renovação (false). Na renovação o evento traz data.assinatura_id, mas não o external_id: correlacione pelo id da assinatura
  • invoice.created, invoice.due_soon, invoice.due_today, invoice.paid: faturas de renovação; trazem data.assinatura_id e o data.external_id da assinatura
  • subscription.created, subscription.cancelled (com data.motivo) e subscription.overdue: ciclo de vida da assinatura; também trazem data.external_id

Nos webhooks os valores vêm em REAIS (99.9 = R$ 99,90); na API REST, em centavos (9990). Payloads na seção 9.

Ainda não configurou webhooks? Configure aqui. Eles são complementares à API. Webhooks notificam mudanças de status; a API executa ações iniciadas pelo usuário.

4. Endpoints

GET/api/v1/configescopo: read

Configuração pública para tokenizar cartão

Devolve a chave pública de tokenização que o SEU front usa para transformar o cartão em card_token. Existe para você não hardcodar essa chave: ela muda entre sandbox e produção.

Exige API key com escopo read. A public_key é pública por design: só cria token de cartão single-use, não lê nem cobra nada. Ainda assim ela sai daqui atrás de autenticação, para o endpoint não virar descoberta de ambiente.
Exemplo curl
curl https://www.atmospay.com/api/v1/config \
  -H "Authorization: Bearer atmospay_live_xxx"
Resposta 200
{
  "object": "config",
  "card_tokenization": {
    "disponivel": true,
    "public_key": "pk_G1aNGq...",
    "tokens_url": "https://api.pagar.me/core/v5/tokens",
    "token_ttl_segundos": 60
  },
  "parcelamento": {
    "max_parcelas": 12
  }
}
  • disponivel: false (com public_key: null) significa que a plataforma não tem chave utilizável neste ambiente: não há public key configurada ou, em produção, a configurada é de teste (pk_test_), que não é publicada porque o token gerado com ela seria recusado na cobrança. Nesse caso, mande os dados do cartão no objeto cartao em vez de card_token
  • token_ttl_segundos: 60: o token é single-use e expira rápido. Gere no submit do formulário, nunca no carregamento da página
  • parcelamento.max_parcelas é o teto aceito por POST /orders e POST /charges. Leia daqui em vez de fixar 12 na sua UI
Tokenizar no seu front (browser)
// 1. O SEU backend busca a config no AtmosPay (com a sua API key) e repassa
//    a public key pro browser. A chave atmospay_live_ nunca vai pro client.
const { card_tokenization } = await minhaApi("/atmospay/config")

// 2. No SUBMIT do formulário, o browser fala DIRETO com o endpoint de
//    tokenização (card_tokenization.tokens_url):
//    o PAN e o CVV não passam pelo seu servidor nem pelo nosso.
const res = await fetch(
  `${card_tokenization.tokens_url}?appId=${card_tokenization.public_key}`,
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      type: "card",
      card: {
        number: numero.replace(/\D/g, ""),
        holder_name: titular,
        exp_month: Number(mm),  // 12
        exp_year: Number(aa),   // 29 ou 2029
        cvv,
      },
    }),
  },
)
const { id: cardToken } = await res.json()   // "token_..."

// 3. Mande SÓ o token pro seu backend, que chama o AtmosPay na sequência.
await minhaApi("/checkout", { cardToken, parcelas: 3 })

O token vale para uma cobrança e morre em ~60s. Um 400 de validação do AtmosPay sai antes de falar com o gateway e não chega a usá-lo, mas em geral não sobra tempo para corrigir e reenviar: gere um token novo a cada tentativa. Depois de um 402 ou 502 ele já foi consumido.

GET/api/v1/productsescopo: read

Listar produtos (catálogo, coproduções e afiliações)

Devolve os produtos do titular da API key com o papel dele em cada um e as ofertas (que é onde mora o preço). É daqui que saem o produto_id e o oferta_id de POST /orders e POST /subscriptions.

Query parameters
papeltodos (padrão) | vendedor | coprodutor | afiliado. Só vendedor pode cobrar pela API; coprodutor e afiliado aparecem porque as vendas deles saem em GET /sales
statusativo | inativo | rascunho | arquivado
tipodigital | fisico | evento | ecommerce | doacao | link
searchbusca no nome do produto
limit1 a 100, padrão 50
starting_afterid do último produto da página anterior (cursor)
Exemplo curl
curl "https://www.atmospay.com/api/v1/products?papel=vendedor&status=ativo" \
  -H "Authorization: Bearer atmospay_live_xxx"
Resposta 200 (resumida)
{
  "object": "list",
  "data": [
    {
      "object": "product",
      "id": "7b1f2c4e-...",
      "nome": "Plano Pro",
      "tipo": "digital",
      "status": "ativo",
      "papel": "vendedor",
      "sou_vendedor": true,
      "sou_coprodutor": false,
      "sou_afiliado": false,
      "ofertas": [
        {
          "object": "offer",
          "id": "9c4e7a15-...",
          "nome": "Mensal",
          "preco": 9990,
          "preco_pix": null,
          "preco_cartao": null,
          "preco_boleto": null,
          "recorrente": true,
          "intervalo": "mensal",
          "is_default": true,
          "ativo": true
        }
      ],
      "entrega": {
        "object": "product_delivery",
        "redirect_url": "https://site.com/obrigado",
        "tipo_entrega": "acesso_plataforma",
        "tem_area_externa": false,
        "tem_acesso_plataforma": true
      },
      ...
    }
  ],
  "has_more": false,
  "next_starting_after": null
}
  • Preços em centavos. preco_pix/preco_cartao null = usa preco
  • recorrente: true → a oferta é de POST /subscriptions; false → de POST /orders
  • entrega.redirect_url vem cru, como foi cadastrado, e só para o dono do produto (coprodutor e afiliado recebem null). Em POST /orders e no webhook o mesmo campo já vem com ?order_id=
GET/api/v1/products/{id}escopo: read

Detalhe de um produto

O mesmo objeto de GET /products (ofertas e bloco entrega incluídos), para quando você já tem o produto_id.

curl https://www.atmospay.com/api/v1/products/7b1f2c4e-... \
  -H "Authorization: Bearer atmospay_live_xxx"
  • Só aceita o UUID: id que não é UUID volta 400 invalid_request (o slug é único por organização e não identifica o produto sozinho)
  • Mesma visibilidade da lista: o produto precisa ser seu, coproduzido por você ou afiliável por você; fora disso, 404
GET/api/v1/customersescopo: read

Buscar cliente por email

Retorna o cliente e as assinaturas dele que pertencem à sua organização. Use quando ainda não tem o subscription_id (fallback). 404 se o cliente nunca comprou da sua organização.

Query parameters
email obrigatório
Email do cliente cadastrado.
Exemplo curl
curl https://www.atmospay.com/api/v1/customers?email=joao@exemplo.com \
  -H "Authorization: Bearer atmospay_live_xxx"
Resposta 200
{
  "object": "customer",
  "id": "8f2a1b9c-...",
  "email": "joao@exemplo.com",
  "nome": "João da Silva",
  "telefone": "+5511999999999",
  "cpf": "12345678901",
  "criado_em": "2025-01-15T10:30:00.000Z",
  "subscriptions": {
    "object": "list",
    "data": [
      {
        "object": "subscription",
        "id": "1c8b2a4d-...",
        "status": "ativa",
        "intervalo": "mensal",
        "valor": 3990,
        "proxima_cobranca": "2026-06-04T00:00:00.000Z"
      }
    ]
  }
}
POST/api/v1/subscriptionsescopo: charge

Criar assinatura (PIX ou cartão)

Assina uma oferta recorrente do seu catálogo pela API e devolve a primeira fatura: o QR Code PIX do primeiro ciclo, ou o resultado da cobrança no cartão. É o par de POST /orders: mesma mecânica, só que a oferta precisa ser recorrente.

Exige API key com escopo charge. Preço e intervalo vêm sempre da oferta no banco: não existe campo de valor no corpo.
Qual oferta assinar

Envie oferta_id do plano, ou apenas produto_id para usar a oferta padrão do produto. Em qualquer caso a oferta precisa estar marcada como recorrente: é ela que define o intervalo (mensal, trimestral, semestral ou anual). Oferta avulsa retorna 400 apontando para POST /api/v1/orders.

Corpo
{
  "oferta_id": "9c4e7a15-...",
  "forma_pagamento": "pix",
  "cliente": {
    "nome": "Maria Silva",
    "email": "maria@exemplo.com",
    "telefone": "11988887777",
    "cpf": "123.456.789-09"
  },
  "metadata": { "external_id": "tenant-991" }
}
  • metadata.external_id: o ID do tenant/conta no seu SaaS. Fica na assinatura e na 1ª fatura: volta nos webhooks subscription.* e invoice.*, no payment.approved da 1ª cobrança e vira filtro em GET /subscriptions?external_id=. As faturas de renovação não copiam o metadata: o payment.approved delas não traz o external_id (use assinatura_id) e elas se listam por GET /subscriptions/{id}/invoices. Sempre envie
  • forma_pagamento ausente vale "pix" (e card_token/cartao sem ele são ignorados); envie sempre, como nos exemplos abaixo
  • Idempotency-Key no header evita que um retry após timeout crie duas assinaturas
  • cupom_codigo é aceito, mas o desconto vale para todos os ciclos (as faturas seguintes são cópias da inicial). Para "primeiro mês com desconto", crie uma oferta específica
  • afiliado (opcional): o código do afiliado do AtmosPay que indicou a assinatura, o mesmo do link dele. Ele recebe a comissão da primeira cobrança e das renovações, pela regra de renovação do programa do produto, congelada na compra. Mesmas regras e mesmos erros de POST /orders(veja "Afiliado e origem da venda" lá)
  • tracking (opcional): as UTMs e os click IDs da compra. Ficam na venda e na assinatura como os da URL do checkout e chegam à Utmify, ao Pixel do Meta e aos webhooks
Exemplo curl
curl -X POST https://www.atmospay.com/api/v1/subscriptions \
  -H "Authorization: Bearer atmospay_live_xxx" \
  -H "Idempotency-Key: 7b9c1f4a-..." \
  -H "Content-Type: application/json" \
  -d '{
    "oferta_id": "9c4e7a15-...",
    "forma_pagamento": "pix",
    "cliente": {
      "nome": "Maria Silva",
      "email": "maria@exemplo.com",
      "telefone": "11988887777",
      "cpf": "12345678909"
    },
    "metadata": { "external_id": "tenant-991" }
  }'
Resposta 201
{
  "object": "subscription",
  "id": "1c8b2a4d-...",
  "status": "pendente",
  "cliente_id": "c1d2e3f4-...",
  "produto_id": "7b1f2c4e-...",
  "oferta_id": "9c4e7a15-...",
  "intervalo": "mensal",
  "valor": 9990,
  "forma_pagamento": "pix",
  "proxima_cobranca": "2026-09-03T00:00:00.000Z",
  "external_id": "tenant-991",
  "first_invoice": {
    "object": "invoice",
    "id": "3f9c1a20-...",
    "subscription_id": "1c8b2a4d-...",
    "order_id": "ORD-...",
    "status": "pendente",
    "valor": 9990,
    "pix": {
      "qrcode": "https://gateway.example.com/....png",
      "copia_cola": "00020126580014br.gov.bcb.pix...",
      "expira_em": "2026-08-03T16:00:00.000Z"
    }
  }
}
No PIX, a assinatura nasce pendente, não ativa. Não libere acesso na resposta deste POST: o cliente ainda não pagou. Espere o webhook payment.approved (o payload traz assinatura.primeira_cobranca = true) ou consulte GET /api/v1/subscriptions/{id} até virar ativa.
O que acontece depois (PIX)
  • Cliente paga o PIX → a assinatura vira ativa e proxima_cobranca passa a ser a data do próximo ciclo, que é também o vencimento da fatura desse ciclo
  • 10 dias antes de proxima_cobranca o AtmosPay emite a fatura do ciclo, com vencimento nesse dia, e avisa o cliente: ele paga quando quiser até o vencimento. O QR Code PIX não sai na emissão (ele vale 1 hora): é gerado na hora de pagar, pelo link enviado ao cliente ou por POST /invoices/{id}/pix
  • proxima_cobranca não muda na emissão: avança um intervalo no dia do ciclo, ou antes, se a fatura for paga antes. Enquanto a fatura está no prazo, ela é o vencimento dessa fatura
  • Não pagou até o vencimento: a partir do dia seguinte (horário de Brasília) cobranca.em_atraso vira true: bloqueie o acesso por ele. A assinatura continua ativa (PIX não expira por atraso) e o acesso volta quando o pagamento entra (payment.approved)
  • Você acompanha por GET /subscriptions/{id}/invoices ou pelos webhooks
  • Para antecipar a emissão, use POST /subscriptions/{id}/generate-invoice
Assinatura com cartão de crédito

Envie "forma_pagamento": "cartao" com card_token (recomendado) ou o objeto cartao: os mesmos dois caminhos de POST /api/v1/orders, com as mesmas regras de campo, o mesmo cliente.endereco opcional e a mesma recusa de pagarme_card_id.

Com cartão, a recorrência é conduzida pelo gateway. O AtmosPay cria a assinatura no gateway (billing_type prepaid) e cada ciclo seguinte é cobrado automaticamente no cartão; não existe fatura PIX para emitir, então POST /subscriptions/{id}/generate-invoice não se aplica (ele é do fluxo PIX, em que quem renova é o AtmosPay pelo cron diário) e responde 400 payment_method_not_supported. Você continua acompanhando os ciclos por GET /subscriptions/{id}/invoices e pelos webhooks.
  • A recusa é síncrona (402), mas a aprovação normalmente não: o 201 costuma vir com a assinatura em pendente e a first_invoice em pendente: o gateway cria a assinatura sem expor a cobrança do primeiro ciclo. Libere o acesso no webhook payment.approved ou consultando GET /subscriptions/{id} até virar ativa
  • parcelas precisa ser 1 (ou ausente): a assinatura no cartão é cobrada em parcela única por ciclo, então aceitar 2x cobraria à vista sem avisar. Por isso o 400 em vez do silêncio
  • Idempotency-Key é obrigatório em cartão: sem o header vem 400. Um retry sem chave cria uma segunda assinatura e cobra de novo
  • cliente.telefone não é obrigatório no cartão (segue obrigatório no PIX)
  • Recusa na primeira cobrança → 402 card_declined, e a assinatura é encerrada nos dois lados (AtmosPay e gateway) antes da resposta: não sobra assinatura fantasma cobrando o cliente todo mês. Para tentar de novo, use outro cartão e uma Idempotency-Key nova
Exemplo curl
curl -X POST https://www.atmospay.com/api/v1/subscriptions \
  -H "Authorization: Bearer atmospay_live_xxx" \
  -H "Idempotency-Key: 7b9c1f4a-..." \
  -H "Content-Type: application/json" \
  -d '{
    "oferta_id": "9c4e7a15-...",
    "forma_pagamento": "cartao",
    "card_token": "token_xxx",
    "cliente": {
      "nome": "Maria Silva",
      "email": "maria@exemplo.com",
      "cpf": "12345678909"
    },
    "metadata": { "external_id": "tenant-991" }
  }'
Resposta 201 (cartão aceito)
{
  "object": "subscription",
  "id": "1c8b2a4d-...",
  "status": "pendente",
  "cliente_id": "c1d2e3f4-...",
  "produto_id": "7b1f2c4e-...",
  "oferta_id": "9c4e7a15-...",
  "intervalo": "mensal",
  "valor": 9990,
  "forma_pagamento": "cartao",
  "proxima_cobranca": "2026-09-03T00:00:00.000Z",
  "external_id": "tenant-991",
  "first_invoice": {
    "object": "invoice",
    "id": "3f9c1a20-...",
    "subscription_id": "1c8b2a4d-...",
    "order_id": "ORD-...",
    "referencia": "2026-08",
    "status": "pendente",
    "valor": 9990,
    "parcelas": 1,
    "valor_total": 9990,
    "juros": 0,
    "cartao": {
      "object": "card",
      "bandeira": "Visa",
      "ultimos_digitos": "0010",
      "parcelas": 1,
      "valor_parcela": 9990
    },
    "pix": null,
    "pago_em": null
  }
}
Em assinatura o 201 normalmente vem pendente mesmo com cartão aprovado, diferente de /orders e /charges, onde o cartão dá o veredito na hora. O gateway costuma criar a assinatura sem expor a cobrança do primeiro ciclo, e o AtmosPay só a confirma quando o webhook chega, segundos depois. Quando o gateway já devolve essa cobrança na criação, o 201 pode vir com a assinatura ativa e a first_invoice em aprovado: aí a confirmação já chegou. Em qualquer caso, libere o acesso só com a assinatura em ativa (no payment.approved ou consultando GET /api/v1/subscriptions/{id}). A recusa, essa sim, é sempre síncrona: vem como 402 no próprio POST. Ou seja: 402 quer dizer "não passou", mas 201 pendente ainda não quer dizer "passou".
em_analise na first_invoice quer dizer que o antifraude segurou a transação: ainda não é aprovação. Só libere acesso com a assinatura em ativa e a fatura em aprovado.
Resposta 402 (cartão recusado)
{
  "error": {
    "type": "card_declined",
    "message": "Saldo ou limite insuficiente.",
    "code": "iso:51",
    "campo": "geral",
    "invoice": { "id": "3f9c1a20-...", "status": "cancelado" }
  }
}

Em assinatura o id ecoado vem em error.invoice: a fatura do primeiro ciclo, já em cancelado. Guarde para reconciliação; a assinatura correspondente não existe mais.

Trocar de método depois

A assinatura não fica presa ao método escolhido aqui: POST /subscriptions/{id}/change-payment-method move de PIX para cartão e vice-versa preservando o mesmo subscription_id, e POST /subscriptions/{id}/card troca o cartão de uma assinatura de cartão (e, se ela estiver pausada por recusa, já tenta a cobrança recusada no cartão novo).

Resposta 502
  • gateway_error: falha de infraestrutura. Em cartão, se o corpo trouxer error.invoice, a fatura chegou a existir e a resposta fica gravada na Idempotency-Key por 24h: repetir com a mesma chave devolve o mesmo 502. Consulte antes (GET /invoices?external_id=) e, se não houve cobrança, tente de novo com uma chave nova. Regras completas na seção 5
  • subscription_not_created: caso raro em que a cobrança (PIX ou cartão) foi criada mas a assinatura não nasceu. Vem com error.invoice.id e também fica gravada na chave por 24h. Não recrie a assinatura: a cobrança existe (no PIX o cliente pode pagá-la; no cartão ela pode já ter sido paga); contate o suporte com esse id
GET/api/v1/subscriptionsescopo: read

Listar assinaturas

Lista paginada de assinaturas. Por padrão, só as que a sua organização vendeu; com papel, também as que ela coproduz ou afilia. Cada item já traz produto, cliente, resumo das faturas e estado de cobrança.

Query parameters
external_id: o metadata.external_id enviado na criação (tenant/conta no seu SaaS)
papel: vendedor (padrão) | afiliado | coprodutor | todos
Relação da sua organização com a assinatura. O padrão é vendedor (diferente de GET /sales, que já nasce em todos) para não mudar o resultado de integrações antigas. Fora de vendedor as ações de escrita continuam bloqueadas (404).
compradora_id: UUID da organização compradora (filtra por metadata.organizacao_compradora_id)
status: ativa | pausada | expirada | cancelada | pendente | atrasada
atrasada é filtro derivado, não um status devolvido: assinaturas ativa/pausada com alguma fatura que passou do vencimento sem ser paga (o mesmo critério de cobranca.em_atraso).
limit: 1..100 (default 20) · starting_after: UUID cursor
Exemplo: assinaturas ativas de um tenant
curl 'https://www.atmospay.com/api/v1/subscriptions?external_id=tenant-991&status=ativa' \
  -H "Authorization: Bearer atmospay_live_xxx"
Resposta 200
{
  "object": "list",
  "data": [
    {
      "object": "subscription",
      "id": "1c8b2a4d-...",
      "status": "ativa",
      "cliente_id": "c1d2e3f4-...",
      "produto_id": "7b1f2c4e-...",
      "oferta_id": "9c4e7a15-...",
      "intervalo": "mensal",
      "valor": 3990,
      "forma_pagamento": "pix",
      "proxima_cobranca": "2026-06-04T00:00:00.000Z",
      "cancelada_em": null,
      "criado_em": "2026-01-04T10:00:00.000Z",
      "atualizado_em": "2026-05-04T10:00:00.000Z",
      "produto": {
        "object": "product_reference",
        "id": "7b1f2c4e-...",
        "nome": "Plano Pro",
        "oferta_id": "9c4e7a15-...",
        "oferta_nome": "Mensal"
      },
      "cliente": {
        "object": "customer_reference",
        "id": "c1d2e3f4-...",
        "nome": "João da Silva",
        "email": "joao@exemplo.com"
      },
      "resumo_faturas": {
        "object": "invoice_summary",
        "total": 5,
        "pagas": 4,
        "pendentes": 1,
        "falhadas": 0,
        "valor_pago": 15960,
        "ultima": { "object": "invoice_reference", "id": "...", "status": "pendente", "valor": 3990, ... }
      },
      "cobranca": {
        "object": "billing_state",
        "em_atraso": false,
        "competencias_em_aberto": 0,
        "dias_atraso": 0,
        "total_em_aberto": 0
      },
      "compradora": null,
      "external_id": "tenant-991",
      "metadata": { "external_id": "tenant-991" }
    }
  ],
  "has_more": false,
  "next_starting_after": null
}

A lista completa de faturas de cada assinatura fica em GET /subscriptions/{id}/invoices. Paginação por cursor: enquanto has_more for true, repita com starting_after=<next_starting_after>.

GET/p/{oferta-slug}

Checkout hospedado: identificar comprador, tenant e origem pela URL

Quando você manda o cliente para o checkout do AtmosPay em vez de cobrar pela API, a URL carrega o que você precisa para reconhecer a venda depois: ?org=, ?external_id= e ?aff= viram metadata da venda e da assinatura, e a API v1 e os webhooks devolvem e filtram por eles.

URL de checkout com identificação
https://secure.atmospay.com/p/{oferta-slug}?org=11111111-2222-3333-4444-555555555555&external_id=tenant-991&aff=afiliado-77

O checkout mora em secure.atmospay.com; um link em www.atmospay.com/p/… funciona, mas passa por um redirecionamento (a query string é preservada).

Resposta da assinatura inclui
{
  "object": "subscription",
  "id": "1c8b2a4d-...",
  "compradora": {
    "object": "organization_reference",
    "id": "11111111-2222-3333-4444-555555555555"
  },
  "external_id": "tenant-991",
  "external_affiliate_id": "afiliado-77",
  "metadata": {
    "organizacao_compradora_id": "11111111-2222-3333-4444-555555555555",
    "external_id": "tenant-991",
    "external_affiliate_id": "afiliado-77"
  }
}
Parâmetros que o checkout lê
orgUUID da organização compradora → metadata.organizacao_compradora_id (e o atalho compradora nas respostas). UUID inválido é ignorado em silêncio
external_ido seu ID do tenant/conta → metadata.external_id. Até 100 caracteres: letras, números e _ . : - (fora disso, ignorado)
aff (ou external_affiliate_id)o afiliado de um sistema EXTERNO que indicou a venda → metadata.external_affiliate_id. Mesmo formato do external_id. O AtmosPay só guarda e devolve (API, filtro ?external_affiliate_id= e webhooks payment.approved, subscription.* e invoice.*); não paga comissão por ele. Não confundir com o afiliado do próprio AtmosPay (/p/{oferta}/{slug}) nem com ?ref= (origem de tráfego; na loja, é o slug do afiliado do AtmosPay)
utm_*, src, sck, click IDsorigem da venda → metadata.tracking
name, email, phone, documentpré-preenchem o formulário do comprador. Não viram metadata
Comportamento
  • Outros parâmetros de query são ignorados pelo checkout
  • Pela API, mande o afiliado externo em metadata.external_affiliate_id de POST /orders, /charges e /subscriptions: ele volta no campo external_affiliate_id das respostas e dos webhooks, e filtra GET /sales, /subscriptions e /invoices com ?external_affiliate_id=
  • O afiliado do AtmosPay (o do link /p/{oferta}/{codigo}, que recebe comissão) vai pela API no campo afiliado de POST /orders e /subscriptions, e as UTMs da compra no campo tracking das três rotas
  • Pela API, o mesmo metadata é enviado no campo metadata de POST /orders, /charges e /subscriptions: até 10 chaves, valores primitivos de até 500 caracteres (padrão de mercado)
  • Chaves prefixadas com _ e as chaves internas do sistema são reservadas e ignoradas
  • O metadata fica na assinatura e na 1ª venda. As faturas de renovação não o copiam: não aparecem em GET /invoices?external_id= (liste-as por GET /subscriptions/{id}/invoices) e o payment.approved delas não traz external_id nem compradora; use data.assinatura_id. Os eventos invoice.* e subscription.* leem o external_id da assinatura e o trazem
  • Precisa de mais campos via URL? Abra um ticket: é trivial liberar ?meta_xxx=valor sob demanda
GET/api/v1/subscriptions/{id}escopo: read

Detalhe de uma assinatura

Retorna o objeto completo da assinatura (produto, cliente, resumo das faturas e estado de cobrança) mais o objeto customer aninhado.

Path parameters
id: UUID da assinatura (outro formato responde 404)

Visível para a organização vendedora e também para quem participa do split das vendas dela (coprodução, afiliação, indicação). Nesse segundo caso customer.telefone vem mascarado e customer.cpf vem null, e as ações de escrita (/cancel, /card, /change-payment-method, /generate-invoice, /reactivate) respondem 404: coprodutor acompanha, não opera. O que cada status quer dizer está na seção 3.

Exemplo curl
curl https://www.atmospay.com/api/v1/subscriptions/1c8b2a4d-... \
  -H "Authorization: Bearer atmospay_live_xxx"
Resposta 200
{
  "object": "subscription",
  "id": "1c8b2a4d-...",
  "status": "ativa",
  "cliente_id": "8f2a1b9c-...",
  "produto_id": "abc...",
  "oferta_id": "def...",
  "intervalo": "mensal",
  "valor": 3990,
  "forma_pagamento": "cartao",
  "proxima_cobranca": "2026-06-04T00:00:00.000Z",
  "cancelada_em": null,
  "criado_em": "2025-12-04T10:00:00.000Z",
  "produto": { "object": "product_reference", "id": "abc...", "nome": "Plano Pro", ... },
  "cliente": { "object": "customer_reference", "id": "8f2a1b9c-...", "nome": "João da Silva", "email": "joao@exemplo.com" },
  "resumo_faturas": { "object": "invoice_summary", "total": 6, "pagas": 6, ... },
  "cobranca": {
    "object": "billing_state",
    "em_atraso": false,
    "competencias_em_aberto": 0,
    "dias_atraso": 0,
    "total_em_aberto": 0
  },
  "compradora": null,
  "external_id": "tenant-991",
  "metadata": { "external_id": "tenant-991" },
  "customer": {
    "object": "customer",
    "id": "8f2a1b9c-...",
    "email": "joao@exemplo.com",
    "nome": "João da Silva",
    "telefone": "+5511999999999",
    "cpf": "12345678901",
    "criado_em": "2025-01-15T10:30:00.000Z"
  }
}
proxima_cobranca é a data do próximo ciclo, e o vencimento da fatura desse ciclo. No PIX essa fatura é emitida 10 dias antes, com vencimentoigual a essa data, e o campo não muda na emissão: ele avança um intervalo no dia do ciclo, ou antes, se a fatura for paga antes. No cartão é a data da próxima cobrança automática. Não use como "acesso pago até": para saber quem está devendo, use cobranca.em_atraso (em PIX a assinatura segue ativa mesmo com fatura vencida).
POST/api/v1/subscriptions/{id}/cancelescopo: write

Cancelar assinatura

Cancela no gateway de pagamento e só então marca status='cancelada'. Idempotente: chamar de novo em assinatura já cancelada retorna 200 com a subscription atual.

Headers opcionais
Idempotency-Key
UUID gerado pelo seu sistema. Garante que tentativas duplicadas (em até 24h) retornem a mesma resposta sem reprocessar.
Exemplo curl
curl -X POST https://www.atmospay.com/api/v1/subscriptions/1c8b2a4d-.../cancel \
  -H "Authorization: Bearer atmospay_live_xxx" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000"
Resposta 200
{
  "object": "subscription",
  "id": "1c8b2a4d-...",
  "status": "cancelada",
  "cancelada_em": "2026-05-04T15:30:00.000Z",
  ...
}
Comportamento
  • Cancela no gateway de pagamento antes de marcar como cancelada, e o passo é bloqueante: se o gateway não confirmar, a resposta é 502 gateway_error e nada muda do nosso lado: a assinatura continua ativa e cobrando. Trate o 502 como "não cancelou" e repita: a mesma Idempotency-Key serve, porque o 502 não entra no cache
  • Marca cancelada_em com timestamp atual
  • Revoga acessos a cursos vinculados ao produto, se aplicável
  • Nenhuma fatura nova é emitida, e a fatura de um ciclo que ainda não começou é cancelada, inclusive a PIX emitida 10 dias antes do ciclo
  • Faturas de ciclos já iniciados e não pagas continuam pagáveis: a dívida de um período já prestado não some com o cancelamento
  • Dispara o webhook subscription.cancelled com data.motivo: "admin"
  • Exige escopo write: a chave "Somente leitura" recebe 403 insufficient_scope (seção 2)
POST/api/v1/subscriptions/{id}/cardescopo: charge

Atualizar o cartão da assinatura (e recobrar, se ela foi pausada por recusa)

Troca o cartão usado nas cobranças automáticas, mantendo a mesma assinatura. Numa assinatura pausada porque a renovação foi recusada, também tenta na hora a cobrança recusada no cartão novo.

Três fontes de cartão: envie só uma (duas no mesmo corpo voltam 400): card_token (recomendado: tokenizado no seu front com a public key de GET /config, PCI SAQ-A), pagarme_card_id (um cartão já salvo do mesmo cliente da assinatura) ou cartao com os dados crus. Com cartao, os dados passam pelo seu servidor e pelo nosso (SAQ A-EP); nunca são persistidos, seguem em memória para o gateway numa única requisição. Só use esse caminho se a sua aplicação já opera nesse escopo.
Pré-requisitos
  • Assinatura de cartão, já vinculada a uma recorrência no gateway. Assinatura PIX responde 400 invalid_state; para levá-la ao cartão, use change-payment-method
  • Status ativa (inclusive com cobranca.em_atraso = true; atraso não é um status) ou pausada. Outro status → 400 invalid_state
  • Conta no limite de 10 req/min de cartão por API key, em qualquer status da assinatura (seção 6)
Dois comportamentos, pelo status
ativaTroca o cartão das próximas cobranças. Nada é cobrado agora. Com card_token o cartão é verificado no emissor ao ser cadastrado; se ele negar, vem 402 card_declined (sem cobrança).
pausadaA renovação foi recusada no cartão. A cobrança recusada é tentada NA HORA no cartão novo: aprovada, a fatura do mês é paga, a assinatura volta a ativa e o cartão passa a valer para as próximas cobranças (a mesma assinatura, sem criar outra). Recusada, nada muda e você pode tentar outro cartão, até o teto de tentativas.
Body
{ "card_token": "token_abc123XYZ" }
  • card_token: o token_… devolvido pela tokenização; vale uma vez e dura ~60s
  • pagarme_card_id: o card_… de um cartão salvo do cliente desta assinatura; de outro cliente → 404
  • cartao ({ numero, titular, validade, cvv }): número de 13 a 19 dígitos, com ou sem máscara; validade MM/AA ou MM/YYYY (vencido é recusado); CVV de 3 ou 4 dígitos
Exemplo curl
curl -X POST https://www.atmospay.com/api/v1/subscriptions/1c8b2a4d-.../card \
  -H "Authorization: Bearer atmospay_live_xxx" \
  -H "Idempotency-Key: 550e8400-..." \
  -H "Content-Type: application/json" \
  -d '{"card_token":"token_abc123XYZ"}'
Resposta 200 (ativa)
{
  "object": "subscription",
  "id": "1c8b2a4d-...",
  "status": "ativa",
  "forma_pagamento": "cartao",
  "cartao": {
    "bandeira": "visa",
    "ultimos_digitos": "1111",
    "titular": "MARIA SILVA",
    "exp_month": 12,
    "exp_year": 2029
  },
  ...
}
Resposta 200 (pausada: a cobrança recusada foi paga)
{
  "object": "subscription",
  "id": "1c8b2a4d-...",
  "status": "ativa",
  "cobranca": { "status": "paga" },
  "cartao": {
    "bandeira": "visa",
    "ultimos_digitos": "1111",
    "titular": null,
    "exp_month": null,
    "exp_year": null
  },
  ...
}
Outros status
  • 202 (só na pausada): o gateway não confirmou a cobrança a tempo; o corpo traz cobranca.status: "pendente". A conclusão vem em minutos (a assinatura volta a ativa e sai o webhook invoice.paid)
  • 402 card_declined: na pausada, o emissor recusou o cartão novo (o corpo traz tentativas_restantes) ou é o mesmo cartão já recusado (code: "mesmo_cartao"); nada foi alterado. Na ativa, só com card_token: recusa na verificação do cartão
  • 409: na pausada, idempotency_key_in_use (a primeira requisição com esta chave ainda está em voo; não repita, espere a resposta dela) ou invalid_state com um code que explica o estado (cobrança em curso; recorrência já encerrada no gateway, e aí use change-payment-method com metodo: "pix"; alteração já em andamento). Na ativa: invalid_state com code: "operacao_em_andamento" (uma troca iniciada pelo cliente ainda está sendo concluída); repita em minutos
  • 429 (dois casos, com corpos diferentes): o limitador de 10/min da API key (corpo { error: string, retryAfter } e header Retry-After) e, só na pausada, too_many_attempts: teto de 3 tentativas em 24h e 5 em 30 dias por assinatura, somando todas as origens (link público, área do cliente e API), com tentativas_restantes: 0 e sem Retry-After
  • 400 invalid_request / invalid_state e 404 not_found: ver acima
  • 502 gateway_error (na ativa): o gateway falhou ao cadastrar ou definir o cartão. 503 gateway_error (na pausada): o gateway não respondeu antes de a cobrança sair; nada foi alterado nem contou no teto; repita em alguns minutos
  • 500 internal_error: falha nossa; nada foi alterado
Idempotência nesta rota

Idempotency-Key é opcional (24h). Na ativa só o 200 fica gravado. Na pausada a chave é reservada antes da cobrança (409 idempotency_key_in_use enquanto a primeira está em voo) e a recusa definitiva (402, 429, 409 terminal) também fica gravada: para tentar outro cartão, use uma chave nova. 400/404/5xx não gravam: repita com a mesma chave.

Com card_token ou cartao, o cartão também passa a ficar salvo para o cliente, e a assinatura fica com forma_pagamento: "cartao".

POST/api/v1/subscriptions/{id}/change-payment-methodescopo: charge

Trocar método de pagamento (PIX ↔ cartão)

Troca a forma de pagamento de uma assinatura ativa ou pausada entre PIX e cartão, preservando o mesmo subscription_id. Conforme o estado, a troca cobra na hora ou só fica agendada para a próxima cobrança (veja o comportamento abaixo).

Headers opcionais
Idempotency-Key
Recomendado: PIX → cartão pode cobrar na hora. Só a resposta de sucesso (200 ou 202) fica gravada por 24h: repetir com a mesma chave devolve essa resposta sem trocar de novo. Erros não ficam gravados; depois de um 502, consulte a assinatura (GET /subscriptions/{id}) antes de repetir.
Body: PIX → cartão
{
  "metodo": "cartao",
  "cartao": {
    "numero": "4111111111111111",
    "titular": "MARIA SILVA",
    "validade": "12/29",
    "cvv": "123"
  }
}
  • Alternativa: { "metodo": "cartao", "pagarme_card_id": "card_xxx" } para usar um cartão já salvo do mesmo cliente (sem enviar PAN/CVV de novo). Cartão de outro cliente → 403
  • Não aceita card_token: aqui o cartão entra como cartao ou pagarme_card_id
Body: cartão → PIX
{ "metodo": "pix" }
Exemplo curl
curl -X POST https://www.atmospay.com/api/v1/subscriptions/1c8b2a4d-.../change-payment-method \
  -H "Authorization: Bearer atmospay_live_xxx" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"metodo":"cartao","cartao":{"numero":"4111111111111111","titular":"MARIA SILVA","validade":"12/29","cvv":"123"}}'
Resposta 200 (cobrado na hora)
{
  "object": "subscription",
  "id": "1c8b2a4d-...",
  "status": "ativa",
  "forma_pagamento": "cartao",
  ...,
  "troca": {
    "status": "success",
    "cartao": {
      "bandeira": "visa",
      "ultimos_digitos": "1111",
      "titular": "MARIA SILVA"
    },
    "venda_id": "uuid-da-nova-venda"
  }
}

Na troca agendada (abaixo) a resposta é a mesma, sem troca.venda_id: nada foi cobrado.

Comportamento
  • Preserva o subscription_id interno; só muda o método. Aceita assinatura ativa ou pausada.
  • PIX → cartão com fatura PIX em aberto: cobra no cartão na hora (a assinatura é criada no gateway com cobrança imediata, prepaid). Aprovado, a fatura PIX em aberto vira status=cancelado com substituida_por_venda_id apontando para a venda nova, que é o troca.venda_id.
  • PIX → cartão com o ciclo atual já pago (sem fatura PIX em aberto e proxima_cobranca no futuro): só agenda. Nada é cobrado agora; a 1ª cobrança no cartão sai em proxima_cobranca, e a resposta vem sem troca.venda_id.
  • Cartão → PIX (ativa): cancela a recorrência no gateway. A fatura de cartão corrente (se houver) continua válida até concluir/falhar; a próxima nasce em PIX, emitida 10 dias antes de proxima_cobranca e vencendo nela.
  • Cartão → PIX (pausada por recusa): a mesma assinatura vira PIX e volta a ativa, a recorrência no cartão é encerrada e a fatura PIX do mês recusado é emitida na hora: troca.venda_id é essa fatura (gere o QR com POST /invoices/{id}/pix). Para tentar a cobrança recusada num cartão novo, use /card.
  • Toda troca dispara e-mail de confirmação pro cliente com aviso anti-fraude.
  • Os dados de cartao não são persistidos: passam em memória só para o gateway (mesmo escopo PCI SAQ A-EP do /card).
Outros status
  • 202: corpo com troca.status: "pending" (não é erro). O cartão ficou em análise no PIX → cartão, ou, no cartão → PIX (ativa ou pausada), o gateway não confirmou a tempo. A conclusão vem em minutos, e um e-mail é enviado ao cliente.
  • 402 card_declined: cartão recusado pela operadora. A assinatura PIX permanece intacta e ativa (sem efeito colateral).
  • 400 invalid_state: assinatura fora de ativa/pausada, já no método pedido, já vinculada a uma recorrência no gateway, ou outra troca em andamento (tente de novo).
  • 403 invalid_state: o pagarme_card_id não pertence ao cliente da assinatura.
  • 404 not_found: assinatura não pertence à sua organização.
  • 409 blocked_chargeback_window: troca bloqueada por 24h após estorno/reembolso da assinatura (anti-fraude).
  • 409 invalid_state: cartão → PIX em estado que não permite a troca agora (outra alteração em andamento, mais de um mês recusado, cobrança em retentativa, assinatura sendo encerrada…); error.message diz qual.
  • 409 gateway_error: a troca agendada não pôde ser feita (o gateway cobrou na hora um ciclo já pago; a cobrança foi estornada e nada mudou). Tente de novo na data da próxima cobrança.
  • 502 gateway_error: falha temporária no gateway.
  • 503 gateway_error: no cartão → PIX, o gateway não respondeu. Nada mudou; repita em alguns minutos.
POST/api/v1/subscriptions/{id}/reactivateescopo: read

Reativar assinatura expirada/cancelada

Devolve a URL de checkout para reativar. A reativação real acontece quando o cliente completa um novo checkout: uma assinatura nova é criada vinculada ao mesmo cliente_id, preservando o histórico (padrão de mercado).

Exemplo curl
curl -X POST https://www.atmospay.com/api/v1/subscriptions/1c8b2a4d-.../reactivate \
  -H "Authorization: Bearer atmospay_live_xxx"
Resposta 200
{
  "object": "reactivation",
  "subscription_id": "1c8b2a4d-...",
  "checkout_url": "https://secure.atmospay.com/p/createfy-mensal",
  "oferta_slug": "createfy-mensal"
}
Comportamento
  • Sem efeito colateral: apenas devolve a URL de checkout
  • checkout_url é uma URL absoluta, <checkout>/p/<slug-da-oferta>, sem parâmetros. Para a nova assinatura nascer com o mesmo tenant, acrescente você ?external_id=<seu id> (e &org=<uuid>, se usar); o checkout lê esses parâmetros (card /p/{oferta-slug} acima)
  • Mostre essa URL como botão "Reativar" pro cliente concluir um novo pagamento
  • O novo pagamento gera uma nova assinatura (outro subscription_id) vinculada ao mesmo email/cliente_id; guarde o id novo como na seção 3
  • 409 invalid_state se a assinatura não está expirada nem cancelada (ativa, pausada, pendente…)
  • 422 offer_unavailable se a oferta não está mais disponível para nova compra
POST/api/v1/subscriptions/{id}/generate-invoiceescopo: charge

Gerar fatura PIX antecipada do próximo ciclo

Cria já a fatura PIX do próximo ciclo de uma assinatura ativa, antes da emissão automática (que acontece 10 dias antes de proxima_cobranca). Útil quando o parceiro quer cobrar o cliente mais cedo.

Quando usar

Assinatura mensal com proxima_cobranca em 2026-06-05. Hoje é 2026-05-12: a emissão automática só sairia em 2026-05-26. Você quer cobrar o cliente agora. Chama esse endpoint → a fatura é criada com referencia: "2026-06" (o mês que está sendo coberto) e vencimento em 05/06. Em seguida você chama POST /invoices/{id}/pix pra gerar o QR Code e cobrar.

Exemplo curl
curl -X POST https://www.atmospay.com/api/v1/subscriptions/1c8b2a4d-.../generate-invoice \
  -H "Authorization: Bearer atmospay_live_xxx" \
  -H "Idempotency-Key: $(uuidgen)"
Resposta 200
{
  "object": "invoice",
  "id": "abc...",
  "subscription_id": "1c8b2a4d-...",
  "order_id": "ORD-1778597430386-O5SV5T",
  "referencia": "2026-06",
  "vencimento": "2026-06-06T02:59:59.000Z",
  "ciclo": "2026-06-05T15:00:00.000Z",
  "status": "pendente",
  "valor": 3990,
  "forma_pagamento": "pix",
  "pix": null,
  "pago_em": null,
  "criado_em": "2026-05-12T15:00:00.000Z",
  "ja_existia": false
}

ja_existia: true se a fatura desse ciclo já existia antes da chamada; nos 10 dias anteriores a proxima_cobranca é o caso normal, porque a fatura automática já foi emitida. A chamada é idempotente: uma fatura viva (pendente ou paga) por ciclo.

vencimento: 2026-06-06T02:59:59.000Z é 05/06 às 23:59:59 no horário de Brasília: o vencimento é o fim do dia. Converta para America/Sao_Paulo antes de exibir a data; formatada em UTC ela mostra o dia seguinte.

Próximo passo: gerar o PIX

A resposta vem com pix: null. Pra gerar o QR Code de pagamento, chame POST /invoices/{id}/pix usando o id retornado.

Comportamento
  • referencia = ano-mês de proxima_cobranca da assinatura (formato YYYY-MM): representa o período que está sendo cobrado. ciclo = a própria proxima_cobranca.
  • vencimento = o dia de proxima_cobranca (fim do dia, horário de Brasília). Antecipar adianta a emissão, não o vencimento. Se a fatura for criada a menos de 7 dias do ciclo, ela vence 7 dias após a emissão, para o cliente ter prazo de pagamento.
  • Não avança proxima_cobranca. O ciclo avança no dia do ciclo, ou antes, quando a fatura é paga (webhook payment.approved), o que vier primeiro.
  • Idempotente em 2 níveis: Idempotency-Key (header, 24h) e uma fatura viva (pendente ou paga) por ciclo: chamar de novo devolve a mesma, com ja_existia: true. Fatura cancelada ou expirada não conta: a chamada emite outra.
  • A fatura não copia o metadata da assinatura (external_id vem null nela): ligue-a ao seu cliente pelo subscription_id.
  • 400 invalid_state se a assinatura não está ativa.
  • 400 generate_invoice_failed quando não há o que emitir: a emissão está pausada (5 competências vencidas sem pagamento; volta sozinha quando o cliente quita) ou os ciclos contratados na oferta já foram todos emitidos. O motivo vem em error.message.
  • 400 payment_method_not_supported se forma_pagamento ≠ pix (cartão/boleto são processados automaticamente pelo gateway). O corpo devolve forma_pagamento. Não confundir com unsupported_payment_method, que é forma_pagamento inválida no corpo das rotas de cobrança (seção 7).
GET/api/v1/subscriptions/{id}/invoicesescopo: read

Listar faturas de uma assinatura

Retorna lista paginada (cursor-based) de todas as faturas geradas por essa assinatura, pagas e pendentes. Mesma visibilidade de GET /subscriptions/{id}: vale também para coprodutor/afiliado da assinatura.

Query parameters
limit (default 20, max 100)
starting_after: UUID da última fatura recebida (cursor)
Exemplo curl
curl "https://www.atmospay.com/api/v1/subscriptions/1c8b2a4d-.../invoices?limit=10" \
  -H "Authorization: Bearer atmospay_live_xxx"
Resposta 200
{
  "object": "list",
  "data": [
    {
      "object": "invoice",
      "id": "abc...",
      "subscription_id": "1c8b2a4d-...",
      "order_id": "ORD-1777699429858-F6M4FG",
      "status": "aprovado",
      "valor": 3990,
      "forma_pagamento": "cartao",
      "vencimento": "2026-05-09T09:55:00.000Z",
      "ciclo": null,
      "pago_em": "2026-05-02T10:00:00.000Z",
      "criado_em": "2026-05-02T09:55:00.000Z"
    }
  ],
  "has_more": true,
  "next_starting_after": "abc..."
}
Paginação

Quando has_more: true, faça nova chamada passando starting_after=<next_starting_after> para pegar a próxima página.

A ordem é por emissão (criado_em). Como a fatura PIX sai 10 dias antes de vencer, a do topo pode ser de um ciclo que ainda não começou; para saber o que está atrasado, compare vencimento ou use cobranca.em_atraso da assinatura.

GET/api/v1/invoicesescopo: read

Listar faturas e cobranças (reconciliação)

Lista tudo o que a organização vendeu: faturas de assinatura, pedidos de /orders e cobranças de /charges saem todos aqui, porque são a mesma tabela. É o endpoint de reconciliação.

Filtros (query string)
external_ido ID que você mandou em metadata.external_id. Acha pedidos, cobranças e a 1ª fatura de uma assinatura. Faturas de renovação não carregam metadata e não casam aqui; liste-as por GET /subscriptions/{id}/invoices
statuspendente, aprovado, cancelado, expirado, estornado, estornado_parcial, reembolsado, em_analise, chargeback
compradora_idUUID da organização compradora (metadata.organizacao_compradora_id)
limit1 a 100, default 20
starting_afterid (UUID) da última fatura da página anterior. Fora do formato ou inexistente → 400 invalid_request
Use na recuperação de falha. Se um POST deu timeout ou retornou 502, consulte antes de recriar: evita cobrar o cliente duas vezes. Em /orders e /charges, use um external_id único por cobrança: é ele que você consulta aqui. Em /subscriptions ele é o id do tenant (se repete entre assinaturas): depois de um timeout ali, consulte GET /subscriptions?external_id= ou, no 502, o error.invoice.id ecoado.
Exemplo curl
curl "https://www.atmospay.com/api/v1/invoices?external_id=pedido-4471" \
  -H "Authorization: Bearer atmospay_live_xxx"
Resposta 200
{
  "object": "list",
  "data": [ { "object": "invoice", "id": "...", "status": "aprovado", "valor": 9990, "external_id": "pedido-4471", ... } ],
  "has_more": false,
  "next_starting_after": null
}

Paginação por cursor: enquanto has_more for true, repita passando starting_after com o valor de next_starting_after.

GET/api/v1/invoices/{id}escopo: read

Detalhe de uma fatura

Detalhe completo de qualquer venda da organização (fatura de assinatura, pedido ou cobrança). Inclui pix.qrcode e pix.copia_cola quando há PIX gerado. Id que não é UUID responde 404.

Exemplo curl
curl https://www.atmospay.com/api/v1/invoices/abc... \
  -H "Authorization: Bearer atmospay_live_xxx"
Resposta 200
{
  "object": "invoice",
  "id": "abc...",
  "subscription_id": "1c8b2a4d-...",
  "order_id": "ORD-...",
  "referencia": "2026-06",
  "vencimento": "2026-06-06T02:59:59.000Z",
  "ciclo": "2026-06-05T15:00:00.000Z",
  "status": "pendente",
  "valor": 3990,
  "forma_pagamento": "pix",
  "pix": {
    "qrcode": "https://gateway.example.com/...",
    "copia_cola": "00020126..."
  },
  "pago_em": null,
  "criado_em": "2026-05-26T11:00:00.000Z"
}
Datas da fatura
criado_emEmissão. Na renovação PIX a fatura é emitida 10 dias antes do vencimento (no exemplo, 26/05 para vencer em 05/06).
vencimentoFim do dia de vencimento no horário de Brasília (23:59:59 -03:00), em UTC: 2026-06-06T02:59:59.000Z é 05/06. Converta para America/Sao_Paulo antes de exibir. Pendente depois dele = atrasada (a partir do dia seguinte). Em renovação PIX é o dia do ciclo; se a emissão sair a menos de 7 dias do ciclo, vence 7 dias após a emissão. Faturas sem vencimento gravado (anteriores a esta regra, a primeira fatura da assinatura e as de cartão) seguem a regra antiga: criado_em + 7 dias, ou o início do mês da competência (referencia) se for depois. Null em vendas avulsas.
cicloA proxima_cobranca da assinatura que a fatura cobre. A competência começa nesse dia: cancelar a assinatura antes dele cancela a fatura; depois, ela continua devida e pagável. Null nas faturas sem ciclo gravado (anteriores a esta regra, primeira fatura, cartão) e em vendas avulsas.
referenciaCompetência YYYY-MM: o mês do ciclo. Como a fatura PIX sai 10 dias antes, pode ser o mês seguinte ao de criado_em.
pix.expira_emValidade do QR Code (1 hora); não é o vencimento da fatura. Vem preenchido na resposta que gerou o QR (POST /orders, /charges, /subscriptions e POST /invoices/{id}/pix); nesta leitura vem null.
POST/api/v1/invoices/{id}/pixescopo: charge

Gerar PIX para uma fatura pendente

Cria (ou regenera) o QR Code PIX. Use quando o cliente quer pagar uma renovação. O QR Code vale 1 hora (não confundir com o vencimento da fatura): chame este endpoint sob demanda quando o cliente abrir a tela de pagamento.

Headers opcionais
Idempotency-Key: evita gerar PIX duplicado em retries
Pré-requisito

A fatura precisa estar com status: "pendente" e aceitar PIX (não ser uma fatura de cartão nem ter sido substituída por outra cobrança), e o cliente precisa ter telefone cadastrado (o PIX é recusado sem ele). Senão retorna 400.

Exemplo curl
curl -X POST https://www.atmospay.com/api/v1/invoices/abc.../pix \
  -H "Authorization: Bearer atmospay_live_xxx" \
  -H "Idempotency-Key: 7b9c1f4a-..."
Resposta 200
{
  "object": "invoice",
  "id": "abc...",
  "status": "pendente",
  "pix": {
    "qrcode": "https://gateway.example.com/....png",
    "copia_cola": "00020126570014BR.GOV.BCB.PIX...",
    "expira_em": "2026-05-26T12:00:00.000Z"
  },
  "gateway_transaction_id": "ch_xxx",
  ...
}

pix.expira_em é a validade deste QR Code (1 hora a partir da geração). Guarde se for mostrar contagem regressiva: GET /invoices/{id} devolve null nesse campo. A resposta traz também o metadata e o external_id da própria fatura, como GET /invoices/{id}: na 1ª fatura, o seu external_id; numa renovação, null (ligue pelo subscription_id). Id que não é UUID responde 404.

Resposta 400 (fatura não pendente)
{
  "error": {
    "type": "invalid_state",
    "message": "Fatura não pode receber PIX (status atual: aprovado)",
    "current_status": "aprovado"
  }
}
  • 400 missing_customer_data: a fatura não aceita PIX (é de cartão, ou foi substituída por outra cobrança ou encerrada em definitivo) ou o cliente não tem telefone cadastrado; a mensagem diz qual
  • 500 gateway_unavailable: gateway de pagamento não configurado na plataforma
  • 502 gateway_error: o gateway não gerou o PIX; tente de novo em instantes (o erro não fica no cache, a mesma chave serve)
Como exibir para o cliente
  • O campo pix.qrcode é uma URL HTTPS da imagem PNG: use direto em <img src={qrcode} />
  • O campo pix.copia_cola é o BR Code para botão "copiar e colar" no app do banco
  • Avise o cliente que o QR Code vale 1 hora; depois disso, gere outro chamando este endpoint de novo. A fatura continua pagável enquanto estiver pendente, inclusive depois do vencimento. Não esconda o botão de pagar da fatura vencida: é ela que tira a assinatura do atraso
POST/api/v1/ordersescopo: charge

Criar pedido avulso (PIX ou cartão)

Vende um produto do seu catálogo pela API: devolve o QR Code PIX pronto para exibir, ou cobra no cartão na hora. É o equivalente programático do checkout /p/: mesma venda, mesmos splits, mesmos webhooks.

Exige API key com escopo charge. O preço vem sempre da oferta no banco: não existe campo de valor no corpo.
Identificar o que será vendido

No AtmosPay o preço mora na oferta, não no produto, e um produto pode ter várias. Envie oferta_id para cobrar uma oferta específica, ou apenas produto_id para usar a oferta padrão (a marcada como default; sem default, a mais barata ativa). A resposta sempre informa qual oferta_id foi cobrada.

Se o seu catálogo tem produtos com múltiplas ofertas, prefira oferta_id: assim o preço não muda sozinho quando alguém criar uma oferta promocional no painel.

Headers
Idempotency-Key: fortemente recomendado no PIX e obrigatório no cartão. Sem ela, um retry após timeout de rede cria um segundo pedido e um segundo QR Code, e o cliente pode pagar os dois; no cartão, ele é simplesmente cobrado duas vezes.
Corpo
{
  "produto_id": "7b1f2c4e-...",
  "forma_pagamento": "pix",
  "cliente": {
    "nome": "Maria Silva",
    "email": "maria@exemplo.com",
    "telefone": "11988887777",
    "cpf": "123.456.789-09"
  },
  "cupom_codigo": "PROMO10",
  "metadata": { "external_id": "pedido-4471" }
}
  • forma_pagamento ausente vale "pix", e card_token/cartao enviados sem ele são ignorados; envie sempre
  • cliente.email identifica o cliente: vincula ao usuário existente ou cria um novo
  • cliente.telefone é obrigatório: o PIX é recusado sem o telefone do pagador
  • cliente.cpf é validado (dígitos verificadores) e aceita CPF ou CNPJ
  • metadata.external_id: o ID do pedido no seu sistema. Volta no webhook e serve de filtro em GET /invoices. Sempre envie
  • cupom_codigo é revalidado no servidor; cupom inválido recusa o pedido com 400 em vez de ignorar o desconto
Afiliado e origem da venda (UTM)

A venda criada pela API pode pagar comissão a um afiliado do AtmosPay e guardar de onde o comprador veio, como uma venda do checkout /p/. São dois campos opcionais no corpo:

{
  "oferta_id": "9c4e7a15-...",
  "forma_pagamento": "pix",
  "cliente": { "nome": "Maria Silva", "email": "maria@exemplo.com", "telefone": "11988887777", "cpf": "123.456.789-09" },
  "afiliado": "k7m2p9q4rt",
  "tracking": {
    "utm_source": "instagram",
    "utm_medium": "stories",
    "utm_campaign": "lancamento",
    "src": "bio",
    "sck": "story-3",
    "fbclid": "IwAR..."
  },
  "metadata": { "external_id": "pedido-4473" }
}
  • afiliado é o código do afiliado, o mesmo do link dele (/p/{oferta}/{codigo}), sem diferença de maiúsculas. A comissão segue as mesmas regras do checkout: o percentual do programa do produto (ou a comissão personalizada daquele afiliado), sobre o valor depois do cupom e da taxa da plataforma. Ela é paga quando a venda é aprovada e desfeita no estorno, como qualquer comissão
  • Código fora do formato (6 a 16 letras e números) responde 422 afiliado_invalido, antes de qualquer cobrança
  • Código no formato mas que não vale não dá erro: a venda sai sem afiliado, como no checkout, e a resposta diz por quê em afiliado.motivo: nao_encontrado (não é afiliado deste produto), nao_aprovado (afiliação pendente, recusada, bloqueada ou pausada), programa_desligado, produto_indisponivel ou nao_elegivel (o afiliado não pode receber comissão nesta venda, por exemplo por ser da sua organização ou coprodutor do produto). O motivo exato de nao_elegivel não sai pela API, para não expor dados do afiliado
  • tracking aceita utm_source, utm_medium, utm_campaign, utm_content, utm_term, src e sck (os da Utmify), fbclid, gclid, ttclid, msclkid e ref, cada um um texto de até 250 caracteres. Chave fora da lista ou valor que não é texto é descartado sem erro. A origem fica em metadata.tracking, onde o checkout guarda a da URL, e chega à Utmify, ao Pixel do Meta (pela API de conversões), aos webhooks e aos relatórios
  • Também vale mandar a origem como metadata.tracking. Se vierem os dois, vale o tracking do primeiro nível inteiro (as duas fontes não se misturam). Ele não conta no limite de 10 chaves do metadata
  • Mudança: um metadata.tracking em texto ou número, que antes ficava guardado como valor solto do metadata, agora é descartado. Só o objeto vale
  • Não confunda com metadata.external_affiliate_id: aquele é o afiliado de um sistema externo, que o AtmosPay só guarda e devolve, sem pagar comissão. Os dois podem ir juntos

A resposta traz o afiliado assim (e null quando não veio código):

"afiliado": {
  "object": "affiliate_reference",
  "codigo": "k7m2p9q4rt",
  "aplicado": true,
  "motivo": null,
  "percentual": 40,
  "comissao": 3684
}

comissao vem em centavos (3684 = R$ 36,84). O mesmo objeto volta em GET /orders/{id}, em GET /sales (para você e para o próprio afiliado) e em GET /subscriptions. Ele não traz nome, e-mail nem documento do afiliado.

Exemplo curl
curl -X POST https://www.atmospay.com/api/v1/orders \
  -H "Authorization: Bearer atmospay_live_xxx" \
  -H "Idempotency-Key: 7b9c1f4a-..." \
  -H "Content-Type: application/json" \
  -d '{
    "produto_id": "7b1f2c4e-...",
    "forma_pagamento": "pix",
    "cliente": {
      "nome": "Maria Silva",
      "email": "maria@exemplo.com",
      "telefone": "11988887777",
      "cpf": "12345678909"
    },
    "metadata": { "external_id": "pedido-4471" }
  }'
Resposta 201
{
  "object": "order",
  "id": "3f9c1a20-...",
  "order_id": "ORD-...",
  "status": "pendente",
  "valor": 9990,
  "produto_id": "7b1f2c4e-...",
  "oferta_id": "9a3d5f81-...",
  "forma_pagamento": "pix",
  "gateway_transaction_id": "ch_xxx",
  "cliente": {
    "object": "customer_reference",
    "id": "c1d2e3f4-...",
    "nome": "Maria Silva",
    "email": "maria@exemplo.com"
  },
  "pix": {
    "qrcode": "https://gateway.example.com/....png",
    "copia_cola": "00020126580014br.gov.bcb.pix...",
    "expira_em": "2026-08-03T16:00:00.000Z"
  },
  "pago_em": null,
  "criado_em": "2026-08-03T15:00:00.000Z",
  "tracking": null,
  "external_id": "pedido-4471",
  "afiliado": null,
  "redirect_url": "https://site.com/obrigado?order_id=ORD-..."
}
Guarde pix.expira_em agora. Ele só vem nesta resposta: o GET /orders/{id} devolve null, porque a validade do QR Code não é persistida.

redirect_url é a página de obrigado cadastrada no produto, já com ?order_id=. É a mesma URL para onde o checkout do AtmosPay levaria o comprador. Vem null quando o produto não tem redirect. Ela também só existe neste 201 (o GET /orders/{id} não devolve), então guarde junto, ou leia produto.redirect_url no webhook payment.approved. Em PIX, redirecione só depois da aprovação.

Como exibir para o cliente
  • pix.qrcode é uma URL de imagem: use direto em <img src={qrcode} />
  • pix.copia_cola é o BR Code para o botão "copiar" (caminho mais confiável no mobile)
  • O PIX expira em 1 hora
  • valor vem em centavos (9990 = R$ 99,90)
Pagamento com cartão de crédito

Envie "forma_pagamento": "cartao". Diferente do PIX, a cobrança é síncrona: o 201 já vem com status: "aprovado" (ou em_analise, quando o antifraude segura a transação; raramente pendente, se a autorização ainda estiver processando no gateway, e aí o desfecho chega pelo webhook) e a recusa do emissor chega como 402. Não há QR Code nem espera: pix volta null.

Idempotency-Key é obrigatório em cartão. Sem o header, a requisição é recusada com 400. No PIX um retry só criava um QR Code a mais, que ninguém era obrigado a pagar; no cartão ele cobra o cliente de novo, pra valer. Gere um UUID por tentativa de compra e reuse o mesmo em todos os retries dela: trocar de chave a cada retry anula a proteção.
Caminho 1: card_token (recomendado)

Seu front tokeniza o cartão direto no endpoint de tokenização (veja GET /api/v1/config) e você manda só o token. PAN e CVV não passam pelo seu servidor nem pelo nosso: os dois lados ficam em PCI SAQ-A. O token é single-use e expira em ~60s: gere no submit e cobre na mesma ação.

{
  "produto_id": "7b1f2c4e-...",
  "forma_pagamento": "cartao",
  "card_token": "token_xxx",
  "parcelas": 3,
  "cliente": {
    "nome": "Maria Silva",
    "email": "maria@exemplo.com",
    "cpf": "123.456.789-09",
    "endereco": {
      "cep": "01310100",
      "rua": "Av. Paulista",
      "numero": "1000",
      "bairro": "Bela Vista",
      "cidade": "São Paulo",
      "estado": "SP"
    }
  },
  "metadata": { "external_id": "pedido-4471" }
}
Caminho 2: dados do cartão (server-to-server)

Mais simples de integrar, mas o PAN transita pelo seu servidor e pelo nosso (escopo PCI SAQ A-EP). É o mesmo formato que POST /subscriptions/{id}/card já aceita. O cartão nunca é persistido: segue em memória, só na requisição ao gateway. Use este caminho apenas se a sua aplicação já opera nesse escopo.

{
  "produto_id": "7b1f2c4e-...",
  "forma_pagamento": "cartao",
  "cartao": {
    "numero": "4111111111111111",
    "titular": "MARIA SILVA",
    "validade": "12/29",
    "cvv": "123"
  },
  "parcelas": 1,
  "cliente": { "nome": "Maria Silva", "email": "maria@exemplo.com", "cpf": "12345678909" }
}
Regras dos campos de cartão
  • card_token tem precedência: mandando os dois, o objeto cartao é ignorado
  • cartao.numero: 13 a 19 dígitos, com ou sem máscara. O dígito verificador (Luhn) é conferido aqui de propósito: um número digitado errado volta 400 dizendo o quê, em vez de virar um 502 genérico do gateway
  • cartao.validade: MM/AA ou MM/YYYY; cartão vencido é recusado
  • cartao.cvv: 3 ou 4 dígitos
  • parcelas: inteiro de 1 a 12; ausente = 1 (à vista). Fora do cartão o campo é ignorado
  • cliente.telefone deixa de ser obrigatório no cartão (segue obrigatório no PIX, que é recusado sem o telefone do pagador). Continua recomendado: entra na análise antifraude
  • cliente.endereco é opcional e vira o billing_address. Sem ele o gateway usa um endereço genérico, que aprova mas pontua pior no antifraude. Campos aceitos: cep, rua, numero, complemento, bairro, cidade, estado; e estado precisa resolver numa UF (sigla ou nome por extenso), senão o endereço inteiro é descartado e cai no genérico
  • pagarme_card_id (cartão salvo) não é aceito e retorna 400. É decisão de segurança, não falta de suporte: cobrar um cartão salvo exige prova de posse (OTP) do titular, e uma API key só prova qual é a organização vendedora, nada sobre quem é o dono do cartão
Parcelamento: quem paga os juros

Acima de 1x, os juros do plano da sua organização entram na cobrança e quem paga é o cliente (mesma regra do checkout /p/). Por isso a resposta separa os valores:

  • valor: o preço do produto, o número que bate com a oferta e com o catálogo
  • juros: os juros de parcelamento, em centavos (0 à vista)
  • valor_total: valor + juros, o que o cliente paga de fato. É este o número da conciliação
  • cartao.valor_parcela: valor_total / parcelas arredondado, só para exibir ("3x de R$ 35,80"). O residual de centavos quem distribui é o emissor, então não concilie por ele

No cartão o preço canônico sai de preco_cartao da oferta, com fallback para preco (no PIX é preco_pix): o valor pode legitimamente diferir entre os dois métodos.

Exemplo curl (cartão em 3x)
curl -X POST https://www.atmospay.com/api/v1/orders \
  -H "Authorization: Bearer atmospay_live_xxx" \
  -H "Idempotency-Key: 7b9c1f4a-..." \
  -H "Content-Type: application/json" \
  -d '{
    "produto_id": "7b1f2c4e-...",
    "forma_pagamento": "cartao",
    "card_token": "token_xxx",
    "parcelas": 3,
    "cliente": {
      "nome": "Maria Silva",
      "email": "maria@exemplo.com",
      "cpf": "12345678909"
    },
    "metadata": { "external_id": "pedido-4471" }
  }'
Resposta 201 (cartão aprovado)
{
  "object": "order",
  "id": "3f9c1a20-...",
  "order_id": "ORD-...",
  "status": "aprovado",
  "valor": 9990,
  "parcelas": 3,
  "valor_total": 10740,
  "juros": 750,
  "cartao": {
    "object": "card",
    "bandeira": "visa",
    "ultimos_digitos": "0010",
    "parcelas": 3,
    "valor_parcela": 3580
  },
  "produto_id": "7b1f2c4e-...",
  "oferta_id": "9a3d5f81-...",
  "forma_pagamento": "cartao",
  "gateway_transaction_id": "ch_xxx",
  "pix": null,
  "pago_em": "2026-08-03T15:00:00.000Z",
  "criado_em": "2026-08-03T15:00:00.000Z",
  "external_id": "pedido-4471",
  "redirect_url": "https://site.com/obrigado?order_id=ORD-..."
}

parcelas, valor_total, juros e cartao são campos aditivos: aparecem também no PIX (com parcelas: 1, juros: 0, cartao: null) e nos objetos charge e invoice. cartao pode vir null mesmo numa venda de cartão antiga, quando a bandeira e os últimos dígitos não foram gravados; quem diz o método é forma_pagamento.

em_analise ainda não é aprovação. Quando o antifraude segura a transação, o 201 volta com esse status e o desfecho chega depois pelo webhook. Libere o produto só em aprovado.
Resposta 402 (cartão recusado)
{
  "error": {
    "type": "card_declined",
    "message": "Saldo ou limite insuficiente.",
    "code": "iso:51",
    "campo": "geral",
    "classificacao": "reversivel",
    "order": { "id": "3f9c1a20-...", "status": "cancelado" }
  }
}
  • 402 é recusa, não falha de infraestrutura. O gateway respondeu normalmente; quem negou foi o emissor ou o antifraude. Repetir a mesma chamada seria recusado de novo: o caminho é outro cartão ou outro método
  • error.code e error.campo vêm traduzidos do gateway e dizem por que recusou (iso:51, reason:antifraud, kw:cvv…; campo numeroCartao, cvv, validade, nomeCartao, cpf ou geral). Use-os para apontar o input certo pro cliente
  • error.classificacao é a classificação ABECS do código, pela bandeira: reversivel, irreversivel ou sem_classificacao. Em irreversivel, não tente de novo com o mesmo cartão: peça outro cartão ou ofereça PIX
  • error.order.id é a venda que já tinha sido criada e ficou em cancelado; guarde para reconciliação
  • O 502 gateway_error continua existindo, só para falha de infraestrutura. No cartão, se ele trouxer error.order, a resposta fica gravada na chave por 24h: repetir com a mesma Idempotency-Key devolve o mesmo 502. Consulte o pedido antes e, se precisar cobrar de novo, use uma chave nova. Sem error.order, a chave responde 409 por até 2 min e depois libera: aí repita com a mesma chave (detalhes abaixo e na seção 5)
Rate limit próprio do cartão

Além do limite geral de 60 req/min, cobranças com cartão têm um limite de 10 por minuto por API key (proteção anti card-testing). Estourou, você recebe 429 com Retry-After.

Erros de regra de negócio (400)
  • Oferta recorrente: use POST /api/v1/subscriptions; esta rota só faz venda única
  • Tipo de produto não aceito: só digital e fisico. Evento e e-commerce dependem de dados extras (ingressos, itens de carrinho) que esta rota não cria
  • Produto sem oferta ativa: inclusive um produto_id que não existe: a resposta é 400 "Produto não possui oferta ativa", não 404
  • Produto fora do status ativo: 400 invalid_state, com current_status

Produto ou oferta de outra organização, e oferta_id inexistente, retornam 404 (não 403), para não confirmar a existência de recursos alheios. Envie produto_id e oferta_id como UUID: outro formato não é validado antes da consulta e pode voltar 500 internal_error.

Resposta 502 (falha no gateway)
{
  "error": {
    "type": "gateway_error",
    "message": "Falha ao gerar a cobrança PIX",
    "order": { "id": "3f9c1a20-...", "status": "cancelado" }
  }
}

Um 502 pode vir depois de a venda já ter sido registrada. Não trate como "nada aconteceu": registre error.order.id quando presente e consulte GET /api/v1/invoices?external_id=... (ou GET /orders/{id}) antes de recriar o pedido, para não duplicar cobrança. O que acontece ao repetir depende do método:

  • Cartão com error.order: o 502 fica gravado na Idempotency-Key por 24h. Não dá para saber se o cartão foi debitado antes da falha, então a API prefere obrigar uma chave nova a arriscar cobrança dupla. Se o pedido consultado estiver aprovado, a cobrança passou; senão, tente de novo com uma chave nova
  • Cartão sem error.order: a reserva segura a chave: ela responde 409 idempotency_key_in_use por até 2 min e depois libera; repita com a mesma chave
  • PIX: o 502 não é cacheado; pode repetir com a mesma chave
GET/api/v1/orders/{id}escopo: read

Consultar um pedido

Acompanha a confirmação do pagamento. No cartão o resultado já vem no POST; no PIX, prefira o webhook payment.approved. Polling aqui é o plano B.

O {id} aceita o UUID (id) ou o order_id legível (ORD-…). A rota devolve qualquer venda da sua organização no formato order, inclusive cobranças de /charges e faturas de assinatura.

Exemplo curl
curl https://www.atmospay.com/api/v1/orders/3f9c1a20-... \
  -H "Authorization: Bearer atmospay_live_xxx"

# ou pelo order_id legível (o mesmo da URL de redirecionamento e dos webhooks)
curl https://www.atmospay.com/api/v1/orders/ORD-1789516000000-AB12CD \
  -H "Authorization: Bearer atmospay_live_xxx"
Página de obrigado (redirecionamento após o pagamento)

No produto (aba Geral → "URL de Redirecionamento Após Pagamento") você cadastra uma URL sua. Quando o pagamento é aprovado (cartão na hora, PIX assim que o pagamento é confirmado na tela do QR Code), o checkout manda o cliente para ela com o pedido na query string:

https://seusite.com/obrigado?order_id=ORD-1789516000000-AB12CD
# URL cadastrada que já tem query string: o parâmetro é anexado com &
https://seusite.com/obrigado?utm_source=x&order_id=ORD-1789516000000-AB12CD
O redirecionamento não é assinado: qualquer pessoa pode abrir a sua URL com um order_id qualquer. Use-o só para saber qual pedido mostrar e confirme status: "aprovado" por GET /orders/ORD-… (ou pelo webhook payment.approved) antes de liberar qualquer coisa. Boleto não redireciona (o pagamento chega dias depois; só o webhook avisa).
Status possíveis
pendenteaguardando o pagamento do PIX (no cartão o resultado costuma ser imediato; pendente ali é autorização ainda processando no gateway)
aprovadopago e confirmado: libere o produto
expiradoo PIX venceu sem pagamento
canceladocobrança recusada ou cancelada
em_analisecartão retido pela análise antifraude; ainda NÃO é aprovação
estornadovalor devolvido ao cliente
estornado_parcialparte do valor devolvida ao cliente
reembolsadovalor reembolsado ao cliente
chargebackcontestação junto ao emissor
Libere o produto apenas em aprovado. E trate estornado, reembolsado e chargeback: eles chegam depois de um aprovado e devem revogar o acesso já concedido.

Fazendo polling: use intervalo crescente (ex.: 5s nos primeiros 2min, depois 30s), pare em status terminal ou quando o vencimento passar, e respeite o limite de 60 req/min.

POST/api/v1/chargesescopo: charge

Criar cobrança de valor livre (PIX ou cartão)

Cobra um valor arbitrário, sem produto do catálogo: o equivalente via API do link de pagamento. Você define o valor e a descrição, e cobra por PIX ou cartão.

Exige API key com escopo charge. A taxa da plataforma é a normal do plano da organização: esta rota não tem isenção nem tratamento especial de split.
Quando usar esta rota e não /orders
POST /orders

Venda de um produto do catálogo. O preço vem da oferta no banco e o payload não tem campo de valor.

POST /charges

Cobrança avulsa sem produto. O valor vem de você, porque não há catálogo por trás. A venda nasce sem produto_id, com tipo_checkout: link_pagamento, sem afiliado e sem coprodutor.

Corpo
{
  "valor": 15000,
  "descricao": "Consultoria (2h)",
  "forma_pagamento": "pix",
  "cliente": {
    "nome": "Maria Silva",
    "email": "maria@exemplo.com",
    "telefone": "11988887777",
    "cpf": "123.456.789-09"
  },
  "metadata": { "external_id": "os-8821" }
}
  • valor em centavos, inteiro (15000 = R$ 150,00). Valor fracionário é recusado de propósito: quase sempre significa que reais foram enviados por engano, e arredondar cobraria o valor errado
  • Mínimo R$ 5,00 (500 centavos), piso da plataforma, o mesmo de produtos e links. Máximo 100.000.000 centavos (R$ 1.000.000,00), teto de sanidade contra reais enviados como centavos. Fora dos limites, 400
  • descricao é obrigatória, com até 200 caracteres: sem produto por trás, é o que identifica a cobrança para o cliente e no extrato. Uma chave descricao dentro de metadata é ignorada: vale o campo de primeiro nível
  • forma_pagamento ausente vale "pix"; envie sempre, como no exemplo
  • Campos de cliente seguem a mesma regra de POST /orders
  • metadata.external_id: o ID da cobrança no seu sistema. Vale ainda mais aqui do que em /orders: sem produto por trás, é o único elo entre a cobrança no AtmosPay e o registro do seu lado. Sempre envie
  • tracking (opcional): as UTMs e os click IDs da compra, com as mesmas regras de POST /orders. Ficam na venda como os da URL do checkout e chegam à Utmify, ao Pixel do Meta e aos webhooks
  • afiliado não é aceito aqui: responde 422 afiliado_nao_suportado. A comissão de afiliado é do produto, e esta cobrança não tem produto. Para pagar comissão, venda o produto por POST /orders
Exemplo curl
curl -X POST https://www.atmospay.com/api/v1/charges \
  -H "Authorization: Bearer atmospay_live_xxx" \
  -H "Idempotency-Key: 7b9c1f4a-..." \
  -H "Content-Type: application/json" \
  -d '{
    "valor": 15000,
    "descricao": "Consultoria (2h)",
    "forma_pagamento": "pix",
    "cliente": {
      "nome": "Maria Silva",
      "email": "maria@exemplo.com",
      "telefone": "11988887777",
      "cpf": "12345678909"
    },
    "metadata": { "external_id": "os-8821" }
  }'
Resposta 201
{
  "object": "charge",
  "id": "8d2e7b10-...",
  "order_id": "ORD-...",
  "status": "pendente",
  "valor": 15000,
  "descricao": "Consultoria (2h)",
  "forma_pagamento": "pix",
  "gateway_transaction_id": "ch_xxx",
  "cliente": { "object": "customer_reference", "id": "...", "nome": "Maria Silva", "email": "maria@exemplo.com" },
  "pix": {
    "qrcode": "https://gateway.example.com/....png",
    "copia_cola": "00020126580014br.gov.bcb.pix...",
    "expira_em": "2026-08-03T16:00:00.000Z"
  },
  "pago_em": null,
  "criado_em": "2026-08-03T15:00:00.000Z",
  "external_id": "os-8821",
  "redirect_url": null
}

Ciclo de vida, webhooks, idempotência e exibição do PIX são idênticos aos de POST /orders, inclusive guardar pix.expira_em, que não volta em consultas. redirect_url vem aqui só para você ler o mesmo campo nas duas rotas: é sempre null, porque o redirect é configuração do produto e esta cobrança não tem produto.

Pagamento com cartão de crédito

Envie "forma_pagamento": "cartao" com card_token (recomendado) ou o objeto cartao. Valem exatamente as mesmas regras de POST /api/v1/orders, que estão detalhadas lá para não repetir aqui: cobrança síncrona (o 201 já vem aprovado ou em_analise; raramente pendente), Idempotency-Key obrigatório, parcelas de 1 a 12, cliente.telefone dispensado, cliente.endereco opcional como billing_address, recusa em 402 card_declined, pagarme_card_id barrado em 400 e limite extra de 10 cobranças com cartão por minuto.

A diferença é a de sempre entre as duas rotas: aqui o valor é seu, não da oferta. Os juros de parcelamento entram por cima dele: valor_total = valor + juros é o que o cliente paga, e o mínimo de R$ 5,00 continua valendo sobre valor.

Corpo (cartão em 3x)
{
  "valor": 15000,
  "descricao": "Consultoria (2h)",
  "forma_pagamento": "cartao",
  "card_token": "token_xxx",
  "parcelas": 3,
  "cliente": {
    "nome": "Maria Silva",
    "email": "maria@exemplo.com",
    "cpf": "123.456.789-09",
    "endereco": {
      "cep": "01310100",
      "rua": "Av. Paulista",
      "numero": "1000",
      "bairro": "Bela Vista",
      "cidade": "São Paulo",
      "estado": "SP"
    }
  },
  "metadata": { "external_id": "os-8821" }
}
Resposta 201 (cartão aprovado)
{
  "object": "charge",
  "id": "8d2e7b10-...",
  "order_id": "ORD-...",
  "status": "aprovado",
  "valor": 15000,
  "descricao": "Consultoria (2h)",
  "forma_pagamento": "cartao",
  "parcelas": 3,
  "valor_total": 16125,
  "juros": 1125,
  "cartao": {
    "object": "card",
    "bandeira": "visa",
    "ultimos_digitos": "0010",
    "parcelas": 3,
    "valor_parcela": 5375
  },
  "gateway_transaction_id": "ch_xxx",
  "cliente": { "object": "customer_reference", "id": "...", "nome": "Maria Silva", "email": "maria@exemplo.com" },
  "pix": null,
  "pago_em": "2026-08-03T15:00:00.000Z",
  "criado_em": "2026-08-03T15:00:00.000Z",
  "external_id": "os-8821",
  "redirect_url": null
}
Resposta 402 (cartão recusado)
{
  "error": {
    "type": "card_declined",
    "message": "Pagamento reprovado pela análise antifraude.",
    "code": "reason:antifraud",
    "campo": "geral",
    "charge": { "id": "8d2e7b10-...", "status": "cancelado" }
  }
}

Aqui o id ecoado vem em error.charge (em /orders é error.order): é a cobrança que já tinha sido criada e ficou em cancelado. Guarde-o e não recrie a cobrança às cegas: consulte por external_id em GET /api/v1/invoices antes.

O 502 gateway_error segue as mesmas regras de /orders, com o id em error.charge: no cartão, com esse id a resposta fica gravada na chave por 24h (nova tentativa exige chave nova); sem ele, 409 por até 2 min; no PIX, pode repetir com a mesma chave.

GET/api/v1/charges/{id}escopo: read

Consultar uma cobrança

Mesmos status e mesmas regras de acompanhamento de GET /orders/{id}.

curl https://www.atmospay.com/api/v1/charges/8d2e7b10-... \
  -H "Authorization: Bearer atmospay_live_xxx"

O {id} aceita o UUID (id) ou o order_id legível (ORD-…) da cobrança, o mesmo da URL de redirecionamento e dos webhooks. Sem correspondência na sua organização, 404 not_found.

Libere a entrega apenas em aprovado. O webhook payment.approved continua sendo o caminho recomendado.

GET/api/v1/salesescopo: read

Listar vendas (relatório)

Lista TODAS as vendas em que a sua organização participa (vendendo, coproduzindo ou afiliando), vindas de qualquer canal (checkout, loja, evento, assinatura ou a própria API). Cada item traz o seu ganho, o produto, o cliente e as UTMs.

Query parameters
papeltodos (padrão) | vendedor | afiliado | coprodutor
statuspendente | aprovado | cancelado | expirado | processando | estornado | estornado_parcial | reembolsado | em_analise | chargeback | recusado
tipounica | recorrente | invoice (invoice = cobrança de um ciclo de assinatura)
tipo_produtodigital | fisico | evento | ecommerce | doacao | link
produto_idUUID do produto (GET /products). Casa com o produto principal, itens do carrinho e order bumps
forma_pagamentopix | cartao | boleto
cliente_emaile-mail do comprador (só nas vendas em que você é o vendedor)
criado_apos / criado_antesISO 8601 (ex.: 2026-08-01). Só a data é o dia de Brasília inteiro; data e hora sem fuso é horário de Brasília
utm_source / utm_campaignorigem da venda
compradora_id / external_idos mesmos filtros de metadata das outras listagens
searchorder_id ou nome do cliente; nas vendas em que você é o vendedor, também gateway_transaction_id e e-mail do cliente
limit / starting_after1 a 100 (padrão 20) · id da última venda da página anterior
Exemplo curl
curl "https://www.atmospay.com/api/v1/sales?papel=afiliado&status=aprovado&criado_apos=2026-08-01" \
  -H "Authorization: Bearer atmospay_live_xxx"
Resposta 200 (resumida)
{
  "object": "list",
  "data": [
    {
      "object": "sale",
      "id": "3f9c1a20-...",
      "order_id": "ORD-...",
      "tipo": "unica",
      "status": "aprovado",
      "forma_pagamento": "pix",
      "papel": "afiliado",
      "valor": 9990,
      "valor_liquido": null,
      "ganho": { "object": "earning", "valor": 2997, "percentual": 30, "papel": "afiliado" },
      "produto": { "object": "product_reference", "id": "7b1f2c4e-...", "nome": "Curso X", ... },
      "cliente": { "object": "customer_reference", "nome": "Maria Silva", "telefone": "(11) ****-7777", "documento": null, ... },
      "tracking": { "utm_source": "instagram", "utm_campaign": "lancamento", ... },
      "pago_em": "2026-08-03T15:32:08.000Z",
      ...
    }
  ],
  "has_more": true,
  "next_starting_after": "3f9c1a20-..."
}
  • Valores em centavos. valor_liquido só vem preenchido quando papel = vendedor
  • Todo valor é em reais e moeda é sempre BRL. Venda cobrada em dólar traz também moeda_original (USD), valor_original (o total em centavos de dólar) e cotacao; na venda em reais os três vêm null
  • Fora de papel = vendedor, o cliente vem com os dados reduzidos (LGPD): o coprodutor recebe o telefone mascarado; o afiliado, o e-mail mascarado e nenhum telefone, salvo com "Liberar dados do comprador" ligado no produto. Nenhum dos dois recebe o documento nem o id do cliente, e o metadata vem sem o endereço, a marca do estorno e o pagador do PIX
  • tracking é null quando a venda não teve UTM. Na venda criada pela API, é a origem que o integrador mandou em tracking na criação
  • afiliado traz o código do afiliado do AtmosPay, se ele foi aplicado, o percentual e a comissão em centavos. Vem para o vendedor e para o próprio afiliado; para o coprodutor vem null
GET/api/v1/sales/{id}escopo: read

Detalhe de uma venda

Todos os campos de GET /sales mais splits (quem dividiu e quanto), itens do carrinho, order bumps, ingressos de evento, cupom e cartão usado.

curl https://www.atmospay.com/api/v1/sales/ORD-1789516000000-AB12CD \
  -H "Authorization: Bearer atmospay_live_xxx"
  • Aceita o id (UUID) ou o order_id legível (ORD-…)
  • Mesma visibilidade da lista: a venda precisa ser da sua organização ou ter split seu; fora disso, 404
  • Afiliado e coprodutor recebem o cliente com os dados reduzidos, como na lista, sem documento do cliente e sem e-mail dos outros participantes do split

5. Idempotência

Endpoints POST aceitam o header Idempotency-Key. Quando presente, a resposta da primeira chamada é cacheada por 24h. Tentativas seguintes com a mesma chave retornam o mesmo resultado sem executar a operação de novo.

A chave vale por API key, não por rota nem por corpo. Reusar a mesma chave em outra rota, ou com outro corpo, devolve a resposta gravada da primeira chamada, sem aviso. Use uma chave nova por operação.
Quando usar: sempre que sua aplicação fizer retry automático de uma chamada que mudou estado (cancel, gerar PIX). Sem a chave, um retry pode duplicar a operação.
Em cartão o header deixa de ser opcional. POST /orders, POST /charges e POST /subscriptions respondem 400 quando forma_pagamento: "cartao" chega sem Idempotency-Key. No PIX um retry só gerava um QR Code a mais, que ninguém era obrigado a pagar; no cartão ele cobra o cliente de novo. Gere um UUID por tentativa de compra e reuse o mesmo em todos os retries dela: uma chave nova a cada retry anula a proteção.

Cobrança em andamento: 409

Em cartão a chave é reservada antes de a cobrança sair. Uma segunda requisição com a mesma chave enquanto a primeira ainda está em voo (o caso clássico do integrador que dá timeout em 10s numa cobrança que leva 12) recebe 409 idempotency_key_in_use em vez de cobrar de novo.

{
  "error": {
    "type": "idempotency_key_in_use",
    "message": "Já existe uma cobrança em andamento com esta Idempotency-Key..."
  }
}

409 significa não cobre de novo: a primeira tentativa ainda pode ter sido aprovada. Espere e consulte pelo seu external_id antes de tentar outra vez. A reserva se libera sozinha em 2 minutos se a cobrança nunca completar.

O que entra no cache (rotas de cobrança)

201 criadocacheado 24h: repetir devolve a mesma resposta, sem cobrar de novo
402 card_declinedcacheado 24h: repetir devolve a mesma recusa, sem nova tentativa no cartão
400 validaçãonão cacheado: nada foi cobrado; corrija e repita com a mesma chave
502 cartão, com idgateway_error trazendo error.order / error.charge / error.invoice: cacheado 24h. A venda chegou a existir e não dá para saber se o cartão foi debitado, então repetir com a mesma chave devolve o mesmo 502. Consulte o id ecoado (GET /orders/{id}, GET /charges/{id} ou GET /invoices/{id}): aprovado, a cobrança passou; senão, tente de novo com uma chave NOVA
502 cartão, sem idnão cacheado, mas a reserva segura a chave: 409 idempotency_key_in_use por até 2 min; depois ela libera e você repete com a mesma chave
502 PIXnão cacheado: pode repetir com a mesma chave
502 subscription_not_createdcacheado 24h (PIX e cartão): a cobrança existe, só a assinatura não nasceu; não recrie

O 402 ser cacheado é deliberado: bancos leem tentativas repetidas no mesmo cartão como sinal de fraude, então repetir uma recusa piora a chance do próximo pagamento legítimo. Depois de um 402, use outro cartão com uma chave nova.

Nas rotas de gestão (/cancel, /generate-invoice, /change-payment-method e /invoices/{id}/pix) a chave é opcional e só a resposta de sucesso fica gravada; um erro (por exemplo, o 502 do gateway que não confirmou o cancelamento) pode ser repetido com a mesma chave. /card tem regras próprias, descritas no card dele.

Depois de um timeout em /orders ou /charges, consulte GET /invoices?external_id= antes de repetir; por isso, ali, o external_id deve ser único por cobrança. Em /subscriptions ele é o id do tenant e se repete: consulte GET /subscriptions?external_id=.

Exemplo Node.js

import crypto from "crypto"

const idempotencyKey = crypto.randomUUID()
const res = await fetch(`${BASE}/subscriptions/${id}/cancel`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Idempotency-Key": idempotencyKey,
  },
})

6. Rate limit

60 requisições por minuto por API key. Excedeu, recebe 429 Too Many Requests com header Retry-After indicando quantos segundos aguardar.

HTTP/1.1 429 Too Many Requests
Retry-After: 47
Content-Type: application/json

{
  "error": "Muitas tentativas. Tente novamente em alguns instantes.",
  "retryAfter": 47
}

Sliding window de 60s. Se você prevê tráfego maior (ex: importação inicial de clientes), entre em contato para ajustar o limite.

Cobranças com cartão têm um limite próprio: 10 por minuto por API key. Vale para POST /orders, /charges e /subscriptions com forma_pagamento: "cartao" e para POST /subscriptions/{id}/card (em qualquer status da assinatura). É proteção anti card-testing e roda dentro dos 60/min gerais; estourar também devolve 429 com Retry-After.

O corpo do 429 é a exceção ao formato de erro da API: aqui error é um texto, não o objeto { type, message } da seção 7. Confira o status antes de ler o corpo. (O 429 too_many_attempts de /card é teto de tentativas por assinatura, não rate limit, e usa o formato padrão.)

7. Códigos de erro

Todos os erros seguem o formato abaixo, exceto o 429 de rate limit, que responde { "error": "<mensagem>", "retryAfter": <segundos> } (ali error é texto, não objeto) junto com o header Retry-After. Confira o status antes de ler error.type.

{
  "error": {
    "type": "<categoria>",
    "message": "<mensagem amigável>",
    ...campos extras quando aplicável (code, campo, current_status, tentativas_restantes, order/charge/invoice)
  }
}

Tipos

Um mesmo type pode sair com mais de um status (invalid_state vem como 400, 403 ou 409; gateway_error como 409, 502 ou 503). Decida pelo par status + type, e mostre error.message ao usuário.

400
invalid_request

Parâmetros faltando ou inválidos. error.message diz qual

400
invalid_state

Recurso em estado que não permite a operação (ex.: gerar PIX em fatura paga, produto fora do status ativo, trocar cartão de assinatura PIX). Às vezes traz current_status

400
unsupported_payment_method

forma_pagamento inválida no corpo de POST /orders, /charges ou /subscriptions. Disponíveis: pix e cartao

400
payment_method_not_supported

A operação não se aplica ao método da assinatura (ex.: generate-invoice numa assinatura de cartão). Nome parecido com o anterior, significado diferente: aqui o corpo está certo, a assinatura é que é do método errado

400
generate_invoice_failed

generate-invoice sem o que emitir: emissão pausada por 5 competências vencidas, ou todos os ciclos contratados já emitidos. O motivo vem em error.message

400
missing_customer_data

POST /invoices/{id}/pix: cliente sem telefone, ou fatura que não aceita PIX (de cartão, substituída)

401
authentication_error

API key ausente, formato ou prefixo inválido, inexistente, revogada ou expirada

402
card_declined

Cartão recusado pelo emissor ou pelo antifraude. Traz error.code e error.campo com o motivo traduzido, e o id do recurso já criado (order, charge ou invoice) em cancelado. Repetir a mesma chamada é recusado de novo: o caminho é outro cartão

403
insufficient_scope

API key sem o escopo exigido (seção 2): a chave Somente leitura numa rota de escrita ou de cobrança

403
invalid_state

change-payment-method: o pagarme_card_id não pertence ao cliente da assinatura

404
not_found

Recurso não existe, ou pertence a outra organização (404 mesmo quando existe noutro tenant). Um {id} de path que não é UUID também é 404 (em /charges/{id} e /orders/{id}, o ORD-… é aceito), exceto GET /products/{id}, que responde 400. Já produto_id inexistente no corpo de /orders e /subscriptions volta 400 "Produto não possui oferta ativa"

409
idempotency_key_in_use

Já existe uma cobrança com cartão em andamento com esta Idempotency-Key. NÃO repita: espere a primeira e consulte pelo external_id

409
invalid_state

Estado que impede a operação agora, ex.: reativar assinatura que não está expirada/cancelada; trocar cartão com outra alteração em andamento (code: operacao_em_andamento)

409
blocked_chargeback_window

change-payment-method bloqueado por 24h depois de estorno/reembolso da assinatura

409
gateway_error

change-payment-method: a troca agendada para cartão não pôde ser feita; nada mudou. Tente na data da próxima cobrança

422
offer_unavailable

reactivate: a oferta da assinatura não está mais disponível para nova compra

422
afiliado_invalido

POST /orders e /subscriptions: o campo afiliado não tem o formato de um código (6 a 16 letras e números). Nada foi cobrado. Código no formato que não vale não dá erro: a venda sai sem afiliado e afiliado.motivo diz por quê

422
afiliado_nao_suportado

POST /charges com o campo afiliado: cobrança de valor livre não tem produto e não paga comissão de afiliado. Use POST /orders

429
- (corpo próprio)

Rate limit excedido (60/min geral ou 10/min de cartão). Corpo { error: string, retryAfter: number }; use o header Retry-After

429
too_many_attempts

POST /subscriptions/{id}/card numa assinatura pausada: teto de 3 tentativas de troca de cartão em 24h e 5 em 30 dias por assinatura. Formato padrão, com tentativas_restantes: 0 e sem Retry-After

500
internal_error

Erro interno: tente de novo; se persistir, abra ticket

500
gateway_unavailable

POST /invoices/{id}/pix: gateway de pagamento não configurado na plataforma

502
gateway_error

Falha de comunicação com o gateway. Pode trazer o recurso já criado: error.order (/orders), error.charge (/charges) ou error.invoice (/subscriptions). No cartão, com esse id, a resposta fica gravada na Idempotency-Key por 24h (seção 5). Em /cancel quer dizer que a assinatura NÃO foi cancelada

502
subscription_not_created

POST /subscriptions: a cobrança (PIX ou cartão) saiu, mas a assinatura não nasceu. Traz error.invoice.id; não cobre de novo: contate o suporte

503
gateway_error

POST /subscriptions/{id}/card numa pausada (o gateway não respondeu antes de a cobrança sair) ou change-payment-method de cartão → PIX (o gateway não respondeu). Nada foi alterado: repita em alguns minutos

8. Exemplos completos

Cliente Node.js (TypeScript)

Wrapper mínimo reutilizável.

const BASE = "https://www.atmospay.com/api/v1"
const KEY = process.env.ATMOSPAY_API_KEY!

async function atmospay<T = any>(path: string, init: RequestInit = {}): Promise<T> {
  const res = await fetch(`${BASE}${path}`, {
    ...init,
    headers: {
      "Authorization": `Bearer ${KEY}`,
      "Content-Type": "application/json",
      ...init.headers,
    },
  })
  const json = await res.json().catch(() => ({}))
  if (!res.ok) {
    // O 429 do rate limit é a exceção ao formato de erro: ali `error` é texto.
    const msg = typeof json?.error === "string" ? json.error : json?.error?.message
    throw new Error(`[${res.status}] ${msg ?? "API error"}`)
  }
  return json
}

// Uso
const customer = await atmospay(`/customers?email=joao@x.com`)
const subscription = await atmospay(`/subscriptions/${id}`)
await atmospay(`/subscriptions/${id}/cancel`, {
  method: "POST",
  headers: { "Idempotency-Key": crypto.randomUUID() },
})

Cliente Python (requests)

import os, requests, uuid

BASE = "https://www.atmospay.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['ATMOSPAY_API_KEY']}"}

# Buscar cliente
r = requests.get(f"{BASE}/customers", params={"email": "joao@x.com"}, headers=HEADERS)
r.raise_for_status()
customer = r.json()

# Cancelar assinatura
sid = customer["subscriptions"]["data"][0]["id"]
r = requests.post(
    f"{BASE}/subscriptions/{sid}/cancel",
    headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
)
r.raise_for_status()

Fluxo: cliente quer pagar renovação via PIX

// No painel do parceiro: cliente clica em "Pagar agora" numa fatura pendente.

// 1. Buscar fatura pra confirmar status
const invoice = await atmospay(`/invoices/${invoiceId}`)
if (invoice.status !== "pendente") {
  throw new Error("Fatura já foi paga")
}

// 2. Gerar (ou regenerar) o PIX
const updated = await atmospay(`/invoices/${invoiceId}/pix`, {
  method: "POST",
  headers: { "Idempotency-Key": crypto.randomUUID() },
})

// 3. Renderizar pro cliente
return {
  qrcode: updated.pix.qrcode,        // URL https da imagem: <img src={qrcode} />
  copiaCola: updated.pix.copia_cola, // botão copiar
  expiraEm: updated.pix.expira_em,   // validade do QR (1 h), não o vencimento da fatura
}

// 4. Quando o cliente pagar, o webhook 'payment.approved' chega no seu endpoint,
//    e também 'invoice.paid', se o endpoint o assinar (fatura de renovação;
//    a 1ª cobrança chega só como 'payment.approved'/'subscription.created'),
//    e você atualiza o status no seu banco. Sem polling. Correlacione por
//    data.assinatura_id: o payment.approved da renovação não traz external_id
//    (o invoice.paid traz, lido da assinatura).
//    Atenção: no webhook os valores vêm em REAIS, não em centavos.

9. Webhooks (complementares à API)

A API trata ações iniciadas pelo cliente (cancelar, gerar PIX). Webhooks tratam mudanças de status assíncronas (pagamento aprovado pelo banco, retry automático de cobrança, falha em cartão expirado).

Nos webhooks os valores vêm em REAIS decimais; na API REST, em centavos. valor_bruto, valor_liquido, order_bumps[].valor e o valor dos eventos subscription.* chegam como 99.9 (= R$ 99,90), enquanto o 201 da mesma cobrança trouxe valor: 9990. Converta antes de comparar os dois: confundir é o bug mais provável desta integração.

Eventos enviados

  • payment.approved: pagamento aprovado (compra única, 1ª cobrança ou renovação de assinatura)
  • payment.failed: tentativa de cobrança recusada (cartão sem saldo, vencido, antifraude)
  • order.created: venda registrada, ainda aguardando pagamento
  • order.cancelled: cancelamento ou estorno via gateway
  • order.refunded: estorno manual ou chargeback (data.motivo: "chargeback")
  • pix.generated / boleto.generated: cobrança emitida (QR Code / linha digitável)
  • subscription.created: assinatura ativada no 1º pagamento aprovado (pendente → ativa). Payload plano: data.assinatura_id, data.cliente_email, data.status…
  • subscription.cancelled: assinatura cancelada; data.motivo: cliente | admin (inclusive o POST /cancel desta API) | gateway | expiracao
  • subscription.overdue: cron diário encontrou faturas vencidas e não pagas
  • produto.liberado: um produto da venda foi entregue (principal ou order bump); um evento por produto, roteado pelo produto dele
  • produto.revogado: o acesso a um produto acabou (cancelamento, inadimplência, expiração ou estorno); um evento por produto
  • invoice.created: fatura de renovação PIX emitida (10 dias antes do vencimento, ou quando o cliente/API adianta)
  • invoice.paid: fatura de renovação paga (PIX, cartão ou baixa manual). Não dispara na 1ª cobrança
  • invoice.due_soon: fatura PIX pendente que vence em 3 dias (cron 09:00, horário de Brasília)
  • invoice.due_today: fatura PIX pendente que vence hoje (cron 09:00, horário de Brasília)
  • abandoned_cart.detected: venda pendente há mais de 30 min sem aprovação
  • event.checkin: participante validado na portaria de um evento

Cada evento é assinado individualmente em Developers → Webhooks. O catálogo completo, com o payload de cada um, está na documentação de webhooks.

Formato do payload (payment.approved)

{
  "evento": "payment.approved",
  "timestamp": "2026-05-04T15:52:09.471Z",
  "data": {
    "venda_id": "3f2b5c6c-bb9b-42cc-8907-a823b05dae0c",
    "order_id": "ORD-1777909906193-QVZUTO",
    "organizacao_id": "5d1e...",
    "assinatura_id": "1c8b2a4d-...",       // null em compras únicas
    "tipo": "invoice",                       // 'unica' | 'invoice' (recorrência)
    "invoice_referencia": "2026-05",         // só em recorrência
    "valor_bruto": 99.9,                     // REAIS (R$ 99,90); na API REST seria 9990
    "valor_liquido": 99.9,                   // REAIS
    "forma_pagamento": "pix",
    "pago_em": "2026-05-04T15:52:06.000Z",
    "cliente_id": "8f2a1b9c-...",
    "cliente_nome": "João da Silva",
    "cliente_email": "joao@exemplo.com",
    "cliente_telefone": "(31) 98956-0672",
    "cliente_documento": "12345678901",
    "produto": {
      "id": "8bc70e59-...",
      "nome": "Aliançados",
      "tipo": "digital",
      "redirect_url": "https://site.com/obrigado?order_id=ORD-1777909906193-QVZUTO"
    },
    "oferta": {
      "id": "595ad338-...",
      "nome": "Plano Mensal",
      "recorrente": true,
      "intervalo_cobranca": "mensal"
    },
    "assinatura": {                          // presente quando assinatura_id != null
      "id": "1c8b2a4d-...",
      "status": "ativa",
      "intervalo": "mensal",
      "proxima_cobranca": "2026-06-04T00:00:00.000Z",
      "cancelada_em": null,
      "primeira_cobranca": true,             // true na 1ª cobrança; false nas renovações
      "criada_em": "2026-05-04T15:50:00.000Z"
    },
    "order_bumps": [],                       // itens extras da compra, cada um com o seu valor (em REAIS)
    "external_id": "tenant-991",             // só se você enviou, e só na 1ª cobrança ou compra única
    "compradora": { "id": "1111..." },       // idem, quando o checkout recebeu ?org=
    "tracking": { "utm_source": "..." }      // idem, quando a venda teve UTM
  }
}
Para integrar com a API v1: ao receber o primeiro payment.approved com assinatura.primeira_cobranca = true, salve data.assinatura_id junto da conta do cliente no seu banco. Ele é o id usado em GET /api/v1/subscriptions/{id}, /cancel, /invoices. O subscription.created, que sai no mesmo momento, traz o mesmo id em data.assinatura_id.

Como diferenciar primeira compra vs renovação

  • data.assinatura_id == null → compra única (não recorrente)
  • data.assinatura.primeira_cobranca == true → primeira compra de uma nova assinatura (envie email de boas-vindas, libere acesso)
  • data.assinatura.primeira_cobranca == false → renovação automática (atualize próxima_cobranca, envie comprovante)
  • Para achar o cliente na renovação, use data.assinatura_id: está em todo pagamento de assinatura. O payment.approved da renovação não traz external_id, compradora nem tracking: esses vêm do metadata da venda, e a fatura de renovação não o copia. Os eventos invoice.* e subscription.* trazem o external_id, lido da assinatura
  • Em PIX o pagamento da renovação pode chegar até 10 dias antes do vencimento (a fatura é emitida com essa antecedência). O fim do período pago é data.assinatura.proxima_cobranca, que já vem avançada no payload; não calcule a partir de pago_em. Para saber se ainda há mês em aberto, consulte cobranca.em_atraso em GET /subscriptions/{id}

10. Checklist de integração

  1. Criar API key (somente leitura, se a integração só consulta) e armazenar como secret no servidor do parceiro; a de acesso total pode cobrar (seção 2)
  2. Configurar webhook outbound apontando para um endpoint do parceiro
  3. Guardar o id da assinatura: do 201 de POST /subscriptions, ou, no webhook handler, data.assinatura_id e data.cliente_email do primeiro payment.approved (com assinatura.primeira_cobranca = true) ou do subscription.created
  4. No webhook, tratar valores em reais (na API REST eles vêm em centavos)
  5. Implementar tela "Minhas assinaturas" no painel chamando GET /subscriptions/{id}
  6. Botão "Cancelar" → POST /subscriptions/{id}/cancel com Idempotency-Key; um 502 quer dizer "não cancelou"; repita
  7. Lista de faturas → GET /subscriptions/{id}/invoices
  8. Botão "Pagar via PIX" em fatura pendente → POST /invoices/{id}/pix
  9. Enviar forma_pagamento explicitamente em toda cobrança
  10. Tratar erros 4xx exibindo a error.message ao usuário, e ler o 429 à parte, porque o corpo dele tem outro formato
  11. Implementar retry com backoff exponencial em 5xx com a mesma Idempotency-Key, exceto o 502 de cartão que traz o id da venda (error.order/charge/invoice): esse fica gravado na chave; consulte o id e, se for o caso, cobre de novo com chave nova. 409 idempotency_key_in_use não se repete: espere e consulte
  12. Monitorar rate limit nos seus logs (429 = ajustar pacing)