PaymentLink
POST /v1/payment_links — criar cobrança
Escopo: payment_links:write. Idempotência: obrigatória.
| campo | tipo | obrig. | constraint |
|---|---|---|---|
amount | inteiro | sim | menor unidade da moeda; > 0 |
currency | string | sim | BRL | USD |
reference | string | não | ≤ 255 chars; sua chave de conciliação |
payment_methods | array<string> | não | subconjunto de ["pix","card"]; default ["pix"] |
max_installments | inteiro | não | 1–21; default 12; só se card habilitado |
fee_handling | string | não | absorb | pass_through; default absorb |
expires_in | inteiro | não | segundos até expirar; >= 0 (0 = sem expiração) |
campaign | boolean | não | default false; true mantém o link aberto após cada pagamento e cota FX por sessão |
description | string | não | ≤ 500; nome do produto exibido no checkout |
product_ref | string | não | ≤ 255; SKU/referência de produto (exibido) |
details | string | não | ≤ 500; descrição longa (exibida) |
product_images | array<string> | não | ≤ 5; imagens do produto |
collect_fields | array<string> | não | subconjunto de ["phone","address"]; omitido herda a config do tenant; coleta esses campos do pagador no checkout |
metadata | objeto | não | ≤ 20 chaves; ver Metadata |
O checkout_url é gerado pela plataforma: pay.pgz.link por padrão, e o seu próprio endereço quando você tem marca própria configurada. Use sempre o checkout_url devolvido, nunca monte a URL — o host pode mudar sem aviso e o link montado à mão não abre.
amount + currency são a única denominação que você informa. target_currency, charge_currency e merchant_payout_currency são derivados pela plataforma e devolvidos no response — nunca são input. A cobertura cambial aplica-se sempre que há conversão, independentemente da moeda de denominação do link.
campaign=false cria link de cobrança única: após o primeiro pagamento capturado/liquidado, novas tentativas são recusadas. campaign=true cria link de campanha: aceita múltiplos pagamentos, cada sessão trava o câmbio exibido por 900 segundos, e pagamentos diferentes podem usar câmbios diferentes.
Response 201 — objeto PaymentLink:
{
"id": "pl_9f8c...", "mode": "live",
"status": "active", "checkout_url": "https://pay.pgz.link/pay/pl_9f8c...",
"amount": 250000, "currency": "BRL", "reference": "pedido-8842",
"target_currency": "BRL", "charge_currency": "BRL", "merchant_payout_currency": "USD",
"payment_methods": ["pix"], "max_installments": 1, "fee_handling": "absorb",
"expires_at": 1790000000, "description": "Rolex Submariner", "product_ref": "126610LN",
"details": null, "product_images": [], "collect_fields": [], "metadata": {}, "created": 1789990000
}Erros: unknown_parameter (400), invalid_request (400), missing_idempotency_key (400), invalid_idempotency_key (400), idempotency_key_reuse (409), invalid_api_key (401), insufficient_scope (403), merchant_net_not_positive (400 — ver Proteção de link).
GET /v1/payment_links/{id}
Escopo: payment_links:read. Response 200: objeto PaymentLink. Erros: payment_link_not_found (404).
GET /v1/payment_links
Escopo: payment_links:read. Query: limit, starting_after, status, reference. Response 200: { data, has_more }.
As duas consultas são isoladas pelo merchant e pelo modo da chave. A listagem usa a paginação geral (default 20, máximo 100), ordenada por criação e ID decrescentes; reference é igualdade exata. status filtra active, expired, paid ou suspended, considerando vencimento e pagamento de links únicos antes de limitar a página. campaign no objeto indica se o link é reutilizável. Consulta não trava preço ou câmbio nem inicia pagamento.
POST /v1/distributed_payment_links/preview e /drafts — preparação da distribuição
Escopo: payment_links:write. Idempotência obrigatória. A prévia é uma estimativa por meio e número de parcelas; o preço será recalculado no pagamento. O endpoint /drafts grava somente um rascunho suspended. Seu checkout_url não pode receber pagamento antes da ativação explícita descrita abaixo. Em produção, continua bloqueado até concluir os gates financeiros e humanos do ADR 0056. Não apresente o URL de um rascunho suspenso ao pagador.
Body: amount_cents (inteiro positivo na moeda nominal), currency (BRL ou USD), payment_methods (pix, card, ted, habilitados para o Merchant), fee_handling (absorb ou pass_through), max_installments (1–21 para cartão), description opcional e rules (1–100 partes). Cada regra contém part_id, beneficiary_id, role (recipient ou merchant_reserve) e exatamente um de amount_cents ou percent_bps. A parte precisa ter uma conta Merchant ou um convite de beneficiário confirmado. O restante do líquido pertence ao Merchant; a reserva voluntária usa o ID do próprio Merchant.
payout_currency opcional (BRL ou USD) escolhe a moeda de recebimento. Valor fixo e percentual permanecem na moeda nominal de currency, após as taxas; não representam um líquido fixo na moeda de destino. Referência: ADR 0068. Para moedas distintas, a API aloca primeiro e retorna em cada parte conversion com principal, bruto, taxa e líquido estimados, spot USD/BRL completo e política do Merchant/modo/direção. Ausência ou desativação dessa política retorna 409 conversion_policy_unavailable; cotação inválida retorna 409 conversion_rate_unavailable. Cotação/taxa/líquido enviados pelo cliente são rejeitados. A estimativa não cobra processamento ou comissão novamente. conversion_required: true identifica partes do split em moedas diferentes. O replay conserva a estimativa original, sem garantir essa cotação no pagamento. Quando a integração financeira ainda não foi instalada, a resposta inclui activation_blocker: "mixed_currency_settlement_pending" e a ativação é recusada. Preparar o rascunho nunca cria pagamento ou saldo.
O painel usa o mesmo contrato em /merchant/v1/distributed-links/preview e /merchant/v1/distributed-links/drafts, com sessão Merchant.
O rascunho exige Idempotency-Key também no painel. Link, plano e registro de repetição são atômicos: mesma chave e conteúdo retornam o mesmo id; conteúdo diferente retorna 409 idempotency_conflict. Campos opcionais: product_ref, details, media_urls (até 5), expires_in (0 = sem expiração), campaign e collect_fields (phone, address). A prévia repetida conserva sua validade original; não representa nova cotação nem autorização de pagamento.
POST /v1/distributed_payment_links/{id}/activate — somente sandbox
Escopo: payment_links:write. Idempotency-Key obrigatória. Body estrito: { "confirm": true, "conversion_policy_ids": ["id-da-politica-apresentada"] }. conversion_policy_ids é obrigatório quando há conversão inicial; envie somente os IDs apresentados na prévia, uma vez por direção. Sem conversão, pode omitir ou enviar a lista vazia. No painel: POST /merchant/v1/distributed-links/{id}/activate, com sessão Merchant e o mesmo body. A chave/sessão precisa pertencer ao Merchant do link e ao modo sandbox. Referências: ADR 0066 e ADR 0069.
Revalida a identidade, acesso das partes, reserva própria e composição do líquido para todos os meios e quantidades de parcelas do cartão. Partes do split podem receber em BRL ou USD; valores fixos e percentuais permanecem na moeda da cobrança. A ativação confirma a versão da taxa própria de conversão apresentada. Alteração ou desativação posterior dessa política bloqueia novos pagamentos desse link (409 distribution_conversion_terms_changed); não altera pagamentos já confirmados. Cada sessão fixa sua cotação, validade e recebimentos convertidos antes da chamada ao provedor. Reabrir uma sessão expirada apresenta nova cotação; o repasse não usa a cotação diária da Tesouraria para mudar o valor prometido. Registra autorização imutável e evento de auditoria na mesma transação que ativa o link. A ativação não cria pagamento, caixa ou saldo disponível. Somente depois disso o checkout sandbox pode iniciar um pagamento e bloquear seu snapshot financeiro antes de chamar o provedor de teste.
Response 200: { id, status: "active", mode: "sandbox", activation_ready: true, replayed, checkout_url }. O URL usa o domínio verificado ou subdomínio Merchant, conforme a resolução pública existente. A mesma chave recupera o resultado sem repetir ativação ou auditoria. Outra referência/identidade com a chave usada: 409 idempotency_conflict. Link expirado, pausado após ativação ou acesso alterado: 409 distribution_activation_changed. Ambiente financeiro incompleto: 409 distribution_not_ready. Modo live: 409 distribution_live_not_ready. Um link com status ativo sem a autorização persistida também permanece bloqueado.
Quote — a única superfície externa de preço
O preço de uma cobrança é obtido exclusivamente por este endpoint. Nenhuma taxa, percentual ou câmbio é publicado neste contrato: você pergunta ao endpoint no momento.
POST /v1/quotes — cotar
Escopo: quotes:write. Idempotência: obrigatória.
| campo | tipo | obrig. | constraint |
|---|---|---|---|
amount | inteiro | sim | menor unidade; > 0 |
currency | string | sim | BRL | USD |
payment_method | string | não | pix | ted | card; omitido retorna todos os cenários |
installments | inteiro | não | 1–21; default 12 |
fee_handling | string | não | absorb | pass_through; omitido usa o default da regra de preço |
Também é aceito o shape legado { "payment_intent_id": "pi_..." } com payment_method? e installments?.
Response 201 — objeto Quote quando payment_method é informado:
{
"id": "qt_9f8c...",
"tenant_id": "three-hands",
"mode": "live",
"payment_intent_id": null,
"payment_method": "card",
"installments": 5,
"payer_total": 5000000,
"payer_currency": "BRL",
"receiver_net": 843375,
"receiver_currency": "USD",
"fx": {
"rate": 5.195993,
"base": "USD",
"quote": "BRL",
"locked_at": 1789990000,
"valid_until": 1789990900,
"rate_history_id": 123
},
"breakdown": {
"fee_handling": "absorb",
"total_fee": { "amount": 118905, "currency": "USD" },
"service_fee": { "final_cents": 118905, "final_currency": "USD", "percent_amount_cents": 118905, "fixed_cents": 0, "raw_total_cents": 118905, "min_applied": false, "max_applied": false },
"installment_surcharge": { "amount": 0, "currency": "USD" },
"tax": { "iof_amount_cents": 0, "iof_applicable": false, "iof_enabled": false, "iof_currency": "USD" }
},
"fx_rate_history_id": 123,
"rule_id": "cg_three-hands_5x",
"expires_at": 1789991800,
"signature": "sha256...",
"created_at": 1789990000
}A Quote é válida por 1800 segundos para ser aceita. Essa validade de negociação é diferente da validade interna do FX (fx.valid_until) e da sessão de checkout de 900 segundos. Os valores numéricos acima são ilustrativos de forma, não de preço.
Quando as condições comerciais incluem um acréscimo ao pagamento, ele já integra payer_total. Nesse caso, breakdown.additional_fee contém { "amount": inteiro, "currency": string }, em centavos da moeda de cobrança. Esse campo opcional é separado de total_fee, taxa de serviço e impostos; não deve ser somado novamente ao total. Sempre apresente e utilize os valores retornados pela cotação.
Para Merchants configurados com comissão em todas as vendas, o fluxo direto POST /v1/payments exige quote_id válido, explícito ou associado à sessão de checkout. Ausência retorna 400 representative_quote_required sem iniciar a cobrança. Pagamentos diretos que usam uma cotação preservam suas condições até a expiração. Links de cobrança continuam usando as condições vigentes no checkout. As regras internas de atribuição são configuradas pela plataforma, não pelo pagador.
Quando payment_method é omitido, a resposta contém scenarios.pix, scenarios.ted e scenarios.card[] com cartão de 1x até o limite do tenant. Cada cenário traz quote_id, método, parcelas, total do pagador, líquido do merchant, FX público, expires_at e signature.
Erros: unknown_parameter (400), invalid_request (400), payment_method_unsupported (400), quote_fx_required (400), idempotência, auth.
POST /v1/quotes/{id}/accept — aceitar cotação
Escopo: payment_intents:write.
| campo | tipo | obrig. | constraint |
|---|---|---|---|
name | string | sim | título exibido no checkout; 1–500 chars |
description | string | não | descrição longa exibida no checkout; ≤ 500 chars |
media_urls | array<string> | não | até 5 URLs Poolvo hospedadas |
expires_in | inteiro | não | segundos até expirar; 0 = sem expiração |
campaign | boolean | não | default false; true não grava o FX do quote no link e cota FX por sessão |
reference | string | não | ≤ 255 chars; sua chave de conciliação |
metadata | objeto | não | metadata livre |
payer_country | string | não | ISO 3166-1 alpha-2 |
Cria um PaymentIntent interno e um PaymentLink. O intent usa os valores persistidos no quote: amount, currency, legal_entity_id, settlement_term, fee_strategy e fx_rate_history_id. Link único (campaign=false) usa o fx_rate_history_id do quote, aplica a catraca de 1% no checkout e expira em 24h quando expires_in é omitido. Link de campanha (campaign=true) não grava o FX do quote no link; cada sessão cota e trava o câmbio exibido por 900 segundos. Quote expirado retorna 400 quote_expired; quote de outro tenant retorna 404 quote_not_found; quote já consumido retorna 400 quote_consumed.
Response 201: link de pagamento.
{
"id": "pl_9f8c...",
"url": "https://pay.pgz.link/pay/pl_9f8c...",
"checkout_url": "https://pay.pgz.link/pay/pl_9f8c...",
"amount": 5000000,
"currency": "BRL",
"payment_method": "card",
"installments": 5,
"payer_total": 5000000,
"payer_currency": "BRL",
"receiver_net": 843375,
"receiver_currency": "USD",
"fx_rate_history_id": 123,
"payment_intent_id": "pi_9f8c...",
"expires_at": 1790000000,
"reference": "pedido-8842"
}PaymentIntent
POST /v1/payment_intents — criar intent legado
Escopo: payment_intents:write.
| campo | tipo | obrig. | constraint |
|---|---|---|---|
legal_entity_id | string | não | default: única entidade ativa do tenant |
amount | inteiro | sim | menor unidade; > 0 |
currency | string | sim | BRL | USD |
payer_country | string | não | ISO 3166-1 alpha-2 |
settlement_term | string | não | default: tenants.settlement_window_days como D+N |
fee_strategy | objeto | não | legado |
metadata | objeto | não | metadata livre |
reference | string | não | ≤ 255 chars |
Este endpoint permanece por compatibilidade. O fluxo canônico é POST /v1/quotes seguido de POST /v1/quotes/{id}/accept. quote_id não é aceito neste endpoint e retorna 400 unknown_parameter.
Payment — objeto de conciliação
O Payment é a fonte de verdade da conciliação. Os valores nele são os mesmos produzidos no momento da cobrança, na mesma precisão (centésimo de basis point), lidos da mesma fonte persistida. Nenhuma camada recalcula preço para exibir ou notificar. Uma diferença entre o que o pagador viu no checkout e o que você recebe aqui é bug crítico, não arredondamento.
Todo objeto Payment carrega os campos abaixo; os campos condicionais obedecem à regra da própria linha (podem ser omitidos ou null exatamente quando a linha indica):
| campo | significado para conciliação |
|---|---|
id | identificador do pagamento (pay_...) |
link_id | o PaymentLink que o originou, ou null |
status | estado persistido do pagamento |
reference | sua chave livre, definida na criação do link, devolvida aqui e em todo webhook |
created_at | quando o pagamento foi iniciado (unix) |
updated_at | última atualização (unix; pode ser null) |
amount + currency | alias legado do bruto cobrado |
gross_amount + gross_currency | bruto cobrado do pagador |
fee_amount + fee_currency | taxa total persistida quando disponível |
net_amount + net_currency | líquido do merchant |
fee_handling | absorb | pass_through — quem pagou a taxa de serviço neste pagamento (valor efetivo); independente de breakdown/fees[] |
fx | { rate, base, quote, locked_at, valid_until, rate_history_id } — câmbio travado com cobertura. Existe somente quando há conversão cambial. Sem conversão (ex.: BRL→BRL) o campo fx é omitido — nunca se fabrica objeto FX, rate: 1.0, objeto vazio ou null só para satisfazer shape. Não expõe spot, cobertura ou fonte interna |
payment_method | pix | ted | card |
installments | inteiro quando aplicável, senão null |
failure_code | presente quando status = failed — código Poolvo do motivo (ver Códigos de recusa) |
Códigos de recusa (payment.failed)
Quando um pagamento falha, decline_code é sempre um código semântico da Poolvo. A razão original de qualquer sistema upstream nunca é repassada: é mapeada internamente para um destes códigos, e o que não tem mapeamento conhecido vira payment_declined.
decline_code | significado |
|---|---|
insufficient_funds | saldo/limite insuficiente |
card_declined | recusado pelo emissor |
expired_card | cartão expirado |
invalid_card | dados de cartão inválidos |
card_not_supported | bandeira/tipo não suportado |
authentication_failed | autenticação (3-D Secure) não concluída |
pix_expired | QR expirou sem pagamento |
pix_rejected | PIX rejeitado pelo banco do pagador |
transfer_not_confirmed | comprovante de transferência não confere na conferência |
payment_declined | recusa genérica (motivo sem mapeamento) |
GET /v1/payments/{id}
Escopo: payments:read. Response 200: objeto Payment. Erros: payment_not_found (404).
GET /v1/payments — fechamento de período
Escopo: payments:read. É o endpoint de conciliação de período.
Query:
| campo | tipo | obrig. | descrição |
|---|---|---|---|
from | string | sim | Data inicial em America/Sao_Paulo, YYYY-MM-DD, inclusiva em 00:00:00 |
to | string | sim | Data final em America/Sao_Paulo, YYYY-MM-DD, inclusiva em 23:59:59 |
limit | integer | não | default 20, máximo 100 |
starting_after | string | não | cursor: id do último pagamento recebido na página anterior |
As bordas seguem o calendário bancário brasileiro usado no cockpit: um pagamento às 2026-08-20T22:00:00-03:00 pertence ao dia 2026-08-20, mesmo sendo 2026-08-21 em UTC.
Ordenação: created_at DESC, id DESC. Não há filtro de status: o integrador recebe todos os pagamentos do período e filtra localmente se quiser.
Response 200: { data: Payment[], has_more, next_cursor }.
GET /v1/financial-position — saldo e depósitos conciliados
Escopo: payments:read. Merchant e ambiente vêm exclusivamente da credencial. Endpoint de leitura; não solicita nem executa transferências.
Query opcional: currency (BRL ou USD), kind (release, reserve, unreserve, hold, release_hold, payout, reverse_payout), from e to (Unix seconds, inclusivos, from <= to), source_after e operation_after (ID do último item da respectiva lista). Campos desconhecidos ou filtros inválidos retornam finance_invalid_filters (400).
Response 200: { updated_at, balances, sources, sources_has_more, operations, operations_has_more, reconciliation, unreconciled, deposit_returns }.
updated_at: instante da consulta em Unix seconds.balances: saldo atual porcurrency, em centavos inteiros:pending_cents(confirmado, aguardando liberação),available_cents,reserved_cents,held_cents(contestação),paid_cents(depósitos líquidos das correções)blocked_cents(em revisão, indisponível),in_transit_cents(comprometido em repasses) eadjusted_cents(ajustes manuais confirmados). O disponível exclui compromissos e ajustes. Os filtros de histórico não alteram os saldos. Moedas nunca são somadas ou convertidas implicitamente.sources: até 50 pagamentos reconhecidos, ordenados porid ASC, comid,payment_id,reference,currency,amount_cents,created_at,payment_status,eligible,cash_confirmed,in_transit_centse os saldos em centavos.available_centsexclui compromissos e ajustes.eligible=falseimpede uso do saldo da fonte.operations: até 50 registros, ordenados poroccurred_at DESC, id DESC, comid,kind,amount_cents,currency,reference,reason,occurred_at,created_at,related_operation_id,reversedeallocations.reasonusa uma descrição controlada, sem notas internas. Cada alocação temsource_id,payment_id,amount_cents; retenções também apresentamremaining_cents. Um depósito pode agrupar pagamentos e um pagamento pode compor vários depósitos.reconciliation: contagensunrecognized_count,missing_economics_countelegacy_settled_count.unreconciledtraz até 100 pagamentos antigos capturados ainda não reconhecidos com líquido e moeda conhecidos, compayment_id,reference(pode ser nulo),currencyenet_cents.deposit_returns: até 100 alocações recentes de devolução bancária, comid,operation_id,source_id,payment_id,currency,amount_cents,occurred_aterecorded_at. Não altera o depósito original. O extrato paginado permite consultar todo o histórico de devoluções.
Os valores capturados entram em A liberar; disponibilidade exige entrada integralmente conciliada. Preparação, envio e crédito são estados distintos. Histórico já liquidado não é reaberto automaticamente. Depósitos registram transferências conferidas e correções preservam o registro original. Esta conciliação não altera o status legado de Payment nem emite um novo webhook de liquidação. Filtro kind=payout lista depósitos com suas composições; reversed=true indica registro integralmente revertido.
GET /v1/financial-statement — extrato da posição financeira
Escopo payments:read; Merchant e ambiente vêm da credencial. Somente leitura. Decisão de contrato: ADR-0046.
Query: currency obrigatório (BRL ou USD), from e to opcionais em Unix seconds inclusivos (padrões: 0 e instante da consulta), after opcional com o id do último movimento retornado. Filtros desconhecidos ou período inválido: 400 finance_invalid_filters.
Response 200: { currency, from, to, basis: "recorded_at", opening_cents, credits_cents, debits_cents, closing_cents, entries, has_more }.
opening_cents + credits_cents - debits_cents = closing_cents, por moeda. O saldo representa a obrigação remanescente, incluindo reservas, retenções e valores em repasse. Não representa saldo bancário nem soma depósitos já pagos. As datas do período usam o instante do registro; occurred_at preserva a data efetiva separadamente. Totais abrangem todo o período, não apenas a página.
entries: até 50 movimentos em recorded_at ASC, id ASC, com id, kind, payment_id, amount_cents, delta_cents, recorded_at e occurred_at. kind: recognition, release, reserve, unreserve, hold, release_hold, payout, reverse_payout, bank_return ou manual_adjustment. Movimentos entre compartimentos têm delta_cents=0; depósitos e ajustes reduzem o saldo; reconhecimento e devoluções o aumentam. Nenhum dado interno de Tesouraria é exposto.
fee_handling e o fluxo de reconhecimento de taxa
fee_handling define quem paga a taxa de serviço:
absorb— o pagador paga o valor do produto; a taxa reduz o líquido do merchant. É o default.pass_through— o pagador paga produto + taxa; o merchant recebe o valor cheio.
Em pass_through, a página de checkout exige o reconhecimento explícito do total pelo pagador antes de gerar a cobrança (a plataforma valida que o total reconhecido bate, ao centavo, com o total travado). O reconhecimento é registrado como o evento interno payment.fees_acknowledged. Em absorb não há reconhecimento. Você não lida com esse fluxo pela API — ele é do checkout hospedado; você só recebe o fee_handling efetivo no Payment.
Regras de link
- Moedas:
BRLeUSD. A cobrança de PIX e cartão é liquidada em BRL ao pagador; conversão e cobertura são derivadas. - Expiração:
expires_inem segundos;0= sem expiração. Após expirar, o link não aceita pagamento e emitelink.expired. - Parcelamento:
1–21(só cartão). O default de produto de um link é12(ADR-0006). - Métodos:
pixe/oucard. - Proteção econômica (
absorb): se, no valor informado, o líquido do merchant seria≤ 0após as taxas, a criação falha commerchant_net_not_positive(400). Usepass_throughou aumente o valor.