# Abeesys payments-core — documentação completa Contrato 0.14.0 (OpenAPI 3.1.0). Endereço público: `https://api-payments.abeesys.com`, `https://api-payments.dev.abeesys.com`. ## Visão geral Hub de pagamentos da plataforma Abeesys. Cada sistema cliente (tenant) se autentica com a própria API key e só enxerga o que é seu. **Autenticação.** Toda rota de negócio exige a API key (`Authorization: Bearer `). As exceções são as de saúde, os avisos de gateway (webhooks, autenticados pela assinatura), o retorno da autorização do recebedor e as quatro rotas de documentação (`/openapi.yaml`, `/openapi.json`, `/llms.txt`, `/llms-full.txt`), que não levam dado de sistema nenhum e só respondem pela rede privada e pelo endereço local — pedidas por um endereço público, são 404 (desde a 0.13.0; quem as publica é o portal de documentação). Valores sempre em **unidade mínima da moeda** (centavos para BRL), inteiros, com a moeda ISO 4217 ao lado. Nunca decimal. Erros seguem a RFC 9457 (`application/problem+json`), com `type` em `https://abeesys.com/errors/`. **O sistema pede o quê; o hub decide como.** O sistema informa valor, meio, pagador e referência — nunca o gateway. O hub escolhe o gateway pela política de roteamento do sistema (ordem de preferência por meio, configurada por quem opera a plataforma), entre as contas que o próprio sistema conectou. O gateway que atendeu aparece na resposta, como informação para suporte e conciliação. **Avisos.** A cada mudança de cobrança o hub envia um aviso assinado ao endereço que o sistema cadastrou com quem opera a plataforma — ver `webhooks.chargeUpdated`. **Estornos.** O sistema pede o estorno de uma cobrança capturada, inteiro ou em partes (`POST /api/v1/charges/{id}/refunds`). O estorno é feito no mesmo gateway e na mesma conta em que a cobrança foi criada. Estornos feitos no painel do gateway também aparecem, com `origin` `provider_dashboard`. **Taxas do gateway.** O gateway informa a taxa de cada cobrança — da captura e, quando cobra, de cada estorno — e o hub a lança no ledger (`provider_fee`; a correção que o gateway fizer depois entra como `provider_fee_adjustment`, para mais ou para menos). O saldo da carteira passa a ser o **líquido**: bruto menos as taxas lançadas. Enquanto o gateway não informa a taxa da captura, a cobrança mostra a taxa como `pending`, nunca como zero. A taxa não gera aviso: consulte a cobrança ou a carteira. **Conta da plataforma e subconta virtual** (desde a 0.8.0, ADR 0007). Para cada gateway que a política tenta, a cobrança vai na **conta do próprio sistema**, se ele tem credencial naquele gateway; se não tem, e o sistema é da Lopes Tech (mesmo CNPJ) e foi liberado por quem opera, vai na **conta da plataforma** daquele gateway, e o valor capturado fica na **subconta virtual** do sistema. `settles_in` diz qual dos dois, como informação. O fallback é sempre entre gateways, nunca entre contas do mesmo gateway. O saldo e o extrato da subconta saem em `GET /api/v1/subaccounts` e `GET /api/v1/subaccounts/entries`, separados das carteiras; os lançamentos do modo plataforma **não** aparecem em `GET /api/v1/ledger/entries`, que é a listagem das carteiras espelho. O repasse do saldo da subconta é feito por quem opera, fora do hub, e registrado nele: aparece no extrato e no aviso `subaccount.payout`. **Endereço de volta do cartão** (desde a 0.9.0). Há gateway que, quando o emissor pede uma autenticação extra (3-D Secure), devolve a ação ao pagador só se souber para onde ele volta depois. O formulário de cartão declara isso em `return_url_required`; quando `true`, a cobrança de cartão daquele formulário exige `card.return_url` (422 nomeando o campo, sem chamar o gateway). Para os outros gateways o campo é aceito e ignorado. **Split — venda dividida com um recebedor** (desde a 0.10.0, spec 0017). O sistema cadastra o **recebedor** (`POST /api/v1/recipients`) e pede a **autorização** da conta dele no gateway (`POST /api/v1/recipients/{id}/links`): o recebedor autoriza a conta dele numa página do gateway e volta ao endereço que o sistema informou, com `status=linked` ou `status=failed`. Na cobrança, `split` diz a parte do recebedor, em valor ou em pontos-base; o resto é a **comissão** do sistema. A cobrança nasce **na conta do recebedor** no gateway (`settles_in` `recipient_account`) e a comissão vai como taxa de marketplace para a conta da plataforma, alocada na subconta virtual do sistema. A parte do recebedor nunca passa pela conta da plataforma. Por enquanto: um recebedor por cobrança e a taxa do gateway na conta do recebedor. **Estorno e disputa de cobrança dividida** (desde a 0.11.0, spec 0061). O estorno, inteiro ou parcial, é **proporcional à divisão**: devolve a parte do recebedor e a comissão na proporção da cobrança. O centavo do arredondamento fica com a comissão até o fim (a comissão devolve o piso da proporção acumulada), e quando tudo foi devolvido cada parte voltou inteira. O estorno confirmado traz as duas partes em `split`, e a cobrança, o total devolvido de cada uma (`split.refunded_recipient_amount`, `split.refunded_commission`). A disputa perdida também é proporcional quando o gateway devolve a comissão; quando ele a mantém na conta da plataforma, a disputa fica em análise de quem opera, e a cobrança continua `disputed`. Sistema declarado **marketplace** por quem opera só cobra pela conta da plataforma **com** divisão: sem `split`, 422 no campo `split`. **Listas abertas.** Os campos de saída documentados como **lista aberta** podem trazer valores novos sem aviso prévio: o sistema trata o valor desconhecido como genérico, sem recusar a resposta. São eles: o tipo do aviso, `failure_reason` da cobrança, `kind` do lançamento, `status`, `origin`, `reason` e `failure_reason` do estorno, `fee.status` e `fee.origins[].origin` da cobrança, `settles_in` da cobrança, e `kind`, `counterparty.type` e `payout.status` do extrato da subconta, e `status` e `deactivation_reason` do vínculo de recebedor. O status da cobrança é conjunto **fechado**. **Correlação** (desde a 0.12.0). Toda resposta traz `X-Request-Id`: 32 caracteres hexadecimais minúsculos, gerados pelo hub a cada requisição. É a referência do `detail` de um 500 e o que quem opera procura no log; informe-a ao suporte. O `X-Request-Id` que o sistema enviar **não** é devolvido nem substitui o do hub: fica registrado à parte, só para casar com o log do sistema (antes da 0.12.0, o hub ecoava o valor recebido). **Cabeçalhos de segurança** (desde a 0.12.0). Toda resposta traz também `Strict-Transport-Security`, `X-Content-Type-Options: nosniff` e `Referrer-Policy: no-referrer`. **Corpo acima do limite** (desde a 0.12.0). Nas rotas de aviso de gateway, corpo acima de 1 MiB é 413 `payload-too-large`. **Limite de taxa** (desde a 0.14.0). Acima do limite, a resposta é 429 `rate-limited`, com `Retry-After` em segundos inteiros e o mesmo número no `detail`. Nas rotas com API key, nada é gravado, nenhum gateway é chamado e a `Idempotency-Key` não é consumida: repetir depois tem o efeito de sempre. Nos avisos de gateway, nada é gravado; o 429 por origem e o de assinatura inválida saem antes de a assinatura ser conferida, e o gateway reenvia. Os limites valem por sistema (API key) e por endereço de origem — em toda rota, menos as de saúde, inclusive rota inexistente. Tentativas repetidas de key inválida, ou de assinatura inválida num mesmo endereço de aviso, bloqueiam a origem por um tempo: a origem bloqueada recebe 429 sem a key (ou a assinatura) ser conferida, e o 429 não revela se a key existe. Os números são configuração da plataforma e não fazem parte do contrato. ## Autenticação - `apiKey`: http bearer — `Authorization: Bearer `. API key do tenant (`abs_live_...`), gerada pelo CLI `tenant create`. - Sem API key: `GET /api/v1/health`, `GET /api/v1/health/ready`, `GET /api/v1/recipient-links/callback`, `POST /api/v1/webhooks/{provider}/{tenantID}`, `POST /api/v1/marketplace-webhooks/{accountID}`, `POST /api/v1/platform-webhooks/{accountID}`. ## Erros Todo erro sai como `application/problem+json` (RFC 9457), com `type`, `title`, `status`, `detail` e, no 422, `errors[]` com `field` e `message`. - `RateLimited`: acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — type `https://abeesys.com/errors/rate-limited` - `Health`: estado do serviço - `NotFound`: recurso inexistente — ou de outro sistema, ou rota de documentação pedida por endereço público — type `https://abeesys.com/errors/not-found` - `PayloadTooLarge`: corpo acima de 1 MiB (1048576 bytes) — type `https://abeesys.com/errors/payload-too-large` - `Problem`: erro (RFC 9457) — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` ## Rotas Os caminhos abaixo são relativos ao endereço da API (`https://api-payments.abeesys.com`, `https://api-payments.dev.abeesys.com`). Um site que só publica esta documentação não os atende. ### GET /api/v1/health Liveness — não toca no banco - operationId: `getLiveness` - autenticação: sem API key Respostas: - `200` (Health): estado do serviço — `application/json` objeto - `status` (obrigatório): string (enum: `ok`, `degraded`) ### GET /api/v1/health/ready Readiness — confere o banco - operationId: `getReadiness` - autenticação: sem API key Respostas: - `200` (Health): estado do serviço — `application/json` objeto - `status` (obrigatório): string (enum: `ok`, `degraded`) - `503` (Health): estado do serviço — `application/json` objeto - `status` (obrigatório): string (enum: `ok`, `degraded`) ### GET /api/v1/charges Lista as cobranças do tenant, da mais recente à mais antiga - operationId: `listCharges` - autenticação: API key do tenant Só as cobranças do sistema autenticado, com qualquer filtro: a referência de um pedido de outro sistema devolve lista vazia, não erro. Os filtros são opcionais e se combinam com E. **Ordem total:** `created_at` decrescente e, no empate, `id` decrescente — uma cobrança nunca aparece em duas páginas nem falta entre elas enquanto o conjunto não muda. Página além do fim devolve `items` vazio, com o `total`. **Percorrer sem deslocamento.** Uma cobrança criada durante a leitura entra no topo e desloca as páginas seguintes. Para percorrer um conjunto fixo, guarde o instante da primeira consulta e mande-o em `created_to` em todas as páginas: o que nasce depois fica fora da janela. Parâmetro que não está aqui, parâmetro repetido, ou valor fora do que o parâmetro aceita (status inexistente, gateway que o hub não conhece, data inválida, `created_from` igual ou depois de `created_to`, página fora da faixa, texto fora de UTF-8): 422, com o parâmetro em `errors[].field` — escapado como na URL (`%FF`) quando o próprio nome não é UTF-8. Query string malformada: 400. Parâmetros: - `status` (query, opcional): ChargeStatus (enum: `pending`, `authorized`, `captured`, `refunded`, `failed`, `canceled`, `disputed`) - `provider` (query, opcional): string, de 1 a 40 caracteres, padrão `^[^\x00-\x1F\x7F-\x9F]*$` — O gateway que atende a cobrança. Gateway que o hub não conhece é 422. - `reference` (query, opcional): string, de 1 a 255 caracteres, padrão `^[^\x00-\x1F\x7F-\x9F]*$` — A referência do pedido, por igualdade exata. Devolve todas as tentativas. - `created_from` (query, opcional): string, formato date-time — Criadas a partir deste instante, inclusive. - `created_to` (query, opcional): string, formato date-time — Criadas antes deste instante (exclusivo). - `page` (query, opcional): integer, de 1 a 10000, padrão `1` - `page_size` (query, opcional): integer, de 1 a 100, padrão `20` Respostas: - `200`: página de cobranças — `application/json` objeto - `items` (obrigatório): lista de Charge - `meta` (obrigatório): PageMeta - `400` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `422` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### POST /api/v1/charges Cria uma cobrança — o hub escolhe o gateway - operationId: `createCharge` - autenticação: API key do tenant Idempotente pela `Idempotency-Key`, única por tenant. Mesma chave e mesmo corpo devolvem a cobrança original com 200, sem chamar gateway nenhum — qualquer que tenha sido o gateway que a atendeu, e mesmo que a política tenha mudado depois. Mesma chave com outro corpo é 409. **Roteamento.** O hub tenta o primeiro gateway da política daquele meio que aceita o valor e as condições (parcelas, validade do pix, vencimento do boleto). Se ele **recusar antes de criar** a cobrança (fora do ar, com certeza de que nada foi criado), o hub tenta o próximo. **Nunca** depois de um timeout: aí a cobrança fica `pending` naquele gateway. Se nenhum gateway da política atende o meio, o valor e as condições — inclusive política vazia —, 422 `no-route`, sem nome de gateway. A cobrança nasce `pending`; a mudança de status chega pelo webhook do gateway. Se o gateway não responder (502), a cobrança fica `pending` — timeout não prova recusa — e é resolvida pelo webhook ou pela conciliação. **Pagador.** Cada gateway exige um subconjunto diferente do pagador para cada meio (por exemplo: pix pede nome, e-mail e documento; boleto pede também o endereço). O hub confere contra o gateway escolhido **antes** de chamá-lo e responde 422 nomeando o campo que falta (`errors[].field`, como `payer.email`) — sem procurar outro gateway que exija menos. Texto vazio equivale a ausente. **Cartão.** O hub nunca recebe número, validade nem código do cartão: só o `token` gerado pelo formulário do gateway no navegador do pagador. Antes, o sistema pede ao hub o formulário (`POST /api/v1/card-forms`) e manda de volta `card.form_id`: a cobrança vai ao gateway daquele formulário — **sem fallback**, porque o token não serve em outro —, mesmo que ele tenha saído da política depois, desde que a conta continue conectada; senão, 422 em `card.form_id`. O token é de uso único e não é guardado; a repetição com a mesma `Idempotency-Key` devolve a cobrança sem reenviá-lo. Parcelas acima do que o gateway aceita, ou em gateway que não parcela, são 422. Quando o gateway exige uma ação do pagador (3-D Secure), a cobrança fica `pending` e a resposta traz `card.action_url`. **Gateway indisponível.** 502 em dois casos, com `detail` diferente: (1) o gateway não respondeu — a cobrança fica `pending`, e pode ter sido criada lá; consulte-a antes de tentar de novo; (2) todos os gateways possíveis recusaram antes de criar — a cobrança fica `failed` com `failure_reason` `provider_unavailable`, e nada foi cobrado. **Recusa do gateway** (cartão negado, token gasto ou expirado) não é erro HTTP: a cobrança volta com status `failed` e `failure_reason`. `card`, `pix` e `boleto` só valem para o `method` de mesmo nome; enviados com outro meio, 422. **Referência e metadados.** `reference` é o pedido no sistema (não é única: um pedido pode ter várias tentativas) e `metadata` é um conjunto pequeno de pares chave-valor. O hub não interpreta nenhum dos dois, devolve-os em toda leitura e em todo aviso da cobrança, e não os muda depois da criação. Eles fazem parte do corpo para a idempotência: mesma chave com outra referência ou outros metadados é 409 (a ordem das chaves de `metadata` não importa). **Divisão** (desde a 0.10.0). Com `split`, a cobrança vai ao primeiro gateway da política com split nativo em que o recebedor tem vínculo ativo, e nasce na conta dele. Nenhum gateway da política com split nativo: 422 `capability-not-supported`; o recebedor sem vínculo em nenhum deles: 422 `no-route`. Recebedor que não existe ou é de outro sistema: 422 em `split.recipients[0].recipient_id`. A divisão faz parte do corpo para a idempotência. Nenhum campo aceita `null`: para não informar, omita o campo. `null` em qualquer nível é 422 nomeando o campo. O corpo precisa ser UTF-8 válido (RFC 8259); fora disso, 400 — o hub não troca bytes em silêncio. `Idempotency-Key` fora de UTF-8 ou com caractere de controle é 422. Parâmetros: - `Idempotency-Key` (header, obrigatório): string, de 1 a 255 caracteres Corpo (`application/json`): CreateCharge - `amount` (obrigatório): integer, formato int64, mínimo 1 — Unidade mínima da moeda. Em cartão parcelado é o **total** — o hub não calcula juros de parcelamento. - `currency` (obrigatório): string, padrão `^[A-Z]{3}$` - `method` (obrigatório): string (enum: `card`, `pix`, `boleto`) - `description` (opcional): string, máximo 500 caracteres, padrão `^[^\x00]*$` — Quebra de linha vale; o caractere NUL (`\\u0000`) é 422. - `payer` (opcional): Payer - `card` (opcional): CardInput - `pix` (opcional): PixInput - `boleto` (opcional): BoletoInput - `reference` (opcional): Reference - `metadata` (opcional): Metadata - `device_id` (opcional): string, máximo 255 caracteres, padrão `^[^\x00-\x1F\x7F-\x9F]*$` — Identificador antifraude do dispositivo do pagador, quando o gateway usa um. O hub o repassa sem interpretar e não o guarda. - `split` (opcional): SplitInput Respostas: - `201`: criada — `application/json` Charge - `200`: repetição da mesma Idempotency-Key — a cobrança original — `application/json` Charge - `400` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `409` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `422` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` - `502` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` ### POST /api/v1/recipients Cadastra um recebedor do sistema - operationId: `createRecipient` - autenticação: API key do tenant Desde a 0.10.0 (spec 0017). O recebedor é quem fica com a parte de uma venda dividida — a loja parceira, o prestador —, numa conta em nome próprio no gateway. É do sistema que o cadastrou: o de outro sistema é 404. Idempotente pela `reference` (o id do recebedor no seu sistema), única por sistema: a mesma referência com o mesmo nome devolve o recebedor já cadastrado com 200; com outro nome, 409. O recebedor nasce **sem vínculo**: a conta dele no gateway entra pela autorização (`POST /api/v1/recipients/{id}/links`). Corpo (`application/json`): CreateRecipient - `name` (obrigatório): string, de 1 a 100 caracteres - `reference` (obrigatório): string, de 1 a 255 caracteres — o id do recebedor no seu sistema — única por sistema Respostas: - `201`: cadastrado — `application/json` Recipient - `200`: a mesma referência — o recebedor já cadastrado — `application/json` Recipient - `400` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `409` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `422` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### GET /api/v1/recipients/{id} Consulta um recebedor do sistema, com os vínculos - operationId: `getRecipient` - autenticação: API key do tenant Parâmetros: - `id` (path, obrigatório): string, formato uuid Respostas: - `200`: o recebedor — `application/json` Recipient - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `404` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### POST /api/v1/recipients/{id}/links Abre a autorização da conta do recebedor num gateway - operationId: `createRecipientLink` - autenticação: API key do tenant Devolve a página do gateway onde o recebedor autoriza a conta dele para o marketplace da plataforma. O sistema leva o recebedor até ela; o recebedor autoriza lá e volta a `return_url`, com `status=linked` (vínculo gravado) ou `status=failed` (negado, código recusado, ou a conta já vinculada a outro recebedor). O link vale até `expires_at` e serve **uma vez**. O vínculo gravado gera o aviso `recipient.link_updated`. Um vínculo ativo por recebedor em cada gateway; a mesma conta autorizada de novo é o mesmo vínculo. A mesma conta do gateway não se vincula a dois recebedores — nem de sistemas diferentes. Só para sistema que pode usar a conta da plataforma (o marketplace é dela). Gateway sem split nativo: 422 `capability-not-supported`. Sem o marketplace neste ambiente, ou sistema que não pode usar a conta da plataforma: 422 no campo `provider`. Parâmetros: - `id` (path, obrigatório): string, formato uuid Corpo (`application/json`): CreateRecipientLink - `provider` (obrigatório): string - `return_url` (obrigatório): string, máximo 2048 caracteres, padrão `^https://[^/\s][^\s]*$` — para onde o recebedor volta, com `status=linked` ou `status=failed` Respostas: - `201`: a página de autorização — `application/json` RecipientLinkAuthorization - `400` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `404` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `422` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### GET /api/v1/recipient-links/callback Volta da autorização do recebedor — quem chama é o navegador dele - operationId: `completeRecipientLink` - autenticação: sem API key Rota pública: o gateway manda o navegador do recebedor de volta para cá. Sem API key — o `state`, de uso único e vida curta, é o que identifica o sistema e o recebedor; nada mais da query decide de quem é o vínculo. `state` inválido, vencido ou já usado: 400, e nada é gravado. Válido: 302 para o `return_url` informado na abertura, com `status=linked` ou `status=failed`. Parâmetros: - `state` (query, obrigatório): string, máximo 128 caracteres - `code` (query, opcional): string Respostas: - `302`: de volta ao sistema, com `status` - `400` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### POST /api/v1/card-forms Qual formulário de cartão usar para esta cobrança - operationId: `createCardForm` - autenticação: API key do tenant O sistema pergunta "vou cobrar cartão de R$ X" e o hub responde com o gateway, a chave pública do formulário dele e o que ele exige — o máximo de parcelas e os campos do pagador. O sistema monta o formulário do gateway no navegador do pagador com a `public_key`, recebe o token e cria a cobrança com `card.form_id` = `id`. O gateway é o primeiro da política de cartão do sistema que aceita o valor e as parcelas e tem conta conectada. Nenhum: 422 `no-route`, sem nome de gateway. O formulário não cobra nada e não expira no hub — o token é que expira, no gateway. **Cobrança dividida** (desde a 0.10.0): informe `recipient_id`, e o formulário é o da conta do recebedor — a cobrança de cartão com `split` exige este formulário, do mesmo recebedor (422 em `card.form_id` se não for). O corpo precisa ser UTF-8 válido (RFC 8259); fora disso, 400. Corpo (`application/json`): CardFormRequest - `amount` (obrigatório): integer, formato int64, mínimo 1 — unidade mínima da moeda - `currency` (obrigatório): string, padrão `^[A-Z]{3}$` - `installments` (opcional): integer, de 1 a 24, padrão `1` - `recipient_id` (opcional): string, formato uuid — desde a 0.10.0 — o recebedor da cobrança dividida; o formulário é o da conta dele Respostas: - `201`: o formulário a usar — `application/json` CardForm - `400` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `422` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### GET /api/v1/charges/{id} Consulta uma cobrança do tenant - operationId: `getCharge` - autenticação: API key do tenant Cobrança de outro tenant responde 404, igual a uma que não existe. Parâmetros: - `id` (path, obrigatório): string, formato uuid Respostas: - `200`: a cobrança — `application/json` Charge - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `404` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### POST /api/v1/charges/{id}/refunds Pede o estorno de uma cobrança capturada, inteiro ou em parte - operationId: `createRefund` - autenticação: API key do tenant Idempotente pela `Idempotency-Key`, única por tenant entre os pedidos de estorno (o espaço de chaves é separado do da criação de cobrança). Mesma chave e mesmo corpo devolvem o mesmo estorno com 200, **no estado em que ele está**; mesma chave com outro corpo, ou para outra cobrança, é 409. **Estornável.** Cabe pedir enquanto `amount` ≤ capturado − estornos confirmados − estornos pendentes − disputa perdida. O estorno pendente **reserva** o valor: outro pedido não pode usá-lo até ele se resolver; o que falha libera a reserva. Acima do estornável é 409, sem chamar o gateway. Vários parciais valem, enquanto couberem. **Quando a cobrança muda.** Ela passa a `refunded` quando a soma dos estornos confirmados chega ao capturado — antes disso continua `captured`, com `refunded_amount` e `pending_refund_amount` dizendo quanto. A mudança de status sai num aviso `charge.updated`, como hoje. **Resultado.** `201` com o estorno em um destes estados: - `succeeded` — confirmado pelo gateway; lançado no ledger. - `pending` — o gateway aceitou e confirma depois; o valor fica reservado, e a confirmação chega pelo aviso `refund.updated`. - `failed` — **recusa de negócio** do gateway (prazo vencido, saldo insuficiente na conta, estorno não permitido), com `failure_reason`. Não é erro HTTP: a requisição deu certo, o estorno não. A reserva é liberada. **Gateway sem resposta.** `502`, e o estorno fica `pending` com o valor reservado: timeout não prova recusa — o gateway pode ter estornado, e o aviso de confirmação que chegar depois é aplicado normalmente. Repita o pedido com a **mesma** `Idempotency-Key`: o hub refaz a chamada com o mesmo id de estorno (o gateway não estorna duas vezes) e devolve o estado atual. **409** quando a cobrança não está `captured` (pendente, autorizada, falha, cancelada, estornada — inclusive depois de uma **disputa perdida**, que sempre a leva a `refunded`), quando está em **disputa** (`disputed`), ou quando o valor passa do estornável. **422** quando o gateway da cobrança não faz estorno parcial e `amount` é diferente do valor da cobrança, ou quando o gateway não estorna pela API o meio da cobrança (o boleto, em alguns gateways) — `capability-not-supported`, sem gravar estorno e sem chamar o gateway. **Cobrança dividida** (desde a 0.11.0; antes, o parcial era 422). O estorno parcial é aceito como o de qualquer cobrança e é **proporcional à divisão**: numa cobrança de 10000 com comissão 2000, estornar 5000 devolve 4000 da parte do recebedor e 1000 da comissão. A divisão é feita sobre o valor lançado do estorno, quando o gateway o confirma, e aparece em `split` no estorno `succeeded`. **Descrição.** `description` é do sistema: fica no hub, volta na consulta e **nunca** é enviada ao gateway. Ao gateway vai só `reason`. Nenhum campo aceita `null`; o corpo precisa ser UTF-8 válido (fora disso, 400). Parâmetros: - `id` (path, obrigatório): string, formato uuid - `Idempotency-Key` (header, obrigatório): string, de 1 a 255 caracteres Corpo (`application/json`): CreateRefund - `amount` (obrigatório): integer, formato int64, mínimo 1 — Unidade mínima da **moeda da cobrança**. Igual ao valor da cobrança é estorno total; menor, parcial. - `reason` (obrigatório): string (enum: `requested_by_customer`, `duplicate`, `fraudulent`, `other`) — Lista **fechada** na entrada: fora dela, 422. Pedido do pagador, cobrança duplicada, suspeita de fraude, outro. - `description` (opcional): string, máximo 255 caracteres, padrão `^[^\x00-\x1F\x7F-\x9F]*$` — Do sistema: fica no hub e volta na consulta. **Nunca é enviada ao gateway** — não ponha dado do pagador aqui. Respostas: - `201`: estorno registrado — `succeeded`, `pending` ou `failed` — `application/json` Refund - `200`: repetição da mesma Idempotency-Key — o estorno, no estado atual — `application/json` Refund - `400` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `404` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `409` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `422` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` - `502` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` ### GET /api/v1/charges/{id}/refunds Lista os estornos de uma cobrança, do mais antigo ao mais novo - operationId: `listRefunds` - autenticação: API key do tenant Todos os estornos da cobrança — pedidos pela API e feitos no painel do gateway —, em qualquer estado, na ordem total `created_at` crescente e, no empate, `id` crescente. Cobrança de outro tenant responde 404, igual a uma que não existe. Parâmetro de consulta desconhecido é 422. Parâmetros: - `id` (path, obrigatório): string, formato uuid - `page` (query, opcional): integer, de 1 a 10000, padrão `1` - `page_size` (query, opcional): integer, de 1 a 100, padrão `20` Respostas: - `200`: página de estornos — `application/json` objeto - `items` (obrigatório): lista de Refund - `meta` (obrigatório): PageMeta - `400` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `404` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `422` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### GET /api/v1/refunds/{id} Consulta um estorno do tenant - operationId: `getRefund` - autenticação: API key do tenant Estorno de outro tenant responde 404, igual a um que não existe. Parâmetros: - `id` (path, obrigatório): string, formato uuid Respostas: - `200`: o estorno — `application/json` Refund - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `404` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### GET /api/v1/wallets Saldo de cada carteira do tenant, por gateway e moeda - operationId: `listWallets` - autenticação: API key do tenant Derivado do ledger — soma das partidas, nunca um campo guardado. `balance` é o **líquido** (`gross − fees`, no centavo); `gross` e `fees` o decompõem, e `pending_fee_charges`/`pending_fee_gross` contam as cobranças cuja taxa ainda não foi informada pelo gateway. Respostas: - `200`: carteiras — `application/json` objeto - `items` (obrigatório): lista de Wallet - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### GET /api/v1/ledger/entries Lançamentos do tenant, do mais recente ao mais antigo - operationId: `listLedgerEntries` - autenticação: API key do tenant Parâmetros: - `page` (query, opcional): integer, mínimo 1, padrão `1` - `page_size` (query, opcional): integer, de 1 a 100, padrão `20` Respostas: - `200`: página de lançamentos — `application/json` objeto - `items` (obrigatório): lista de LedgerEntry - `total` (obrigatório): integer - `page` (obrigatório): integer - `page_size` (obrigatório): integer - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### GET /api/v1/subaccounts Saldo de cada subconta virtual do tenant, por gateway e moeda - operationId: `listSubaccounts` - autenticação: API key do tenant A subconta virtual registra quanto do dinheiro da conta da plataforma num gateway foi gerado por este sistema e ainda não foi repassado a ele (ADR 0007). Derivado do ledger — soma dos lançamentos, nunca um campo guardado. Um sistema que só cobra com credencial própria não tem subconta (lista vazia): o dinheiro dele vai direto para a conta dele, e o saldo está em `GET /api/v1/wallets`. O saldo é um número só (`balance`), e pode ficar **negativo** — um estorno ou uma disputa perdida depois do repasse. As disputas abertas e as taxas ainda não informadas aparecem **só como informação**, sem lançamento: não estão no saldo. Respostas: - `200`: subcontas — `application/json` objeto - `items` (obrigatório): lista de Subaccount - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### GET /api/v1/subaccounts/entries Extrato de uma subconta, do lançamento mais recente ao mais antigo - operationId: `listSubaccountEntries` - autenticação: API key do tenant Cada lançamento que mexeu na subconta, com a cobrança, o estorno ou o repasse que o causou. Ordem total: `created_at` decrescente, `id` decrescente — nenhum lançamento se repete nem some entre páginas. Parâmetro desconhecido é 422. Subconta que não existe devolve a página vazia. A contrapartida de cada lançamento é a conta da plataforma no gateway, mostrada só como `counterparty` `{type: platform_account, provider}` — sem saldo, sem identificador e sem nada de outro sistema. Parâmetros: - `provider` (query, obrigatório): string, de 1 a 64 caracteres - `currency` (query, obrigatório): string, padrão `^[A-Z]{3}$` - `page` (query, opcional): integer, de 1 a 100000, padrão `1` - `page_size` (query, opcional): integer, de 1 a 100, padrão `20` Respostas: - `200`: página do extrato — `application/json` objeto - `items` (obrigatório): lista de SubaccountEntry - `meta` (obrigatório): EntryPageMeta - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `422` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### POST /api/v1/webhooks/{provider}/{tenantID} Recebe o webhook de um gateway - operationId: `receiveWebhook` - autenticação: sem API key Sem API key: quem chama é o gateway. A autenticação é a assinatura, verificada com o segredo de webhook daquele tenant naquele gateway. Evento repetido, fora de ordem ou de cobrança desconhecida responde 200 — devolver erro faria o gateway reenviar para sempre — e fica registrado com o desfecho. **Estornos.** O evento de estorno se casa com o estorno pelo id que o gateway deu a ele: resposta do pedido, webhook e consulta, em qualquer ordem, lançam uma vez só. Estorno feito no painel do gateway vira estorno novo da cobrança, lançado pelo valor informado; sem valor, fica registrado sem efeito para investigação (`under_review`) — nunca pelo total. **Taxas.** O evento pode trazer a taxa do gateway, da captura ou de um estorno. O hub lança a diferença entre o total informado e o que já lançou para aquela origem: a mesma taxa repetida não lança nada; a corrigida lança o ajuste. Informação mais antiga que a última aplicada, ou da mesma hora com outro total, fica registrada sem efeito. Taxa que chega antes da captura (ou do estorno) espera e é lançada junto com ela. Taxa de cobrança falha, cancelada, em outra moeda, ou de estorno que falhou fica registrada como divergência, sem lançamento — assim como a taxa malformada (total negativo, moeda fora do formato); taxa de cobrança desconhecida, sem efeito. O desfecho é o do status; um evento sem efeito no status vira `applied` só quando a taxa foi lançada. **Antes da assinatura, uma resposta só (desde a 0.11.1).** Identificador do caminho mal formado, sistema ou conta que não existe, sistema sem conta naquele gateway, gateway desconhecido e assinatura inválida ou ausente respondem o mesmo 401 `invalid-signature`, sem dizer o motivo: quem não assina não descobre o que existe. **Limite de taxa (desde a 0.14.0).** O 429 por origem e o de assinatura inválida (por origem e caminho) saem antes de a assinatura ser conferida; o gateway reenvia depois. Nada é gravado. Parâmetros: - `provider` (path, obrigatório): string - `tenantID` (path, obrigatório): string, formato uuid Corpo (`application/json`): object Respostas: - `200`: evento processado — `application/json` objeto - `outcome` (obrigatório): string (enum: `applied`, `unchanged`, `unknown_charge`, `out_of_order`, `under_review`) — lista aberta — quem lê é o gateway, que ignora o corpo - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `413` (PayloadTooLarge): corpo acima de 1 MiB (1048576 bytes) — `application/problem+json` Problem — type `https://abeesys.com/errors/payload-too-large` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### POST /api/v1/marketplace-webhooks/{accountID} Recebe o aviso do gateway ao marketplace de uma conta da plataforma - operationId: `receiveMarketplaceWebhook` - autenticação: sem API key Desde a 0.10.0 (spec 0017). O aviso de uma cobrança criada pelo marketplace da conta da plataforma na conta de um recebedor. O caminho identifica a conta da plataforma, nunca um sistema; a autenticação é a assinatura com o segredo do **marketplace** dela. A cobrança é achada pelo id do gateway, só entre as criadas por este marketplace na conta daquele recebedor; o sistema sai dela. Aviso desta rota nunca mexe em cobrança da conta de um sistema nem da conta da plataforma, e vice-versa. Como nas outras rotas: repetido, fora de ordem ou sem cobrança deste marketplace responde 200 e fica registrado com o desfecho. **Antes da assinatura, uma resposta só (desde a 0.11.1).** Identificador do caminho mal formado, conta que não existe, conta sem marketplace neste gateway e assinatura inválida ou ausente respondem o mesmo 401 `invalid-signature`, sem dizer o motivo: quem não assina não descobre o que existe. **Limite de taxa (desde a 0.14.0).** O 429 por origem e o de assinatura inválida (por origem e caminho) saem antes de a assinatura ser conferida; o gateway reenvia depois. Nada é gravado. Parâmetros: - `accountID` (path, obrigatório): string, formato uuid Corpo (`application/json`): object Respostas: - `200`: aviso processado — `application/json` objeto - `outcome` (obrigatório): string (enum: `applied`, `unchanged`, `unknown_charge`, `out_of_order`, `under_review`) — lista aberta — quem lê é o gateway, que ignora o corpo - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `413` (PayloadTooLarge): corpo acima de 1 MiB (1048576 bytes) — `application/problem+json` Problem — type `https://abeesys.com/errors/payload-too-large` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ### POST /api/v1/platform-webhooks/{accountID} Recebe o webhook de uma conta da plataforma num gateway - operationId: `receivePlatformWebhook` - autenticação: sem API key A conta da plataforma (da Lopes Tech) atende vários sistemas: por isso o caminho identifica a **conta**, nunca um tenant. Sem API key: quem chama é o gateway. A autenticação é a assinatura, verificada com o segredo **daquela conta**, antes de qualquer leitura do corpo. A cobrança é achada pelo identificador que o gateway deu na criação, buscado só entre as cobranças criadas **nesta** conta — nunca por referência ou metadado do corpo. O sistema dono da cobrança sai dela. Evento desta conta nunca mexe em cobrança criada na conta própria de um sistema, e vice-versa. Nos gateways que não assinam o webhook, o evento só move dinheiro depois de confirmado por consulta com a credencial da conta; divergência entre evento e consulta fica registrada sem efeito (`under_review`). Como na rota do tenant: evento repetido, fora de ordem ou sem cobrança desta conta responde 200 e fica registrado com o desfecho; o repetido não produz efeito. **Antes da assinatura, uma resposta só (desde a 0.11.1).** Identificador do caminho mal formado, conta que não existe e assinatura inválida ou ausente respondem o mesmo 401 `invalid-signature`, sem dizer o motivo: quem não assina não descobre o que existe. **Limite de taxa (desde a 0.14.0).** O 429 por origem e o de assinatura inválida (por origem e caminho) saem antes de a assinatura ser conferida; o gateway reenvia depois. Nada é gravado. Parâmetros: - `accountID` (path, obrigatório): string, formato uuid Corpo (`application/json`): object Respostas: - `200`: evento processado — `application/json` objeto - `outcome` (obrigatório): string (enum: `applied`, `unchanged`, `unknown_charge`, `out_of_order`, `under_review`) — lista aberta — quem lê é o gateway, que ignora o corpo - `401` (Problem): erro (RFC 9457) — `application/problem+json` Problem — type `https://abeesys.com/errors/no-route`, `https://abeesys.com/errors/conflict`, `https://abeesys.com/errors/provider-unavailable` - `413` (PayloadTooLarge): corpo acima de 1 MiB (1048576 bytes) — `application/problem+json` Problem — type `https://abeesys.com/errors/payload-too-large` - `429` (RateLimited): acima do limite de taxa — nada foi gravado e nenhum gateway foi chamado — `application/problem+json` Problem — type `https://abeesys.com/errors/rate-limited` ## Avisos que o hub envia ao sistema (webhooks) Quem implementa estas rotas é o sistema cliente; o hub é quem chama. ### webhook chargeUpdated O aviso de mudança de cobrança — quem implementa é o sistema cliente - operationId: `receiveChargeUpdated` - autenticação: assinatura do aviso (ver a descrição) A cada mudança de cobrança, o hub envia um `POST` ao endereço que o sistema cadastrou com quem opera a plataforma (`tenant notify --slug --url https://…`). O cadastro devolve o **segredo de assinatura** do sistema uma vez (`whsec_…`); guarde-o como credencial. Cada sistema recebe só avisos das próprias cobranças, assinados com o próprio segredo. **O que o aviso leva:** o id do aviso, o tipo, o instante da mudança, o status anterior e a cobrança no mesmo formato de `GET /api/v1/charges/{id}` — **sem `payer`**. Quem precisar do pagador consulta a cobrança. O aviso nunca leva credencial, dado de cartão nem resposta do gateway. **Tipos** (lista aberta — ignore com `2xx` o que não conhece): - `charge.updated` — a cobrança mudou de status. - `refund.updated` — um estorno chegou a `succeeded` ou `failed`. Leva também `refund`, o estorno no formato de `GET /api/v1/refunds/{id}`. Um estorno que leva a cobrança a `refunded` produz os dois avisos, nesta ordem: `refund.updated` e `charge.updated`. - `subaccount.payout` — quem opera registrou um repasse do saldo de uma subconta virtual do sistema (feito fora do hub, pela conta da plataforma no gateway), ou o reverso de um. Leva `payout` e **não** leva `charge` nem `previous_status`. - `recipient.link_updated` — desde a 0.10.0: o vínculo de um recebedor foi gravado, ou ficou inativo (a autorização foi desfeita, ou o gateway recusou a renovação). Leva `recipient_id` e `recipient_link`; não leva `charge`. **Assinatura** — padrão [Standard Webhooks](https://www.standardwebhooks.com/), que tem biblioteca pronta em várias linguagens: 1. tire o prefixo `whsec_` do segredo e decodifique o resto em base64: são os 32 bytes da chave; 2. monte o conteúdo assinado: `..` — o corpo **cru**, byte a byte como chegou, antes de qualquer parse; 3. calcule `HMAC-SHA256(chave, conteúdo)` e codifique em base64 (padrão, com `=`); 4. `webhook-signature` traz uma ou mais assinaturas separadas por espaço, cada uma no formato `v1,`; aceite o aviso se **alguma** for igual à sua, comparando em **tempo constante**. **Anti-replay:** recuse o aviso se `webhook-timestamp` estiver a mais de **5 minutos** do seu relógio, para trás ou para a frente — um aviso capturado não pode ser reapresentado depois. Cada tentativa é assinada de novo com o instante do envio, então uma repetição legítima nunca chega com instante velho. **Exemplo que bate** (corpo curto, para caber): ``` segredo whsec_AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA= webhook-id 5f0c1a52-8f5e-4d7a-9a3e-2b1c4d6e8f90 webhook-timestamp 1791115200 corpo {"id":"5f0c1a52-8f5e-4d7a-9a3e-2b1c4d6e8f90","type":"charge.updated"} webhook-signature v1,StqoxgeT0W+w6bY3yMQDrlug4zz1qK/xxAbJGZMPfeA= ``` **Repetição e ordem.** O mesmo aviso pode chegar mais de uma vez (o hub reinicia, a sua resposta se perde): o `id` — igual ao cabeçalho `webhook-id` — não muda entre tentativas; guarde-o e ignore o repetido. Os avisos de uma **mesma cobrança** chegam na ordem em que aconteceram: o seguinte só sai depois de o anterior ser entregue. Avisos de cobranças diferentes não têm ordem entre si. **Resposta.** Qualquer `2xx`, com qualquer corpo, é entregue. Qualquer outra coisa é falha: outro status (inclusive `3xx` — redirecionamento não é seguido), mais de 10 segundos sem resposta, conexão recusada, TLS inválido. Responda rápido e processe depois. **Tentativas.** Depois de uma falha, o hub espera 10 s e tenta de novo, dobrando a espera a cada falha até 1 hora entre tentativas, por até **24 horas** contadas da primeira tentativa. Esgotado o prazo, o aviso fica como **não entregue**, visível a quem opera, que pode pedir o reenvio; os avisos seguintes da mesma cobrança esperam o reenvio, para não chegarem fora de ordem. Avisos de outras cobranças seguem normalmente. **Endereço.** Só `https`. Endereço de rede interna (localhost, IP privado ou link-local, `*.railway.internal`, nome sem domínio) é recusado no cadastro e na hora de conectar, pelo IP resolvido. Parâmetros: - `webhook-id` (header, obrigatório): string, formato uuid — o id do aviso — igual a `id` no corpo; estável entre tentativas - `webhook-timestamp` (header, obrigatório): string, padrão `^[0-9]+$` — o instante **do envio**, em segundos desde 1970-01-01T00:00:00Z - `webhook-signature` (header, obrigatório): string — `v1,` — uma ou mais, separadas por espaço Corpo (`application/json`): Notice - `id` (obrigatório): string, formato uuid — o id do aviso — estável entre tentativas; o sistema deduplica por ele - `type` (obrigatório): string — O tipo do aviso: `charge.updated`, `refund.updated`, `subaccount.payout` ou `recipient.link_updated`. **Lista aberta**: ignore (com `2xx`) o tipo que não conhece. `charge` e `previous_status` estão em todo `charge.updated` e `refund.updated`; `payout`, só em `subaccount.payout`; `recipient_id` e `recipient_link`, só em `recipient.link_updated`. - `created_at` (obrigatório): string, formato date-time — o instante da mudança — não o do envio, que vai em `webhook-timestamp` - `previous_status` (opcional): ChargeStatus (enum: `pending`, `authorized`, `captured`, `refunded`, `failed`, `canceled`, `disputed`) — O status da cobrança antes do fato. Em `refund.updated`, é igual ao status de `charge` quando o estorno não mudou a cobrança. - `charge` (opcional): Charge — A cobrança como ficou depois desta mudança, no formato da consulta, sem `payer` e sem `fee` — a taxa não gera aviso, e o retrato não a acompanha. É o retrato daquele momento: dois avisos da mesma cobrança contam a história, não repetem o estado atual. - `refund` (opcional): Refund — Só em `refund.updated` — o estorno como ficou. - `payout` (opcional): Payout — Só em `subaccount.payout` — o repasse como ficou. - `recipient_id` (opcional): string, formato uuid — Só em `recipient.link_updated` — o recebedor do vínculo. - `recipient_link` (opcional): RecipientLink — Só em `recipient.link_updated` — o vínculo como ficou. Respostas: - `2XX`: entregue; o corpo da resposta é ignorado - `default`: falha; o hub tenta de novo com espera crescente ## Schemas ### CreateCharge objeto, sem campos além destes Não há campo de gateway: quem escolhe é o hub. `provider` enviado é 422 (campo desconhecido). - `amount` (obrigatório): integer, formato int64, mínimo 1 — Unidade mínima da moeda. Em cartão parcelado é o **total** — o hub não calcula juros de parcelamento. - `currency` (obrigatório): string, padrão `^[A-Z]{3}$` - `method` (obrigatório): string (enum: `card`, `pix`, `boleto`) - `description` (opcional): string, máximo 500 caracteres, padrão `^[^\x00]*$` — Quebra de linha vale; o caractere NUL (`\\u0000`) é 422. - `payer` (opcional): Payer - `card` (opcional): CardInput - `pix` (opcional): PixInput - `boleto` (opcional): BoletoInput - `reference` (opcional): Reference - `metadata` (opcional): Metadata - `device_id` (opcional): string, máximo 255 caracteres, padrão `^[^\x00-\x1F\x7F-\x9F]*$` — Identificador antifraude do dispositivo do pagador, quando o gateway usa um. O hub o repassa sem interpretar e não o guarda. - `split` (opcional): SplitInput ### SplitInput objeto, sem campos além destes Desde a 0.10.0. A parte do recebedor; o resto do valor é a comissão do sistema. No percentual, a parte é o piso de `amount × percentage_bps / 10000` e o resto do arredondamento fica na comissão — parte + comissão = valor, sempre. - `recipients` (obrigatório): lista de objeto, sem campos além destes, de 1 a 1 itens - `recipient_id` (obrigatório): string, formato uuid - `amount` (opcional): integer, formato int64, mínimo 1 — a parte do recebedor, em unidade mínima, até o valor da cobrança - `percentage_bps` (opcional): integer, de 1 a 10000 — a parte do recebedor em pontos-base (10000 = 100%) - `fee_payer` (opcional): string, padrão `recipient` (enum: `recipient`) — Quem arca com a taxa do gateway. Hoje só `recipient`: a taxa é debitada na conta do recebedor, onde a cobrança nasce. ### SplitDetails objeto - `recipients` (obrigatório): lista de objeto - `recipient_id` (obrigatório): string, formato uuid - `amount` (obrigatório): integer, formato int64 — a parte pedida do recebedor - `commission` (obrigatório): integer, formato int64 — a comissão pedida - `applied_commission` (opcional): integer, formato int64 — A comissão que o gateway diz ter aplicado, quando informa. Diferente da pedida, é ela que o ledger lança — a diferença fica visível aqui. - `fee_payer` (obrigatório): string (enum: `recipient`) - `refunded_recipient_amount` (opcional): integer, formato int64 — Desde a 0.11.0. O total já devolvido da parte do recebedor pelos estornos confirmados. Com `refunded_commission`, soma o `refunded_amount` da cobrança. - `refunded_commission` (opcional): integer, formato int64 — Desde a 0.11.0. O total já devolvido da comissão pelos estornos confirmados. ### CreateRecipient objeto, sem campos além destes - `name` (obrigatório): string, de 1 a 100 caracteres - `reference` (obrigatório): string, de 1 a 255 caracteres — o id do recebedor no seu sistema — única por sistema ### Recipient objeto - `id` (obrigatório): string, formato uuid - `name` (obrigatório): string - `reference` (obrigatório): string - `links` (obrigatório): lista de RecipientLink - `created_at` (obrigatório): string, formato date-time ### RecipientLink objeto O vínculo, sem a conta do recebedor no gateway e sem credencial. - `id` (obrigatório): string, formato uuid - `provider` (obrigatório): string - `status` (obrigatório): string — **Lista aberta.** `active`: o gateway entra na escolha das vendas deste recebedor. - `linked_at` (obrigatório): string, formato date-time - `deactivated_at` (opcional): string, formato date-time - `deactivation_reason` (opcional): string — **Lista aberta.** `revoked`: o recebedor desfez a autorização no gateway. `renewal_lost` (desde a 0.10.1): a autorização venceu porque a renovação do acesso se perdeu do lado do hub, sem o recebedor ter desfeito nada — ele precisa autorizar de novo. ### CreateRecipientLink objeto, sem campos além destes - `provider` (obrigatório): string - `return_url` (obrigatório): string, máximo 2048 caracteres, padrão `^https://[^/\s][^\s]*$` — para onde o recebedor volta, com `status=linked` ou `status=failed` ### RecipientLinkAuthorization objeto - `authorization_url` (obrigatório): string, formato uri - `expires_at` (obrigatório): string, formato date-time ### Payer objeto, sem campos além destes Quem paga. O que é obrigatório depende do gateway e do meio. Dado pessoal: só o tenant dono da cobrança o vê, e o hub não o escreve em log. - `name` (opcional): string, máximo 200 caracteres, padrão `^[^\x00-\x1F\x7F]*$` - `email` (opcional): string, formato email, máximo 254 caracteres - `document` (opcional): Document - `phone` (opcional): string, padrão `^\+[1-9][0-9]{7,14}$` — E.164, com o código do país. O exemplo é fictício. - `address` (opcional): Address ### Document objeto, sem campos além destes - `type` (obrigatório): string (enum: `cpf`, `cnpj`) - `number` (obrigatório): string, padrão `^[0-9A-Z]{11,14}$` — Sem pontuação. CPF com 11 dígitos; CNPJ com 14 posições, inclusive no formato alfanumérico da Receita. O dígito verificador é conferido: inválido é 422. Os exemplos são números fictícios de teste, de ninguém. ### Address objeto, sem campos além destes Para `country` = `BR`, `state` é a UF (duas letras) e `postal_code` o CEP com 8 dígitos, sem hífen. - `street` (obrigatório): string, máximo 200 caracteres, padrão `^[^\x00-\x1F\x7F]*$` - `number` (obrigatório): string, máximo 20 caracteres, padrão `^[^\x00-\x1F\x7F]*$` - `complement` (opcional): string, máximo 100 caracteres, padrão `^[^\x00-\x1F\x7F]*$` - `district` (obrigatório): string, máximo 100 caracteres, padrão `^[^\x00-\x1F\x7F]*$` - `city` (obrigatório): string, máximo 100 caracteres, padrão `^[^\x00-\x1F\x7F]*$` - `state` (obrigatório): string, máximo 64 caracteres, padrão `^[^\x00-\x1F\x7F]*$` - `postal_code` (obrigatório): string, máximo 16 caracteres, padrão `^[0-9A-Za-z -]*$` - `country` (obrigatório): string, padrão `^[A-Z]{2}$` — ISO 3166-1 alfa-2 ### CardInput objeto, sem campos além destes - `form_id` (obrigatório): string, formato uuid — O `id` devolvido por `POST /api/v1/card-forms`. Diz ao hub de qual gateway é o token. Formulário inexistente, de outro sistema, ou de um gateway cuja conta foi desconectada: 422 neste campo. - `token` (obrigatório): string, máximo 255 caracteres, padrão `^[^\x00-\x1F\x7F]*$` — Gerado pelo formulário do gateway no navegador do pagador. Uso único. - `installments` (opcional): integer, de 1 a 24, padrão `1` — Cada gateway aceita até um número próprio; acima dele, 422. - `return_url` (opcional): string, formato uri, máximo 2048 caracteres, padrão `^https://[^/\x00-\x20\x7F-\x9F][^\x00-\x20\x7F-\x9F]*$` — Desde a 0.9.0. Para onde o pagador volta depois de uma autenticação extra do emissor (3-D Secure): uma página do sistema, em `https`, com host, sem espaço nem caractere de controle. Obrigatório quando o formulário declarou `return_url_required` (422 neste campo se faltar); para os outros gateways, aceito e ignorado. O gateway pode acrescentar parâmetros dele ao endereço na volta; o resultado da cobrança chega pelo aviso, não pela volta. ### PixInput objeto, sem campos além destes - `expires_in` (opcional): integer, de 1 a 2592000 — Validade do código, em segundos. Ausente: o padrão do gateway. Fora da faixa que o gateway aceita, 422. ### BoletoInput objeto, sem campos além destes - `due_date` (opcional): string, formato date — Vencimento. Ausente: o padrão do gateway. Fora da faixa que o gateway aceita (contada em dias a partir de hoje, em UTC), 422. ### Reference string, máximo 255 caracteres, padrão `^[^\x00-\x1F\x7F-\x9F]*$` A referência do pedido no sistema cliente (o ingresso reservado, a mensalidade). O hub não a interpreta e ela **não é única**: várias cobranças do mesmo pedido — uma que falhou, outra que passou — têm a mesma referência, e a listagem por referência devolve todas. Texto vazio equivale a ausente. Na resposta e no aviso está sempre presente: texto vazio quando a cobrança não tem referência. ### Metadata object Pares chave-valor que o sistema quer ver de volta em toda leitura e em todo aviso da cobrança. Até 20 chaves; chave com 1 a 40 caracteres, valor com até 500, ambos texto sem caractere de controle. Acima disso, 422 nomeando o campo (`metadata` ou `metadata.`). **Não é lugar para dado de cartão nem dado pessoal do pagador** (documento, e-mail, telefone, nome): o pagador tem campo próprio, com o tratamento que dado pessoal exige. Metadados são guardados e repetidos nos avisos como vieram. Objeto vazio equivale a ausente. Na resposta e no aviso está sempre presente: objeto vazio quando a cobrança não tem metadados. ### PageMeta objeto - `total` (obrigatório): integer, mínimo 0 — quantas cobranças atendem aos filtros - `page` (obrigatório): integer, mínimo 1 - `page_size` (obrigatório): integer, de 1 a 100 ### ChargeStatus string (enum: `pending`, `authorized`, `captured`, `refunded`, `failed`, `canceled`, `disputed`) ### Charge objeto - `id` (obrigatório): string, formato uuid - `settles_in` (obrigatório): string — Onde a cobrança liquida — informação, desde a 0.8.0. **Lista aberta.** `tenant_account`: na conta do próprio sistema no gateway (o saldo aparece em `GET /api/v1/wallets`). `virtual_subaccount`: na conta da plataforma naquele gateway, alocado na subconta virtual do sistema (`GET /api/v1/subaccounts`). `recipient_account` (desde a 0.10.0): na conta do recebedor da divisão; só a comissão vai à subconta virtual. Não muda depois de a cobrança chegar ao gateway. - `provider` (obrigatório): string — O gateway que atende a cobrança — informação para suporte e conciliação, não para o sistema decidir nada. Enquanto `pending` depois de um 502, é o gateway que não respondeu. Em `failed` com `failure_reason` `provider_unavailable`, é o último gateway que recusou — nenhum deles criou a cobrança. - `external_id` (opcional): string — id no gateway - `amount` (obrigatório): integer, formato int64 - `currency` (obrigatório): string - `method` (obrigatório): string (enum: `card`, `pix`, `boleto`) - `status` (obrigatório): ChargeStatus (enum: `pending`, `authorized`, `captured`, `refunded`, `failed`, `canceled`, `disputed`) - `description` (obrigatório): string - `reference` (obrigatório): Reference - `metadata` (obrigatório): Metadata - `payer` (opcional): Payer — Só na consulta, na listagem e na resposta da criação — **nunca no aviso**: o dado pessoal do pagador não sai do hub por um canal que repete a mensagem por até 24 h. - `card` (opcional): CardDetails - `pix` (opcional): PixInstructions - `boleto` (opcional): BoletoInstructions - `split` (opcional): SplitDetails — Só na cobrança dividida (desde a 0.10.0). - `failure_reason` (opcional): string — Só em `failed`. Vocabulário do hub, igual em todo gateway, e **lista aberta** — trate valor desconhecido como recusa genérica. Hoje: `card_declined`, `insufficient_funds`, `card_token_invalid` (token expirado, já usado ou desconhecido), `payer_action_failed` (3-D Secure não concluído), `provider_rejected` (recusa sem motivo mais específico), `provider_unavailable` (todos os gateways possíveis recusaram antes de criar; nada foi cobrado). - `pix_copy_paste` (opcional): string — só em cobrança pix. Use `pix.copy_paste`. - `refunded_amount` (opcional): integer, formato int64, mínimo 0 — Quanto já foi estornado e lançado no ledger: a soma dos estornos `succeeded`. Não inclui disputa perdida. Na resposta está sempre presente (0 sem estorno). - `pending_refund_amount` (opcional): integer, formato int64, mínimo 0 — Quanto está reservado por estornos `pending`: não pode ser pedido de novo até eles se resolverem. Na resposta está sempre presente (0 sem estorno pendente). - `fee` (opcional): ChargeFee — A taxa do gateway (spec 0008). Só em cobrança que chegou a ser capturada (`captured`, `disputed`, `refunded`) — antes disso não há taxa a esperar. **Não vem no aviso**: consulte a cobrança. - `created_at` (obrigatório): string, formato date-time - `updated_at` (obrigatório): string, formato date-time ### ChargeFee objeto - `status` (obrigatório): string — **Lista aberta.** `pending`: o gateway ainda não informou a taxa da captura — ela **não** é zero, é desconhecida. `posted`: informou, e o total está lançado (pode ser 0, se o gateway não cobrou). - `amount` (obrigatório): integer, formato int64 — O lançado de taxa da cobrança, todas as origens somadas (captura e estornos), na moeda da cobrança. 0 enquanto nada foi lançado. - `currency` (obrigatório): string - `origins` (obrigatório): lista de objeto — O lançado por origem, para as origens com taxa lançada. A soma é `amount`. A taxa de um estorno não volta quando o estorno acontece: ela é dele, separada da taxa da captura. - `origin` (obrigatório): string — **Lista aberta.** `capture` (a captura), `refund` (um estorno) ou, desde a 0.9.0, `dispute` (a tarifa que o gateway cobra pela contestação da cobrança, quando cobra; uma origem por cobrança, sem `refund_id`). - `refund_id` (opcional): string, formato uuid — só em `refund` — o estorno que é a origem da taxa - `amount` (obrigatório): integer, formato int64 ### CardFormRequest objeto, sem campos além destes - `amount` (obrigatório): integer, formato int64, mínimo 1 — unidade mínima da moeda - `currency` (obrigatório): string, padrão `^[A-Z]{3}$` - `installments` (opcional): integer, de 1 a 24, padrão `1` - `recipient_id` (opcional): string, formato uuid — desde a 0.10.0 — o recebedor da cobrança dividida; o formulário é o da conta dele ### CardForm objeto - `id` (obrigatório): string, formato uuid — vai de volta em `card.form_id` - `provider` (obrigatório): string — O gateway do formulário — informação; o sistema não decide nada com ela. - `public_key` (obrigatório): string — Chave pública (publicável) do formulário do gateway. Não é segredo. - `max_installments` (obrigatório): integer, de 1 a 24 - `payer_fields` (obrigatório): lista de string (enum: `payer.name`, `payer.email`, `payer.document`, `payer.phone`, `payer.address`) — Os campos do pagador que este gateway exige para cartão. - `return_url_required` (obrigatório): boolean — Desde a 0.9.0. `true`: o gateway deste formulário exige `card.return_url` na cobrança — para onde o pagador volta depois de uma autenticação extra do emissor (3-D Secure). Sem ele, 422 em `card.return_url`. - `created_at` (obrigatório): string, formato date-time ### CardDetails objeto - `installments` (obrigatório): integer, de 1 a 24 - `action_url` (opcional): string, formato uri — Só enquanto `pending`, quando o gateway exige uma ação do pagador (3-D Secure). O sistema leva o pagador até ela; o resultado chega pelo webhook. ### PixInstructions objeto - `copy_paste` (obrigatório): string — o código "copia e cola" - `qr_code_image` (opcional): string — Imagem do QR — URL `https` ou data URI (`data:image/png;base64,...`). - `expires_at` (opcional): string, formato date-time ### BoletoInstructions objeto - `digitable_line` (obrigatório): string — linha digitável - `url` (opcional): string, formato uri — endereço do boleto para o pagador - `due_date` (opcional): string, formato date ### Notice objeto - `id` (obrigatório): string, formato uuid — o id do aviso — estável entre tentativas; o sistema deduplica por ele - `type` (obrigatório): string — O tipo do aviso: `charge.updated`, `refund.updated`, `subaccount.payout` ou `recipient.link_updated`. **Lista aberta**: ignore (com `2xx`) o tipo que não conhece. `charge` e `previous_status` estão em todo `charge.updated` e `refund.updated`; `payout`, só em `subaccount.payout`; `recipient_id` e `recipient_link`, só em `recipient.link_updated`. - `created_at` (obrigatório): string, formato date-time — o instante da mudança — não o do envio, que vai em `webhook-timestamp` - `previous_status` (opcional): ChargeStatus (enum: `pending`, `authorized`, `captured`, `refunded`, `failed`, `canceled`, `disputed`) — O status da cobrança antes do fato. Em `refund.updated`, é igual ao status de `charge` quando o estorno não mudou a cobrança. - `charge` (opcional): Charge — A cobrança como ficou depois desta mudança, no formato da consulta, sem `payer` e sem `fee` — a taxa não gera aviso, e o retrato não a acompanha. É o retrato daquele momento: dois avisos da mesma cobrança contam a história, não repetem o estado atual. - `refund` (opcional): Refund — Só em `refund.updated` — o estorno como ficou. - `payout` (opcional): Payout — Só em `subaccount.payout` — o repasse como ficou. - `recipient_id` (opcional): string, formato uuid — Só em `recipient.link_updated` — o recebedor do vínculo. - `recipient_link` (opcional): RecipientLink — Só em `recipient.link_updated` — o vínculo como ficou. ### CreateRefund objeto, sem campos além destes - `amount` (obrigatório): integer, formato int64, mínimo 1 — Unidade mínima da **moeda da cobrança**. Igual ao valor da cobrança é estorno total; menor, parcial. - `reason` (obrigatório): string (enum: `requested_by_customer`, `duplicate`, `fraudulent`, `other`) — Lista **fechada** na entrada: fora dela, 422. Pedido do pagador, cobrança duplicada, suspeita de fraude, outro. - `description` (opcional): string, máximo 255 caracteres, padrão `^[^\x00-\x1F\x7F-\x9F]*$` — Do sistema: fica no hub e volta na consulta. **Nunca é enviada ao gateway** — não ponha dado do pagador aqui. ### Refund objeto - `id` (obrigatório): string, formato uuid - `charge_id` (obrigatório): string, formato uuid - `amount` (obrigatório): integer, formato int64, mínimo 0 — O valor do estorno, na moeda da cobrança: o pedido, ou o informado pelo gateway no estorno feito no painel. 0 só em `under_review` (o gateway não informou o valor). - `currency` (obrigatório): string - `status` (obrigatório): string — **Lista aberta.** `pending` (reserva o valor; aguarda o gateway), `succeeded` (confirmado e lançado), `failed` (recusado pelo gateway, com `failure_reason`; a reserva é liberada), `under_review` (estorno do painel sem valor informado: registrado sem efeito, para investigação — não reserva, não lança). - `origin` (obrigatório): string — **Lista aberta.** `api` (pedido por esta API) ou `provider_dashboard` (feito no painel do gateway, fora do hub). - `reason` (opcional): string — **Lista aberta** na saída: `requested_by_customer`, `duplicate`, `fraudulent`, `other`. Ausente em estorno do painel quando o gateway não informa. - `description` (obrigatório): string — a descrição do pedido; texto vazio quando não houve - `failure_reason` (opcional): string — Só em `failed`. **Lista aberta**: `refund_window_expired` (prazo do gateway vencido), `insufficient_balance` (saldo insuficiente na conta), `refund_not_allowed`, `provider_rejected` (recusa sem motivo mais específico). - `external_id` (opcional): string — o id do estorno no gateway, quando conhecido - `created_at` (obrigatório): string, formato date-time - `updated_at` (obrigatório): string, formato date-time - `confirmed_at` (opcional): string, formato date-time — só em `succeeded` — quando o hub registrou a confirmação do gateway - `split` (opcional): RefundSplit — Desde a 0.11.0. Só no estorno `succeeded` de cobrança dividida: quanto do valor lançado saiu da parte do recebedor e quanto saiu da comissão. ### RefundSplit objeto - `recipient_amount` (obrigatório): integer, formato int64, mínimo 0 — a parte do recebedor devolvida por este estorno - `commission` (obrigatório): integer, formato int64, mínimo 0 — a comissão devolvida por este estorno; as duas partes somam o valor lançado ### Wallet objeto - `provider` (obrigatório): string - `balance` (obrigatório): integer, formato int64 — O saldo: a soma das partidas da carteira (créditos menos débitos). Desde a 0.7.0, com as taxas do gateway lançadas, é o **líquido** — `gross − fees`, no centavo — e bate com o que o gateway entrega à conta do sistema. - `currency` (obrigatório): string - `gross` (obrigatório): integer, formato int64 — o bruto — capturado menos estornado menos disputa perdida - `fees` (obrigatório): integer, formato int64 — As taxas do gateway lançadas, correções incluídas (taxa paga). - `pending_fee_charges` (obrigatório): integer, mínimo 0 — Quantas cobranças capturadas neste gateway e moeda ainda não têm a taxa da captura informada. Informação: a taxa delas não está em `fees` nem em `balance`. - `pending_fee_gross` (obrigatório): integer, formato int64 — o bruto dessas cobranças (capturado menos estornado menos disputa perdida) ### Subaccount objeto - `provider` (obrigatório): string — o gateway da conta da plataforma em que o dinheiro está - `currency` (obrigatório): string - `balance` (obrigatório): integer, formato int64 — A soma dos lançamentos da subconta: capturado − estornado − disputa perdida − taxas lançadas − repassado. Pode ser negativo (estorno ou disputa depois do repasse). - `pending_fee_charges` (obrigatório): integer, mínimo 0 — Informação, sem lançamento: quantas cobranças desta subconta ainda não têm a taxa da captura informada pelo gateway. A taxa delas é **pendente**, não zero, e não está em `balance`. - `pending_fee_gross` (obrigatório): integer, formato int64 — o bruto dessas cobranças - `open_dispute_charges` (obrigatório): integer, mínimo 0 — Informação, sem lançamento: quantas cobranças desta subconta estão em disputa aberta. O saldo só muda no desfecho. - `open_dispute_amount` (obrigatório): integer, formato int64 — o valor dessas cobranças (capturado − estornado) ### SubaccountEntry objeto - `id` (obrigatório): string, formato uuid - `kind` (obrigatório): string — **Lista aberta.** `charge_captured`, `charge_refunded`, `dispute_lost`, `provider_fee`, `provider_fee_adjustment`, `subaccount_payout` (repasse), `subaccount_payout_reversal` (reverso de um repasse), `platform_adjustment` (ajuste de quem opera) e, desde a 0.11.0, `subaccount_shortfall_coverage` (a plataforma cobre o saldo que a devolução de uma comissão deixaria negativo). Tipo desconhecido: some `amount` como o de qualquer outro. - `amount` (obrigatório): integer, formato int64 — o efeito na subconta, com sinal — positivo entra, negativo sai - `currency` (obrigatório): string - `charge_id` (opcional): string, formato uuid — a cobrança que causou o lançamento, quando há uma - `refund_id` (opcional): string, formato uuid — o estorno que causou o lançamento (ou que é a origem da taxa) - `payout` (opcional): objeto — só nos lançamentos de repasse - `reference` (obrigatório): string — a referência da transferência no extrato da conta no gateway - `transferred_on` (obrigatório): string, formato date - `status` (obrigatório): string — **Lista aberta.** `registered` ou `reversed`. - `counterparty` (obrigatório): objeto - `type` (obrigatório): string — **Lista aberta.** Hoje sempre `platform_account`. - `provider` (obrigatório): string - `created_at` (obrigatório): string, formato date-time ### EntryPageMeta objeto - `total` (obrigatório): integer, mínimo 0 — quantos lançamentos a subconta tem - `page` (obrigatório): integer, mínimo 1 - `page_size` (obrigatório): integer, de 1 a 100 ### Payout objeto - `id` (obrigatório): string, formato uuid - `provider` (obrigatório): string — o gateway da conta da plataforma de onde saiu - `currency` (obrigatório): string - `amount` (obrigatório): integer, formato int64, mínimo 1 - `transferred_on` (obrigatório): string, formato date - `reference` (obrigatório): string - `status` (obrigatório): string — **Lista aberta.** `registered` (o repasse foi registrado) ou `reversed` (um registro errado foi revertido por inteiro; a mesma referência pode voltar com o valor certo). ### LedgerEntry objeto - `id` (obrigatório): string, formato uuid - `charge_id` (obrigatório): string, formato uuid - `kind` (obrigatório): string — O tipo do lançamento. **Lista aberta** (ADR 0010): hoje `charge_captured` (captura), `charge_refunded` (estorno — inteiro ou parcial, com `refund_id`; lançamentos de antes da 0006 não o têm), `dispute_lost` (disputa perdida), `provider_fee` (a taxa do gateway de uma origem — a captura ou um estorno — pelo total que ele informou: débito na carteira) e `provider_fee_adjustment` (a correção que o gateway fez depois na taxa daquela origem, pela diferença: débito na carteira para mais, crédito para menos). Um tipo desconhecido é um lançamento genérico: **some as partidas dele** como as de qualquer outro, senão o saldo calculado diverge do de `GET /api/v1/wallets`. - `refund_id` (opcional): string, formato uuid — Em `charge_refunded`, o estorno que gerou o lançamento. Em `provider_fee` e `provider_fee_adjustment`, o estorno que é a origem da taxa — ausente quando a origem é a captura. - `currency` (obrigatório): string - `postings` (obrigatório): lista de objeto, mínimo 2 itens - `account` (obrigatório): string (enum: `wallet`, `provider_clearing`) - `provider` (obrigatório): string - `direction` (obrigatório): string (enum: `debit`, `credit`) - `amount` (obrigatório): integer, formato int64, mínimo 1 - `created_at` (obrigatório): string, formato date-time ### Problem objeto - `type` (obrigatório): string, formato uri - `title` (obrigatório): string - `status` (obrigatório): integer - `detail` (opcional): string - `errors` (opcional): lista de objeto - `field` (obrigatório): string - `message` (obrigatório): string