# NewPag — API para integração > NewPag é um gateway de pagamentos Pix para o Brasil. Este documento > descreve a API pública em formato legível por modelos de linguagem > (padrão llms.txt), para que assistentes de IA ajudem a integrar. > > Base da API: https://app.newpag.app. As chaves de API são geradas no painel > em Desenvolvedores > Chaves de API. ## Visão geral - Base URL: https://app.newpag.app/api/v1 - Formato: JSON (UTF-8). Valores monetários em CENTAVOS (inteiro). Ex.: R$ 19,90 = 1990. - Datas: ISO 8601 (UTC). - Métodos suportados hoje: Pix (cash-in, com estorno total via API) e saque Pix. Cartão e checkout hospedado vêm a seguir. - Toda resposta traz um `request_id` (`req_…`) no corpo JSON e no header `x-request-id`. Guarde-o nos seus logs: ele identifica a requisição na tela "Logs da API" do painel (Integrações > Logs da API, retenção 30 dias) e no suporte. Requisições autenticadas ficam registradas lá com status, duração e código de erro — útil para depurar a integração sem abrir chamado. ## Autenticação HTTP Basic auth: usuário = chave pública (`pk_live_…`), senha = segredo (`sk_live_…`). Gere no painel; a pública fica sempre visível e o segredo é mostrado uma única vez. ``` Authorization: Basic base64(pk_live_...:sk_live_...) # cURL: curl ... -u "pk_live_...:sk_live_..." ``` - A credencial identifica a SUA empresa — cada cobrança é criada para a sua conta. - Nunca exponha o segredo no navegador nem em repositórios versionados. - Revogue credenciais comprometidas no painel (revogação é imediata). - Legado: `Authorization: Bearer sk_live_...` (só o segredo) ainda funciona (aceita apenas segredos nossos: `sk_live_...`/`sk_test_...`). - Allowlist de IP (opcional, painel > Chaves de API): se configurada, requisições de IPs fora da lista recebem `401` — mesmo com a credencial correta. ## Modo de teste (sandbox) Crie uma chave de TESTE no painel (Chaves de API > Criar chave > ambiente "Teste") — credenciais `pk_test_…`/`sk_test_…`. Com elas você integra sem mover dinheiro real: - Cobranças de teste ficam num ambiente separado — nunca aparecem no saldo, dashboards ou extrato. Nenhum provedor Pix é chamado; o `pix.copyPaste` retornado é um marcador NÃO-pagável. - Mesmos endpoints e mesmos formatos de resposta. Toda resposta traz `livemode: false` (chaves de produção trazem `livemode: true`). - Simule o pagamento: `POST /api/v1/test/charges/{id}/pay` (só aceita chave de teste). A cobrança vira `paid`, a taxa é calculada com as taxas REAIS da sua conta e o webhook `transaction.paid` é disparado com `livemode: false` no `data` — filtre pelo campo antes de dar baixa em pedidos reais. - Chave de teste pode ser criada ANTES da aprovação do cadastro (KYC) — dá para integrar enquanto a conta está em análise. - Não suportado em teste: `splits` (400), saques/`transfers` (403 `test_mode_unsupported`). `GET /balance` responde zeros com `livemode: false`. ``` # criar cobrança de teste curl -X POST https://app.newpag.app/api/v1/charges \ -u "pk_test_...:sk_test_..." \ -H "Content-Type: application/json" \ -d '{"amountCents":1990,"customer":{"name":"Cliente","email":"c@ex.com","document":"00000000000"}}' # simular o pagamento (dispara o webhook transaction.paid com livemode:false) curl -X POST https://app.newpag.app/api/v1/test/charges/{id}/pay \ -u "pk_test_...:sk_test_..." ``` ## Recursos ### Criar uma cobrança Pix ``` POST /api/v1/charges Authorization: Basic base64(pk_live_...:sk_live_...) Content-Type: application/json Idempotency-Key: pedido-1234 (opcional, recomendado — retry seguro) { "amountCents": 1990, "customer": { "name": "Cliente Exemplo", "email": "cliente@exemplo.com", "document": "00000000000", "phone": "11987654321" }, "description": "Pedido #1234", "external_reference": "pedido-1234" } ``` `customer.phone` (opcional, DDD + número, com ou sem máscara/+55): telefone do pagador. Recomendado — alguns adquirentes exigem; sem ele, usamos o telefone do titular da conta. Telefone inválido → `400 validation`. `external_reference` (opcional, até 120 chars): o ID do pedido NO SEU sistema — volta em toda resposta/webhook da cobrança e é pesquisável (`GET /api/v1/charges?external_reference=pedido-1234`). Não precisa ser único. Resposta `201 Created`: ``` { "id": "clx...", "livemode": true, "status": "pending", "amountCents": 1990, "external_reference": "pedido-1234", "pix": { "copyPaste": "00020101br.gov.bcb.pix...", "qrCodeBase64": "data:image/png;base64,iVBORw0..." }, "createdAt": "2026-07-19T21:00:00.000Z" } ``` Use `pix.copyPaste` para o copia-e-cola e `pix.qrCodeBase64` (PNG data-URL, pronto para ``) para exibir o QR ao pagador — sem gerar QR no seu lado. Ambos vêm no `201` e no `GET /charges/{id}` (a listagem não os inclui). `pix.copyPaste` é um payload **EMV** (padrão BR Code do BCB): uma sequência de campos, cada um com id de 2 dígitos, tamanho de 2 dígitos e valor. O campo `54` é o valor da cobrança quando o QR é de valor fixo; o campo `26` traz a chave Pix. Trate o código como opaco e repasse sem alterar. ### Consultar o status de uma cobrança ``` GET /api/v1/charges/{id} Authorization: Basic base64(pk_live_...:sk_live_...) ``` Resposta `200 OK` com o mesmo formato acima, mais os dados de conciliação (preenchidos quando `status = paid`): ``` { "id": "clx...", "livemode": true, "status": "paid", "amountCents": 1990, "external_reference": "pedido-1234", "description": "Pedido #1234", "customer": { "name": "Cliente Exemplo", "email": "cliente@exemplo.com", "document": "00000000000" }, "payer": { "name": "Fulano de Tal", "document": "10900327570", "bank": "NU PAGAMENTOS - IP" }, "pix": { "copyPaste": "...", "qrCodeBase64": "...", "endToEnd": "E18236120202609130426s1894658118" }, "paidAt": "2026-09-13T04:26:13.927Z", "createdAt": "2026-09-13T04:25:54.018Z" } ``` - `customer` = o que VOCÊ informou ao criar a cobrança. - `payer` = quem DE FATO pagou o Pix (nome, CPF/CNPJ e banco informados pelo PSP na confirmação). `null` enquanto pendente ou se o PSP não informar. - `pix.endToEnd` = End-to-end ID do Pix (identificador único da liquidação no SPI) — use-o com `payer` para conciliar com o extrato bancário. - Documentos vêm completos (só dígitos). A listagem (`GET /api/v1/charges`) traz os mesmos campos, exceto `pix.copyPaste`/`qrCodeBase64`. - `refund` = estorno da cobrança, se houver (`null` sem estorno): `{ id, status: pending|completed|failed, amountCents, reason, createdAt, completedAt, failureReason }`. Só no GET por id — ver "Estornar uma cobrança". Faça polling do `status` até `paid` — ou, melhor, assine o webhook `transaction.paid`. Status possíveis: `pending`, `paid`, `expired`, `canceled`, `refunded`, `under_review`, `blocked`. ### Split de pagamentos (entre sellers do sistema) Envie `splits` no corpo de `POST /api/v1/charges`. Quando o pagamento confirma, cada recebedor é creditado no saldo dele; a reserva incide só sobre o que sobra para você. `recipient_id` = id da empresa recebedora. `PERCENTAGE` usa `percent` (% do líquido); `FIXED` usa `amount` (centavos). Soma dos percentuais ≤ 100. Split inválido → `400`. ``` "splits": [ { "recipient_id": "id-do-seller-B", "type": "PERCENTAGE", "percent": 40 }, { "recipient_id": "id-do-seller-C", "type": "FIXED", "amount": 1500 } ] ``` ### Listar cobranças (filtros + paginação por cursor) ``` GET /api/v1/charges?limit=50&status=paid&external_reference=pedido-1234&from=2026-07-01&to=2026-07-31&starting_after= Authorization: Basic base64(pk_live_...:sk_live_...) ``` Todos os parâmetros são opcionais. `status`: pending|paid|expired|canceled|under_review|refunded|blocked. `from`/`to`: datas ISO. `starting_after`: id da última cobrança da página anterior (cursor). Resposta `200`: `{ "data": [ { id, status, amountCents, description, external_reference, customer, createdAt } ], "has_more": true }`. Enquanto `has_more` for true, repita com `starting_after` = id do último item. ### Saldo ``` GET /api/v1/balance Authorization: Basic base64(pk_live_...:sk_live_...) ``` Resposta `200`: `{ "available": 12345, "reserved": 0, "currency": "BRL" }` (centavos). ### Dados da conta ``` GET /api/v1/company Authorization: Basic base64(pk_live_...:sk_live_...) ``` Resposta `200` (objeto plano; `document` vem mascarado): `{ id, legal_name, trade_name, document, status, kyc_status, balance: { available, reserved, currency }, fees: { pixPercent, pixFixedCents, reservePercent, reserveDays, withdrawalPercent, withdrawalFixedCents }, created_at }`. ### Solicitar saque (Pix out) ``` POST /api/v1/transfers Authorization: Basic base64(pk_live_...:sk_live_...) Content-Type: application/json Idempotency-Key: saque-1234 (opcional, recomendado — retry seguro, evita saque duplicado) { "amount": 500, "pix_key": "12345678909", "pix_key_type": "CPF", "message": "opcional" } ``` **Saque por QR Code** (copia-e-cola Pix, payload EMV) — no lugar de `pix_key`/`pix_key_type`: ``` POST /api/v1/transfers { "qr_code": "00020126580014br.gov.bcb.pix0136...520400005303986540510.005802BR..." } ``` - QR de valor fixo: `amount` é opcional. Se informado, tem que bater com o valor do QR. - QR de valor variável: `amount` é obrigatório (valor em centavos). - Não envie `qr_code` junto com `pix_key`/`pix_key_type`. - O destinatário recebe o valor do QR; a taxa é debitada **por cima** (saldo sai por valor + taxa). No saque por chave, a taxa sai do próprio `amount`. - Só alguns provedores aceitam QR. Em outro, a resposta é `422 transfer_failed` e nada é debitado. - No retorno, `pix_key` traz o código do QR e `pix_key_type` é `qrcode`. `pix_key_type`: `CPF`, `CNPJ`, `EMAIL`, `PHONE` ou `EVP` (chave aleatória, formato UUID). `message` (opcional): observação livre. Resposta `201`: `{ "data": { id, status, amount, net_amount, fee, pix_key, pix_key_type, pending_approval, created_at } }`. Com "saque automático" desligado na conta, o saque nasce pendente de aprovação (`pending_approval: true`). Consulte com `GET /api/v1/transfers/{id}` ou liste em `GET /api/v1/transfers?status=completed&from=...&to=...&starting_after=` — mesma paginação por cursor das cobranças (resposta inclui `has_more`). Envie `Idempotency-Key` (único por saque): no retry, o MESMO saque é devolvido em vez de debitar o saldo de novo. Se o saque via API estiver desabilitado para a sua conta, o retorno é `403 api_withdrawal_disabled`. ### Estornar uma cobrança (refund) Devolve o valor ao pagador via Pix. **Total, uma vez por cobrança**, só para cobrança `paid`, até 90 dias após o pagamento. ``` POST /api/v1/charges/{id}/refund Authorization: Basic base64(pk_live_...:sk_live_...) Content-Type: application/json { "reason": "Cliente desistiu" } (opcional, até 140 caracteres — o pagador pode ver) ``` Resposta `202` (PSP aceitou; a devolução está em processamento): ``` { "id": "clx...", "chargeId": "clx...", "livemode": true, "status": "pending", "amountCents": 1990, "reason": "Cliente desistiu", "createdAt": "...", "completedAt": null, "failureReason": null } ``` O estorno é ASSÍNCRONO. `202 pending` significa que o PSP aceitou o pedido — o dinheiro ainda não voltou. Ouça os webhooks: - `transaction.refunded` — devolução concluída; a cobrança passa a `refunded`. - `transaction.refund_failed` — o PSP recusou; a cobrança continua `paid` e o valor retido volta ao seu saldo. Se a devolução concluir na hora (pagador na mesma instituição), a resposta já vem `200` com `status: "completed"`. Enquanto o estorno está `pending`, o valor líquido fica RETIDO do seu saldo disponível (não pode ser sacado). Repetir o `POST` na mesma cobrança é idempotente: devolve o mesmo estorno, sem "estornar de novo". Um estorno `failed` pode ser pedido de novo. Consulte com `GET /api/v1/charges/{id}/refund` (mesmo objeto; `404` sem estorno) ou pelo campo `refund` de `GET /api/v1/charges/{id}`. Em modo de teste (chave `pk_test_`), o estorno conclui na hora, sem PSP, e a cobrança de sandbox passa a `refunded`. Recusas (`422`): `charge_not_refundable` (não está `paid`), `refund_window_expired` (mais de 90 dias), `insufficient_balance` (saldo não cobre — o corpo traz `shortfallCents`), `refund_not_supported` (o provider desta cobrança não oferece estorno via API). `502 refund_failed`: o PSP recusou ou não respondeu; nada fica retido. ## Erros Respostas de erro usam JSON `{ "error": "", "message": "...", "request_id": "req_..." }` (cite o `request_id` ao abrir chamado — ele aparece também em Integrações > Logs da API): - `401 unauthorized` — chave ausente, inválida, revogada — ou IP fora da allowlist da conta. - `400 validation` — corpo inválido (ver `details`). - `403 account_inactive` — conta em análise ou bloqueada. - `403 api_withdrawal_disabled` — saque via API desabilitado para esta conta. - `403 test_mode_unsupported` — recurso indisponível com chave de teste (ex.: saques). - `403 test_mode_only` — endpoint de simulação chamado com chave de produção. - `404 not_found` — cobrança/saque não encontrado. - `422 transfer_failed` — saque recusado (ex.: saldo insuficiente). - `422 charge_not_refundable` / `refund_window_expired` / `insufficient_balance` / `refund_not_supported` — estorno recusado (ver "Estornar uma cobrança"). - `502 refund_failed` — o PSP recusou ou não respondeu ao estorno; a retenção foi liberada. - `409 idempotency_conflict` — a `Idempotency-Key` já foi usada com um corpo DIFERENTE. A mesma chave só vale para retry do MESMO pedido; use outra chave. - `429 rate_limited` — muitas requisições; aguarde o valor (segundos) do header `retry-after` e tente de novo com backoff. O limite é por conta. Toda resposta autenticada traz os headers `x-ratelimit-limit`, `x-ratelimit-remaining` e `x-ratelimit-reset` (epoch em segundos) — monitore-os para não tomar 429. - `502 charge_failed` — falha ao gerar a cobrança no provedor. ## Exemplo (curl) ``` curl -X POST https://app.newpag.app/api/v1/charges \ -u "pk_live_...:sk_live_..." \ -H "Content-Type: application/json" \ -d '{"amountCents":1990,"customer":{"name":"Cliente","email":"c@ex.com","document":"00000000000"}}' ``` ## Webhooks Cadastre endpoints em Integrações > Webhooks. A cada evento assinado enviamos um `POST` JSON para a sua URL, com os headers `x-webhook-event` e `x-webhook-signature: sha256=`. Reenviamos até 3 vezes; responda `2xx` para confirmar. Corpo: `{ event, data, createdAt }`. `data` dos eventos `transaction.*` (mesmo shape do `GET /api/v1/charges/{id}`): ``` { "event": "transaction.paid", "data": { "id": "clx...", "livemode": true, "amount": 5000, "net": 4670, "fee": 330, "method": "pix", "status": "paid", "external_reference": "pedido-1234", "description": "Pedido #1234", "customer": { "name": "Cliente Exemplo", "email": "cliente@exemplo.com", "document": "00000000000" }, "payer": { "name": "Fulano de Tal", "document": "10900327570", "bank": "NU PAGAMENTOS - IP" }, "pix": { "endToEnd": "E18236120202609130426s1894658118" }, "paidAt": "2026-09-13T04:26:13.927Z" }, "createdAt": "2026-09-13T04:26:14.100Z" } ``` `payer` (quem de fato pagou) e `pix.endToEnd` vêm do PSP na confirmação — são os campos para conciliar com o extrato. Em `transaction.pending` ambos são `null`; `net`/`fee` só existem em `transaction.paid`. Eventos do sandbox trazem `livemode: false` e `payer: null`. Eventos disponíveis: - `transaction.pending` — Aguardando pagamento (cobrança criada). - `transaction.paid` — Pago. - `transaction.refunded` — Reembolsado (a devolução ao pagador concluiu; `data.status` = `refunded`). - `transaction.refund_failed` — Estorno recusado pelo PSP (a cobrança segue `paid`; o valor retido voltou ao saldo). - `withdrawal.processing` — Saque processando. - `withdrawal.completed` — Saque concluído. - `withdrawal.failed` — Saque falhou. MED (contestações Pix — disparados quando o seller tem MED ativo): - `dispute.opened` — infração aberta (responda a defesa no painel > Disputas dentro do prazo). - `dispute.accepted` — defesa aceita (disputa ganha). - `dispute.rejected` — defesa rejeitada (disputa perdida). ### Gerenciar endpoints via API Além do painel, os endpoints podem ser geridos pela própria API (funciona com chave de produção E de teste — o endpoint é o mesmo; eventos de teste chegam com `livemode: false` no `data`): ``` GET /api/v1/webhooks → { "data": [ { id, url, events, status, secret_masked, ... } ] } POST /api/v1/webhooks → cria; corpo: { "url": "https://...", "events": ["transaction.paid"], "description"?: "..." } resposta 201 inclui `secret` (assinatura HMAC) UMA ÚNICA VEZ — guarde-o. PATCH /api/v1/webhooks/{id} → { "active": true|false } pausa/reativa DELETE /api/v1/webhooks/{id} → remove o endpoint ``` Regras: URL obrigatoriamente `https://` e pública (rede interna → `400 invalid_url`); eventos devem ser da lista acima; máximo de 20 endpoints por conta (`400 endpoint_limit`). ## Boas práticas - Trate valores sempre em centavos (inteiros). - Prefira webhooks para saber que uma cobrança foi paga; use polling do status (GET) como alternativa. - Envie `Idempotency-Key` (único por pedido) ao criar: no retry, a MESMA cobrança é devolvida em vez de gerar outro Pix. Reusar a chave com um corpo DIFERENTE retorna `409 idempotency_conflict` (funciona igual no modo de teste). - Use `description` para a sua referência (ex.: "Pedido #1234"); ela fica salva na cobrança. ## Em breve - Checkout hospedado (link de pagamento). - Cartão de crédito, boleto, 3DS e assinaturas (exigem engines próprias).