PromissePay
Voltar ao painel
API operacional

API PromissePay

Uma API REST para receber e enviar PIX no Brasil. Cobranças com QR Code, saques para qualquer chave, consulta de saldo, contestações do MED e webhooks assinados. Não precisa de SDK: qualquer cliente HTTP funciona.

REST · JSONHTTPS obrigatórioPIX in & outWebhooks assinados (HMAC)
Base URLhttps://api.promisse.com.br
Todos os valores monetários são inteiros em centavos, ou seja, 1490 significa R$ 14,90. A única exceção é POST /balance, que devolve os saldos em reais.

Começando

Início rápido#

Do zero à primeira cobrança paga em três passos.

  1. 1

    Crie uma chave de API

    No painel, em Integrações → Credenciais, gere uma chave sk_live_… e marque os escopos que sua integração vai usar. A chave completa aparece uma única vez.

  2. 2

    Crie uma cobrança PIX

    curl -X POST "https://api.promisse.com.br/transactions" \
      -H "Authorization: sk_live_sua_chave_aqui" \
      -H "Content-Type: application/json" \
      -d '{"amount":1490}'

    Exiba o qrCodeBase64 ou o copyPaste para o pagador:

    {
      "status": "pending",
      "id": "9c1e7b2a-3f4d-4a8b-bc12-5e6f7a8b9c0d",
      "amount": 1490,
      "copyPaste": "00020126580014br.gov.bcb.pix...6304ABCD",
      "qrCodeBase64": "data:image/png;base64,iVBORw0KGgoAAA..."
    }
  3. 3

    Receba a confirmação

    Registre um webhook para o evento payment.approved e confirme o pagamento no seu sistema. O pagamento é assíncrono, então não fique consultando em loop: use o webhook e o apenas como reconciliação.

Começando

Autenticação#

Toda requisição precisa da sua chave de API no header Authorization. A chave vai crua, sem o prefixo Bearer.

Authorization: sk_live_sua_chave_aqui
Chaves sk_live_ só devem existir no seu servidor. Nunca coloque em app mobile, front-end ou repositório público: quem tem a chave pode sacar do seu saldo.
Você pode restringir uma chave a IPs específicos no painel. Com a lista preenchida, requisições de qualquer outro IP são recusadas com IP_NOT_ALLOWED.

Começando

Escopos#

Cada chave carrega uma lista de permissões. Se o endpoint exigir um escopo que a chave não tem, a resposta é 403 FORBIDDEN_SCOPE. Conceda só o necessário.

payments.create

Criar cobranças PIX (POST /transactions).

payments.read

Consultar e listar cobranças (GET /transactions).

transfers.read

Consultar transferências internas.

withdrawals.create

Solicitar saques via API (POST /withdrawals).

withdrawals.read

Consultar saques (GET /withdrawals/:id).

webhooks.manage

Criar, listar, editar e remover webhooks.

Começando

Erros#

Erros usam o status HTTP adequado e trazem sempre um code estável para tratar em código e uma message legível para logs.

{
  "status": "error",
  "code": "INSUFFICIENT_FUNDS",
  "message": "Saldo insuficiente para realizar o saque."
}
CodeHTTPQuando acontece
ACCESS_FORBIDDEN401Chave de API ausente, inválida ou desativada.
IP_NOT_ALLOWED401A chave tem lista de IPs permitidos e a requisição veio de outro IP.
FORBIDDEN_SCOPE403A chave não tem o escopo exigido pelo endpoint.
ACCOUNT_BLOCKED403A conta está bloqueada e não pode gerar novas cobranças.
KYC_REQUIRED403A verificação de identidade precisa ser concluída antes desta operação.
BAD_REQUEST400Campo obrigatório ausente ou valor inválido (ex.: amount abaixo de 50 centavos ou não inteiro).
INVALID_PIX_KEY400A chave PIX informada não corresponde a nenhum formato reconhecido.
INSUFFICIENT_FUNDS400Saldo insuficiente para o saque (considerando valor + taxa e bloqueios por infração).
OUT_LIMIT_EXCEEDED400O valor excede o limite por saque da sua conta.
DAILY_LIMIT_EXCEEDED400O valor excede o limite diário de saques da sua conta.
NOT_FOUND404O recurso consultado não existe ou não pertence à sua conta.
TOO_MANY_REQUESTS429Limite de 45 cobranças por minuto atingido.
WITHDRAWAL_BURST_LOCKED429Mais de 2 saques em 5 segundos. A conta fica travada por 10 segundos.
WITHDRAWAL_QUEUE_STUCK429Existe um saque na fila há mais de 30 segundos. Aguarde a conclusão.
WITHDRAWAL_PENDING_REVIEW429Já existe um saque em análise manual. Só um por vez.
WITHDRAWALS_MAINTENANCE503Saques temporariamente suspensos para manutenção.
INTERNAL_SERVER_ERROR500Falha inesperada. Nenhum valor é movimentado, então pode tentar novamente.

Começando

Limites#

Além destes, sua conta tem limite por saque e limite diário próprios, que você consulta no painel. Ao estourar, a API responde OUT_LIMIT_EXCEEDED ou DAILY_LIMIT_EXCEEDED informando quanto ainda resta.

Cobranças por minuto45 por conta
Valor mínimo (cobrança e saque)50 centavos
Saques simultâneos1 por conta por vez (fila FIFO)
Rajada de saquesmáx. 2 em 5 segundos
Webhooks por conta10
Timeout de entrega do webhook10 segundos
Retenção para contas novassaques de contas com < 15 dias vão para aprovação manual

Referência

Endpoints por recurso

Todos exigem o header Authorization. Substitua sk_live_sua_chave_aqui pela sua chave real.

Cobranças

Receber PIX: criar a cobrança, exibir o QR Code ao pagador e acompanhar o pagamento.

POST/transactionsGET/transactions/:idGET/transactions
POST/transactions

Cria uma cobrança PIX

Cria uma cobrança PIX (depósito) e retorna o QR Code e o código copia-e-cola para o pagador. O valor é sempre em centavos e precisa ser um inteiro.

POSThttps://api.promisse.com.br/transactionsescopo: payments.create
Limite de 45 cobranças por minuto por conta. Ao exceder, a API responde 429 TOO_MANY_REQUESTS.
O valor mínimo é 50 centavos e precisa ser maior que a soma das taxas aplicadas à sua conta.

Parâmetros

amountbodyobrigatório

integerValor em centavos (mín. 50). Ex.: 1490 = R$ 14,90.

webhookbody

stringURL para receber a notificação quando o pagamento for aprovado.

split_emailbody

stringE-mail de outra conta PromissePay para dividir o valor (split).

split_taxbody

integerPercentual destinado ao parceiro no split (padrão 50).

Requisição

curl -X POST "https://api.promisse.com.br/transactions" \
  -H "Authorization: sk_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{"amount":1490}'

Resposta

201 Created
{
  "message": "Transaction created successfully",
  "status": "pending",
  "id": "9c1e7b2a-3f4d-4a8b-bc12-5e6f7a8b9c0d",
  "amount": 1490,
  "qrCodeBase64": "data:image/png;base64,iVBORw0KGgoAAA...",
  "copyPaste": "00020126580014br.gov.bcb.pix...6304ABCD",
  "expiresAt": "2026-06-10T15:30:00.000Z",
  "fee": 44,
  "storeId": "usr_a1b2c3"
}
GET/transactions/:id

Consulta uma cobrança

Retorna os detalhes de uma cobrança (ou saque) pelo seu ID, incluindo o status atual do pagamento. Use como fallback caso não queira depender apenas do webhook.

GEThttps://api.promisse.com.br/transactions/:idescopo: payments.read

Parâmetros

idpathobrigatório

stringID da transação retornado na criação.

Requisição

curl "https://api.promisse.com.br/transactions/9c1e7b2a-3f4d-4a8b-bc12-5e6f7a8b9c0d" \
  -H "Authorization: sk_live_sua_chave_aqui"

Resposta

200 OK
{
  "id": "9c1e7b2a-3f4d-4a8b-bc12-5e6f7a8b9c0d",
  "status": "PAID",
  "type": "DEPOSIT",
  "amount": 1490,
  "fee": 44,
  "storeId": "usr_a1b2c3",
  "payer": { "name": "João Silva", "document": "***.456.789-**" },
  "copyPaste": "00020126580014br.gov.bcb.pix...6304ABCD",
  "qrCodeBase64": "data:image/png;base64,iVBORw0KGgoAAA...",
  "createdAt": "2026-06-10T14:30:00.000Z"
}
GET/transactions

Lista cobranças

Lista as transações da sua conta com paginação. Aceita filtros por status e tipo via query string.

GEThttps://api.promisse.com.br/transactionsescopo: payments.read

Parâmetros

startquery

integerPosição inicial da paginação (padrão 1).

limitquery

integerQuantidade de registros por página (padrão 50).

statusquery

stringFiltra por status (ex.: paid, PENDING).

typequery

stringFiltra por tipo: DEPOSIT ou WITHDRAWAL.

Requisição

curl "https://api.promisse.com.br/transactions?limit=20&status=paid" \
  -H "Authorization: sk_live_sua_chave_aqui"

Resposta

200 OK
{
  "status": "success",
  "total": 134,
  "start": 1,
  "limit": 20,
  "count": 20,
  "transactions": [
    {
      "id": "9c1e7b2a-3f4d-4a8b-bc12-5e6f7a8b9c0d",
      "status": "paid",
      "type": "DEPOSIT",
      "amount": 1490,
      "fee": 44,
      "createdAt": "2026-06-10T14:30:00.000Z"
    }
  ]
}

Saques

Enviar PIX: transferir do seu saldo para qualquer chave e acompanhar a liquidação.

POST/withdrawalsGET/withdrawals/:id
POST/withdrawals

Realiza um saque via PIX

Solicita um saque (transferência PIX) para uma chave de destino. O saque entra na fila de processamento e é enviado ao banco de forma assíncrona. Acompanhe o desfecho pelo webhook ou consultando GET /withdrawals/:id.

POSThttps://api.promisse.com.br/withdrawalsescopo: withdrawals.create
O `id` retornado é o identificador do saque e serve para consulta imediata, mesmo antes de o banco processar.
A fila processa um saque por conta por vez. Empilhar vários é permitido, mas se o mais antigo ficar mais de 30s sem concluir, novos pedidos são recusados com 429 WITHDRAWAL_QUEUE_STUCK.
Contas com menos de 15 dias têm o saque retido para aprovação manual: a resposta é 202 com status `in_review` (veja abaixo).
Mais de 2 solicitações em 5 segundos bloqueiam a conta por 10 segundos (429 WITHDRAWAL_BURST_LOCKED).

Parâmetros

pixKeybodyobrigatório

stringChave PIX de destino: CPF, CNPJ, e-mail, telefone, chave aleatória ou um copia-e-cola.

amountbodyobrigatório

integerValor em centavos (mín. 50). Ignorado quando a chave é um copia-e-cola que já carrega o valor.

webhookbody

stringURL para receber a notificação quando o saque for concluído.

descriptionbody

stringDescrição opcional para identificar o saque.

Requisição

curl -X POST "https://api.promisse.com.br/withdrawals" \
  -H "Authorization: sk_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{"amount":5000,"pixKey":"email@exemplo.com"}'

Resposta

201 Created
{
  "message": "Withdraw request created successfully",
  "status": "pending",
  "code": "WITHDRAWAL_QUEUED",
  "id": "7b3d9a1c-2e5f-4c8a-9d6b-1a2b3c4d5e6f",
  "jobId": "7b3d9a1c-2e5f-4c8a-9d6b-1a2b3c4d5e6f",
  "amount": 5000,
  "pixKey": "email@exemplo.com",
  "pixKeyType": "EMAIL",
  "fee": 30
}

Conta com menos de 15 dias (retido para análise)

202 Accepted
{
  "status": "in_review",
  "code": "WITHDRAWAL_IN_REVIEW",
  "message": "Sua conta tem menos de 15 dias. O saque foi enviado para análise e será processado após a aprovação de um administrador.",
  "amount": 5000,
  "fee": 30
}
GET/withdrawals/:id

Consulta um saque

Retorna o estado atual de um saque. Aceita tanto o `id` da transação quanto o `jobId` devolvido na criação. Funciona inclusive na janela em que o saque ainda está na fila e não chegou ao banco.

GEThttps://api.promisse.com.br/withdrawals/:idescopo: withdrawals.read

Parâmetros

idpathobrigatório

stringID (ou jobId) do saque retornado na criação.

Requisição

curl "https://api.promisse.com.br/withdrawals/7b3d9a1c-2e5f-4c8a-9d6b-1a2b3c4d5e6f" \
  -H "Authorization: sk_live_sua_chave_aqui"

Resposta

201 Created
{
  "id": "7b3d9a1c-2e5f-4c8a-9d6b-1a2b3c4d5e6f",
  "status": "PAID",
  "withdrawStatusId": "Successfull",
  "type": "WITHDRAWAL",
  "amount": 5000,
  "fee": 30,
  "taxes": 30,
  "pixKey": "email@exemplo.com",
  "pixKeyType": "EMAIL",
  "end_to_end": "E12345678202606101430abcdef00001",
  "paidAt": "2026-06-10T14:31:22.000Z",
  "receiver": { "name": "João Silva", "document": "12345678900" }
}

Ainda na fila (não enviado ao banco)

201 Created
{
  "id": "7b3d9a1c-2e5f-4c8a-9d6b-1a2b3c4d5e6f",
  "status": "PENDING",
  "withdrawStatusId": "Pending",
  "amount": 5000,
  "fee": 30,
  "taxes": 30,
  "pixKey": "email@exemplo.com",
  "pixKeyType": "EMAIL",
  "paidAt": null,
  "error": null,
  "receiver": { "name": "N/A", "document": "N/A" }
}

Cripto

Converter saldo em USDT e enviar para uma carteira na rede BNB Smart Chain (BEP-20).

POST/withdrawals/crypto/quotePOST/withdrawals
POST/withdrawals/crypto/quote

Cotação de um saque em USDT

Calcula quanto USDT será entregue para um valor em reais, já com as taxas. Use antes de criar o saque para mostrar o resumo ao usuário — a cotação varia com o mercado.

POSThttps://api.promisse.com.br/withdrawals/crypto/quoteescopo: withdrawals.create
O `amount` é o valor que VIRA CRIPTO. As taxas entram por cima: o total debitado do saldo é `totalDebit`.
O valor em USDT é uma **estimativa**: a cotação é fechada quando a conversora recebe o pagamento, não na criação do saque.
Sem `amount` no corpo, devolve apenas a cotação e os limites — útil para montar o formulário.
`lowLiquidity: true` indica que a conversora está com pouco USDT em carteira e a entrega pode demorar mais.
Rede BNB Smart Chain (BEP-20), token USDT. Não há suporte a outras redes.

Parâmetros

amountbody

integerValor em centavos que será convertido em USDT. Omita para receber só a cotação e os limites.

Requisição

curl -X POST "https://api.promisse.com.br/withdrawals/crypto/quote" \
  -H "Authorization: sk_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{"amount":10000}'

Resposta

200 OK
{
  "status": "success",
  "quote": {
    "rateBRLPerUSD": 5.244,
    "fixedFeeBRL": 300,
    "network": "bsc",
    "token": "USDT"
  },
  "breakdown": {
    "amount": 10000,
    "platformFee": 500,
    "networkFee": 300,
    "totalFees": 800,
    "pixAmount": 10300,
    "totalDebit": 10800,
    "estimatedUSDT": 19.069412
  },
  "lowLiquidity": false,
  "limits": {
    "minAmount": 2000,
    "maxAmount": 100000,
    "markupPercent": 5,
    "networkFeeBRL": 300
  }
}

Saque em cripto indisponível para a conta

503 Service Unavailable
{
  "status": "error",
  "code": "CRYPTO_DISABLED",
  "message": "O saque em criptomoeda está temporariamente indisponível."
}
POST/withdrawals

Realiza um saque em USDT

Mesma rota do saque PIX. Enviando o objeto `crypto` com a carteira de destino, o valor é convertido em USDT e entregue na rede BNB Smart Chain (BEP-20). Não envie `pixKey`: o destino é resolvido pela plataforma.

POSThttps://api.promisse.com.br/withdrawalsescopo: withdrawals.create
**A transferência é irreversível.** Confira o endereço: 42 caracteres começando com `0x`. Endereço errado significa perda total do valor.
O `amount` é o valor que vira cripto; a taxa da plataforma e a taxa de rede são somadas por cima e o total sai do saldo. Consulte POST /withdrawals/crypto/quote antes para saber o débito exato.
Só a rede BEP-20 (BSC) com token USDT. Carteira de outra rede (ERC-20, TRC-20, Solana) não recebe.
A entrega leva cerca de 30 segundos após a liquidação do pagamento. Acompanhe por GET /withdrawals/:id — o campo `crypto.txHash` traz o comprovante na blockchain.
Valem as mesmas regras do saque PIX: fila por conta, retenção de contas novas e limites de frequência.

Parâmetros

amountbodyobrigatório

integerValor em centavos a ser convertido em USDT.

crypto.walletbodyobrigatório

stringEndereço da carteira de destino na rede BEP-20 (0x + 40 caracteres hexadecimais).

webhookbody

stringURL para receber a notificação quando o saque for concluído.

Requisição

curl -X POST "https://api.promisse.com.br/withdrawals" \
  -H "Authorization: sk_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{"amount":10000,"crypto":{"wallet":"0x71C7656EC7ab88b098defB751B7401B5f6d8976F"}}'

Resposta

201 Created
{
  "message": "Withdraw request created successfully",
  "status": "pending",
  "code": "WITHDRAWAL_QUEUED",
  "id": "7b3d9a1c-2e5f-4c8a-9d6b-1a2b3c4d5e6f",
  "jobId": "7b3d9a1c-2e5f-4c8a-9d6b-1a2b3c4d5e6f",
  "amount": 10300,
  "fee": 500,
  "crypto": {
    "wallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
    "network": "bsc",
    "token": "USDT",
    "rateBRLPerUSD": 5.244,
    "estimatedUSDT": 19.069412,
    "amountBRL": 10000,
    "networkFee": 300,
    "ourFee": 500,
    "totalDebit": 10800
  }
}

Endereço inválido ou bloqueado

400 Bad Request
{
  "status": "error",
  "code": "INVALID_WALLET",
  "message": "O endereço deve ter 42 caracteres (recebido: 41)."
}

Fora dos limites

400 Bad Request
{
  "status": "error",
  "code": "BELOW_MINIMUM",
  "message": "Valor mínimo para saque em cripto: R$ 20,00."
}

Conta

Saldo disponível, valores bloqueados e total já movimentado.

GET/feesPOST/balance
GET/fees

Consulta as taxas da conta

Devolve a tabela de taxas configurada para a sua conta e, informando `type` e `amount`, quanto será cobrado naquela operação. Use antes de criar a cobrança ou o saque em vez de replicar o cálculo do seu lado.

GEThttps://api.promisse.com.br/fees
Sem parâmetros, devolve só a tabela de taxas e os limites da conta.
No depósito, `net` é o valor que será creditado no seu saldo depois da taxa.
No saque, `totalDebit` é o que sai do saldo: valor + taxa.
Em `type=crypto` o cálculo é o mesmo de POST /withdrawals/crypto/quote, incluindo a estimativa em USDT.
Todos os valores em centavos, exceto os percentuais.

Parâmetros

typequery

stringOperação a calcular: `deposit`, `withdrawal` ou `crypto`. Omita para receber apenas a tabela.

amountquery

integerValor em centavos. Obrigatório quando `type` é informado.

Requisição

curl "https://api.promisse.com.br/fees?type=deposit&amount=10000" \
  -H "Authorization: sk_live_sua_chave_aqui"

Resposta

200 OK
{
  "status": "success",
  "type": "deposit",
  "amount": 10000,
  "fee": 95,
  "net": 9905,
  "fees": {
    "deposit":    { "percent": 0.6, "fixed": 35 },
    "withdrawal": { "percent": 0,   "fixed": 250 }
  }
}

Sem parâmetros (tabela de taxas)

200 OK
{
  "status": "success",
  "fees": {
    "deposit":    { "percent": 0.6, "fixed": 35 },
    "withdrawal": { "percent": 0,   "fixed": 250 }
  },
  "limits": {
    "in_limit": 100000,
    "out_limit": 100000,
    "out_limitday": 300000
  }
}

Saque em cripto (type=crypto)

200 OK
{
  "status": "success",
  "type": "crypto",
  "amount": 10000,
  "platformFee": 500,
  "networkFee": 300,
  "totalFees": 800,
  "totalDebit": 10800,
  "estimatedUSDT": 19.069412,
  "rateBRLPerUSD": 5.244,
  "network": "bsc",
  "token": "USDT",
  "limits": { "minAmount": 2000, "maxAmount": 100000 }
}
POST/balance

Consulta o saldo da conta

Retorna o saldo disponível, bloqueado e o total movimentado da sua conta. Atenção: diferente dos outros endpoints, os valores aqui vêm em reais, não em centavos.

POSThttps://api.promisse.com.br/balance

Parâmetros

Nenhum parâmetro, apenas o header de autenticação.

Requisição

curl -X POST "https://api.promisse.com.br/balance" \
  -H "Authorization: sk_live_sua_chave_aqui"

Resposta

200 OK
{
  "codeStatus": 200,
  "balance": {
    "id": "usr_a1b2c3",
    "balance_available": 1530.45,
    "balance_locked": 0,
    "balance_infractions": 0,
    "totalBalances": 18420.90,
    "netBalance": 1530.45,
    "fees": { "saque": 30 }
  }
}

Webhooks

Receba os eventos no seu servidor em vez de ficar consultando a API em loop.

GET/webhooksPOST/webhooksPATCH/webhooksDELETE/webhooks

Eventos#

Enviamos um POST com JSON para a sua URL a cada mudança de estado. Configure em Integrações → Webhooks ou pela própria API.

payment.approved

A cobrança PIX foi paga e o valor foi creditado no seu saldo.

payment.failed

A cobrança foi recusada ou expirou sem pagamento.

transfer-approved

O saque foi liquidado pelo banco: o dinheiro chegou ao destino.

transfer-failed

O saque foi recusado (ex.: chave PIX inexistente). O valor é estornado ao seu saldo.

transfer-refunded

O saque foi liquidado, mas o destinatário devolveu o PIX. O valor devolvido volta para o seu saldo.

pix.infraction

Uma infração PIX (MED) foi aberta ou atualizada contra uma transação sua.

Ao cadastrar um webhook enviamos um POST de teste com o corpo {"event":"webhook.test"}. Seu endpoint precisa responder 200, 201 ou 202 nesse teste, senão o cadastro é recusado.

Payload e headers#

O corpo tem sempre o mesmo envelope: event, data e timestamp.

{
  "event": "payment.approved",
  "timestamp": "2026-06-10T14:31:02.418Z",
  "data": {
    "id": "9c1e7b2a-3f4d-4a8b-bc12-5e6f7a8b9c0d",
    "status": "PAID",
    "type": "DEPOSIT",
    "amount": 1490,
    "fee": 44,
    "storeId": "usr_a1b2c3",
    "end_to_end": "E12345678202606101430abcdef00001",
    "payer": { "name": "João Silva", "document": "***.456.789-**" },
    "paidAt": "2026-06-10T14:31:00.000Z"
  }
}

Headers enviados

promisse-signature

HMAC SHA-256 do corpo bruto da requisição, no formato sha256=<hex>. É a forma segura de confirmar que o evento veio de nós.

promisse-webhook-secret

A secret do webhook em texto puro, para uma comparação simples quando você não quiser calcular o HMAC.

Content-Type

Sempre application/json.

Temos 10 segundos de timeout por entrega. Responda 2xx assim que receber e processe o evento em background. Se o seu handler demorar, a entrega é marcada como falha.
O mesmo evento pode chegar mais de uma vez (reenvio manual, retentativa do banco). Trate o handler como idempotente usando o data.id como chave.

Verificar a assinatura#

Sempre valide a assinatura antes de confiar em um evento: sua URL é pública e qualquer um pode chamá-la. O header promisse-signature traz o HMAC SHA-256 do corpo bruto da requisição, usando a secret do webhook como chave.

Calcule o HMAC sobre os bytes exatos que chegaram. Se você desserializar e reserializar o JSON antes de verificar, a assinatura não vai bater.

Node.js (Express)

import crypto from "crypto";
import express from "express";

const app = express();

// IMPORTANTE: use o corpo BRUTO. Se o JSON for reserializado, o HMAC não bate.
app.post("/webhooks/pix", express.raw({ type: "application/json" }), (req, res) => {
  const assinatura = req.headers["promisse-signature"];
  const esperado = "sha256=" + crypto
    .createHmac("sha256", process.env.PROMISSE_WEBHOOK_SECRET)
    .update(req.body)
    .digest("hex");

  // timingSafeEqual evita vazar informação pelo tempo de comparação
  const a = Buffer.from(String(assinatura || ""));
  const b = Buffer.from(esperado);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).send("assinatura inválida");
  }

  const evento = JSON.parse(req.body.toString());
  console.log(evento.event, evento.data.id);

  // Responda 200 rápido. Processe o resto em background.
  res.sendStatus(200);
});

Python (Flask)

import hmac, hashlib, os
from flask import Flask, request

app = Flask(__name__)

@app.post("/webhooks/pix")
def pix_webhook():
    assinatura = request.headers.get("promisse-signature", "")
    esperado = "sha256=" + hmac.new(
        os.environ["PROMISSE_WEBHOOK_SECRET"].encode(),
        request.get_data(),  # corpo BRUTO
        hashlib.sha256,
    ).hexdigest()

    if not hmac.compare_digest(assinatura, esperado):
        return "assinatura inválida", 401

    evento = request.get_json()
    print(evento["event"], evento["data"]["id"])
    return "", 200

PHP

<?php
$corpo = file_get_contents("php://input"); // corpo BRUTO
$assinatura = $_SERVER["HTTP_PROMISSE_SIGNATURE"] ?? "";
$esperado = "sha256=" . hash_hmac("sha256", $corpo, getenv("PROMISSE_WEBHOOK_SECRET"));

if (!hash_equals($esperado, $assinatura)) {
    http_response_code(401);
    exit("assinatura inválida");
}

$evento = json_decode($corpo, true);
error_log($evento["event"] . " " . $evento["data"]["id"]);
http_response_code(200);
GET/webhooks

Lista webhooks

Lista os webhooks configurados na sua conta. A secret é sempre retornada mascarada por segurança.

GEThttps://api.promisse.com.br/webhooksescopo: webhooks.manage

Parâmetros

Nenhum parâmetro, apenas o header de autenticação.

Requisição

curl "https://api.promisse.com.br/webhooks" \
  -H "Authorization: sk_live_sua_chave_aqui"

Resposta

200 OK
{
  "status": "success",
  "webhooks": [
    {
      "id": "665f8a1b2c3d4e5f60718293",
      "url": "https://seusite.com/webhooks/pix",
      "events": ["payment.approved"],
      "secret": "whsec_a1b2...c3d4",
      "active": true,
      "createdAt": "2026-06-01T12:00:00.000Z"
    }
  ]
}
POST/webhooks

Cria um webhook

Registra uma URL para receber eventos. A secret completa é retornada UMA ÚNICA VEZ, nesta resposta. Guarde-a: é ela que valida a assinatura das notificações.

POSThttps://api.promisse.com.br/webhooksescopo: webhooks.manage
Antes de salvar, enviamos um POST de teste para a URL com o corpo `{"event":"webhook.test"}`. Seu endpoint precisa responder 200, 201 ou 202, senão a criação falha com 400 WEBHOOK_TEST_FAILED.
Máximo de 10 webhooks por conta.

Parâmetros

urlbodyobrigatório

stringURL completa (http/https) que receberá os eventos via POST.

eventsbodyobrigatório

string[]Lista de eventos a assinar. Eventos desconhecidos são descartados silenciosamente.

Requisição

curl -X POST "https://api.promisse.com.br/webhooks" \
  -H "Authorization: sk_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://seusite.com/webhooks/pix","events":["payment.approved","transfer-approved"]}'

Resposta

201 Created
{
  "status": "success",
  "webhook": {
    "id": "665f8a1b2c3d4e5f60718293",
    "url": "https://seusite.com/webhooks/pix",
    "events": ["payment.approved", "transfer-approved"],
    "active": true,
    "secret": "a1b2c3d4e5f6...9f0a",
    "createdAt": "2026-06-01T12:00:00.000Z"
  }
}
PATCH/webhooks

Atualiza um webhook

Altera a URL, os eventos assinados ou ativa/desativa um webhook. Envie apenas os campos que quer mudar.

PATCHhttps://api.promisse.com.br/webhooksescopo: webhooks.manage

Parâmetros

webhookIdbodyobrigatório

stringID do webhook a atualizar.

urlbody

stringNova URL completa (http/https).

eventsbody

string[]Nova lista de eventos assinados (substitui a anterior).

activebody

booleanfalse pausa as entregas sem apagar o webhook.

Requisição

curl -X PATCH "https://api.promisse.com.br/webhooks" \
  -H "Authorization: sk_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{"webhookId":"665f8a1b2c3d4e5f60718293","active":false}'

Resposta

200 OK
{
  "status": "success",
  "message": "Webhook atualizado com sucesso"
}
DELETE/webhooks

Remove um webhook

Apaga um webhook definitivamente. Para apenas pausar as entregas, prefere-se PATCH com `active: false`.

DELETEhttps://api.promisse.com.br/webhooksescopo: webhooks.manage

Parâmetros

webhookIdbodyobrigatório

stringID do webhook a remover.

Requisição

curl -X DELETE "https://api.promisse.com.br/webhooks" \
  -H "Authorization: sk_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{"webhookId":"665f8a1b2c3d4e5f60718293"}'

Resposta

200 OK
{
  "status": "success",
  "message": "Webhook removido com sucesso"
}

Infrações (MED)

Contestações abertas por pagadores pelo Mecanismo Especial de Devolução.

GET/infractions
GET/infractions

Lista infrações (MED / contestações)

Lista as infrações PIX do MED (Mecanismo Especial de Devolução) registradas contra a sua conta, com paginação e estatísticas agregadas. Cada infração é enriquecida com o nome do pagador e o ID da transação relacionada.

GEThttps://api.promisse.com.br/infractions

Parâmetros

fromquery

integerÍndice inicial da paginação (padrão 0).

toquery

integerÍndice final da paginação (padrão 10). O range é limitado a 30 itens por requisição.

statusquery

stringFiltra por status (ex.: WAITING_PSP, COMPLETED). Use all ou omita para retornar todos.

Requisição

curl "https://api.promisse.com.br/infractions?from=0&to=10&status=WAITING_PSP" \
  -H "Authorization: sk_live_sua_chave_aqui"

Resposta

200 OK
{
  "status": "success",
  "summary": {
    "totalCount": 3,
    "totalAmount": 14900,
    "waitingPsp": 1,
    "completed": 2
  },
  "range": { "total": 3, "from": 0, "to": 10 },
  "data": [
    {
      "infractionId": "INF-2026-0001",
      "storeId": "usr_a1b2c3",
      "type": "FRAUD",
      "status": "WAITING_PSP",
      "endToEndId": "E12345678202606101430abcdef00001",
      "amount": 1490,
      "fee": 0,
      "bank": "PromissePay",
      "creationDate": "2026-06-10T14:35:00.000Z",
      "reportDetails": "Pagador alega não reconhecer a transação.",
      "payerName": "João Silva",
      "linkedTransactionId": "9c1e7b2a-3f4d-4a8b-bc12-5e6f7a8b9c0d"
    }
  ]
}