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 |
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://loja.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": 12, "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).
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 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 Pagooz 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://loja.pgz.link/pay/pl_9f8c...",
"checkout_url": "https://loja.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 Pagooz 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 Pagooz. 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 |
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 }.
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.