Introdução
A API LofyPay é REST, retorna JSON, usa autenticação por chave no body e tem rate-limit por IP e por chave. Esta documentação cobre os 4 endpoints principais.
Você precisa de uma chave de API (gerada no painel) pra fazer qualquer requisição. As chaves têm prefixo sk_live_ (produção) ou sk_test_ (sandbox).
- JSON puro — sem SDK obrigatório
- Respostas em
application/json - Rate limit: 30 req/min por IP, 120 req/min por API key (gateway)
- CORS configurável por origem confiável
Ambientes
Use sk_test_ para testes e sk_live_ para produção. Ambas as chaves apontam para a mesma URL base — o roteamento para sandbox é feito pelo prefixo da chave.
Produção
LIVETráfego real, taxas reais, movimentação de dinheiro de verdade.
https://app.lofypay.com/api/v1Sandbox
TESTPara desenvolvimento e testes de integração. Nenhuma cobrança real é processada.
https://app.lofypay.com/api/v1sk_test_) sinaliza o ambiente.Simular pagamento (sandbox)
No sandbox o PIX nunca é pago de verdade. Pra testar o fluxo completo, marque a transação de teste com o endpoint de simulação — ele atualiza o status e dispara o webhook de teste pra notification_url. Só aceita chaves sk_test_ (chave live retorna 403) e nunca movimenta saldo real.
https://app.lofypay.com/api/v1/sandbox/pay{
"api-key": "sk_test_…",
"idTransaction": "52fc5262-…",
"status": "paid"
}status aceita paid · failed · expired · pending. Também dá pra identificar a transação por external_reference no lugar de idTransaction.
{
"event": "pix.paid",
"environment": "test",
"test": true,
"idTransaction": "52fc5262-…",
"externalReference": "PEDIDO-123456",
"status": "paid"
}X-Lofypay-Environment: test e o payload acima — sem amount/paid_at e com externalReference em camelCase. Use-o pra validar conectividade; o contrato de produção é o da seção Webhooks.Autenticação
A LofyPay aceita a Secret Key (sk_live_… ou sk_test_…) em três lugares — escolha o que for mais cômodo. Nunca exponha em código que roda no navegador.
Ordem de prioridade na leitura:
Authorization: Bearer sk_live_…(recomendado)x-api-key: sk_live_…(alternativa por header)"api-key": "sk_live_…"no body JSON (compat)
curl -X POST https://app.lofypay.com/api/v1/gateway \
-H "Authorization: Bearer sk_live_…sua_secret_key…" \
-H "Content-Type: application/json" \
-d '{ "amount": 100.50, "method": "pix", "client": { "name": "Pedro", "document": "12345678900" } }'{
"api-key": "sk_live_…sua_secret_key…",
"amount": 100.50
}sk_live_ dá acesso total à sua conta — cobranças, saques e configurações. Mantenha-a apenas em variáveis de ambiente do servidor. Se vazou, regenere imediatamente em /gateway.code específico: missing_key, public_key_used (você colou pk_ em vez de sk_), invalid_format, key_not_found ou key_revoked.Gerar PIX
Cria uma cobrança PIX dinâmica e retorna o BR code (copia-e-cola) para exibir no checkout — algumas adquirentes também devolvem o QR Code em base64.
https://app.lofypay.com/api/v1/gatewayParâmetros do body
| Campo | Tipo | Descrição |
|---|---|---|
api-keyobrigatório | string | Sua chave (sk_live_… ou sk_test_…). |
amountobrigatório | number | Valor da cobrança em reais (R$). Ex: 100.50 |
method | enum | Default "pix". |
clientobrigatório | object | Dados do pagador. O objeto é obrigatório; campos ausentes recebem placeholders neutros. |
client.name | string | Nome completo do pagador. Recomendado — sem ele a cobrança é registrada como genérica. |
client.document | string | CPF ou CNPJ do pagador (só dígitos). Recomendado para conciliação e defesa de MED. |
client.email | string | E-mail do pagador. Recomendado para conciliação. |
external_reference | string | ID do pedido no seu sistema (ex: PEDIDO-1050). Volta no webhook. |
notification_url | url | URL (HTTPS recomendado) que recebe um POST automático quando o pagamento confirmar. |
expiration | number | Validade da cobrança em segundos (60–86400). |
split.percentual | number | Percentual do bruto a destinar pra outro user (0–100). |
split.destino | string | user_id do recebedor do split. |
metadata.order_id | string | Alternativa a external_reference. |
Exemplo
{
"api-key": "sk_live_…",
"amount": 100.50,
"method": "pix",
"external_reference": "PEDIDO-123456",
"notification_url": "https://seu-site.com/webhook",
"client": {
"name": "Pedro H. Santos",
"document": "12345678900",
"email": "pedro@exemplo.com"
},
"split": {
"percentual": 10,
"destino": "100042"
}
}curl -X POST https://app.lofypay.com/api/v1/gateway \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"amount": 100.50,
"method": "pix",
"external_reference": "PEDIDO-123456",
"client": {
"name": "Pedro H. Santos",
"document": "12345678900",
"email": "pedro@exemplo.com"
}
}'const res = await fetch("https://app.lofypay.com/api/v1/gateway", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.LOFYPAY_SECRET}`,
},
body: JSON.stringify({
amount: 100.50,
method: "pix",
external_reference: "PEDIDO-123456",
client: {
name: "Pedro H. Santos",
document: "12345678900",
email: "pedro@exemplo.com",
},
}),
});
const data = await res.json();
// data.idTransaction, data.paymentCode (e data.paymentCodeBase64, quando disponível)Resposta (200)
{
"status": "success",
"paymentCode": "00020126580014br.gov.bcb.pix0136123e4567-…",
"idTransaction": "52fc5262-4063-4900-933b-55e6985096a1",
"paymentCodeBase64": "iVBORw0KGgoAAAANSUhEUgAA…",
"environment": "live"
}paymentCodeBase64 é opcional — depende da adquirente que processa a cobrança (e não vem no sandbox). Para cobrir todos os casos, gere o QR Code você mesmo a partir do paymentCode (copia-e-cola). No sandbox, status vem "OK" em vez de "success".
{ "status": "error", "message": "…" }. Antes de exibir o checkout, valide status de sucesso e a presença de idTransaction — não confie só no HTTP status.HTTP 429 com header Retry-After em segundos.Consultar status
Verifica se um pagamento foi aprovado usando o idTransaction retornado na criação. Útil pra polling — mas prefira webhooks quando possível.
https://app.lofypay.com/api/v1/status{
"idtransaction": "52fc5262-4063-4900-933b-55e6985096a1"
}Resposta
{
"status": "PAID_OUT"
}Valores possíveis: WAITING_FOR_APPROVAL PAID_OUT EXPIRED REFUNDED FAILED. Pagamento confirmado = PAID_OUT.
Transação não encontrada (ou body inválido) responde HTTP 400 com { "error": "No matching record found" }.
notification_url na criação do PIX e a LofyPay te avisa exatamente quando o pagamento confirmar — latência tipicamente abaixo de 2 segundos.Webhooks
Dois canais: o notification_url por cobrança (POST simples quando o PIX confirma, sem assinatura) e os endpoints registrados em /gateway (eventos assinados com HMAC).
Canal 1 — notification_url (por cobrança)
Quando você passa notification_url no payload de criação, a LofyPay dispara um POST pro seu servidor assim que o pagamento confirma. Esse canal envia apenas Content-Type: application/json — não é assinado.
Payload recebido
{
"status": "PAID",
"idTransaction": "52fc5262-4063-4900-933b-55e6985096a1",
"amount": 100.50,
"external_reference": "PEDIDO-123456",
"paid_at": "2026-05-20 14:30:12"
}/api/v1/status com o idTransaction recebido e só libere se voltar PAID_OUT.Canal 2 — Endpoints registrados & eventos
Além do notification_url por cobrança (que sinaliza só o pagamento confirmado), você pode cadastrar endpoints de webhook em /gateway e assinar os eventos que quiser. Todo evento desse canal vai assinado (HMAC-SHA256 em X-LofyPay-Signature) com o secret do endpoint — ideal para quem usa a LofyPay como adquirente e precisa repassar os eventos à própria base.
| Campo | Tipo | Descrição |
|---|---|---|
payment.approved | evento | Cobrança confirmada (PIX pago). |
payment.refunded | evento | Cobrança estornada/devolvida — o crédito foi revertido. |
med.opened | evento | Abriu um MED (infração PIX / devolução BACEN) sobre uma transação sua. |
med.updated | evento | MED mudou de status (ex: em análise, defesa enviada). |
med.resolved | evento | MED encerrado. Campo resolution: lost (procedente) ou kept (improcedente). |
Headers enviados (canal registrado)
| Campo | Tipo | Descrição |
|---|---|---|
Content-Type | string | application/json |
X-LofyPay-Event | string | Tipo do evento (ex: payment.approved). |
X-LofyPay-Timestamp | string | Unix (segundos) de quando o evento foi assinado. |
X-LofyPay-Signature | string | Assinatura no formato t=<unix>,v1=<hmac>. O HMAC-SHA256 é calculado sobre <timestamp>.<body> com o secret do endpoint. |
X-LofyPay-Id | string | UUID único da entrega. Use pra deduplicar (idempotência). |
User-Agent | string | LofyPay-Webhook/1.0 |
POST no seu endpoint. Só trate o evento depois de validar a X-LofyPay-Signature: apenas a LofyPay conhece o secret do seu endpoint, então uma assinatura válida é a prova de origem. Sempre rejeite também timestamps fora da janela (anti-replay).Verificando a assinatura
import { createHmac, timingSafeEqual } from "node:crypto";
// secret = o secret do endpoint cadastrado em /gateway
function verifyLofypay(rawBody, secret, sigHeader, toleranceSec = 300) {
// sigHeader = "t=1730475012,v1=9f3c…"
const parts = Object.fromEntries(
sigHeader.split(",").map((kv) => {
const i = kv.indexOf("=");
return [kv.slice(0, i).trim(), kv.slice(i + 1).trim()];
})
);
const t = Number(parts.t);
if (!Number.isFinite(t)) return false;
// 1) Anti-replay: recusa eventos fora da janela (relógio do seu servidor).
if (Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
// 2) Assinatura: HMAC de `${t}.${rawBody}` (use o corpo CRU, sem reparsear).
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(parts.v1 || "", "hex");
return a.length === b.length && timingSafeEqual(a, b); // timing-safe
}X-LofyPay-Id e o mesmo corpo. Responda 200 rápido e processe de forma assíncrona.X-LofyPay-Id já processados e ignore repetições antes de creditar/agir./api/v1/status com o idTransaction do evento e só libere se voltar PAID_OUT. É o equivalente — sem certificado — ao cross-check que a LofyPay faz com os adquirentes dela.Exemplo — med.opened
{
"event": "med.opened",
"sent_at": "2026-06-01T14:30:12.000Z",
"data": {
"med_case_id": 482,
"provider": "onlyup",
"provider_case_id": "infra_9f3c…",
"type": "FRAUD",
"reason": "ACCOUNT_HOLDER",
"status": "pending_institution",
"status_label": "Aguardando Instituição",
"resolution": null,
"amount": 150.00,
"transaction": {
"idTransaction": "52fc5262-4063-4900-933b-55e6985096a1",
"external_reference": "PEDIDO-123456"
},
"opened_at": "2026-06-01T14:29:50.000Z",
"due_at": "2026-06-06T14:29:50.000Z"
}
}{ event, sent_at, data }). Use a mesma função de X-LofyPay-Signature acima — o secret é o do endpoint cadastrado em /gateway. Em med.resolved, resolution: "lost" significa que a infração foi procedente e o valor foi devolvido ao pagador.Cashout / Saque
Envia valores da sua carteira LofyPay pra uma chave PIX externa. Debita do saldo imediatamente; aprovação real depende da liquidação do adquirente.
https://app.lofypay.com/api/v1/cashoutAlias legado: https://app.lofypay.com/api/c1/cashout continua funcionando, mas novos projetos devem usar /api/v1/cashout.
| Campo | Tipo | Descrição |
|---|---|---|
api-keyobrigatório | string | Sua Secret Key (também aceita Authorization: Bearer). |
amountobrigatório | number | Valor em reais. Mínimo R$ 1,00 (contas roteadas via CyberHub: R$ 15,00) — e o valor precisa cobrir a taxa total do saque. |
nameobrigatório | string | Nome do favorecido (titular da chave). |
cpfobrigatório | string | CPF do favorecido (só dígitos). |
keypixobrigatório | string | Chave PIX de destino. |
tipo_chaveobrigatório | enum | Tipo da chave: CPF · CNPJ · EMAIL · TELEFONE · EVP (aleatória). |
{
"amount": 50.00,
"name": "Favorecido da Silva",
"cpf": "12345678900",
"keypix": "favorecido@exemplo.com",
"tipo_chave": "EMAIL"
}curl -X POST https://app.lofypay.com/api/v1/cashout \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-d '{
"amount": 50.00,
"name": "Favorecido da Silva",
"cpf": "12345678900",
"keypix": "favorecido@exemplo.com",
"tipo_chave": "EMAIL"
}'Resposta
{
"success": true,
"code": "SUCCESS",
"message": "…",
"retryable": false,
"status": "PROCESSING",
"withdrawalId": 1234,
"id": 1234
}{
"success": false,
"code": "PROVIDER_REJECTED",
"message": "…",
"retryable": false,
"status": "REJECTED",
"id": 1234
}Trate sucesso/erro pelo campo success e pelo HTTP status — não por body.status, que aqui carrega o status do saque (PROCESSING, PENDING_REVIEW, REJECTED…) e nunca vale "error". Códigos principais: SUCCESS (200), CASHOUT_PENDING_REVIEW (202 — fila de aprovação manual), MISSING_FIELDS / INVALID_AMOUNT (400), CASHOUT_IN_PROGRESS (409), INSUFFICIENT_BALANCE / PROVIDER_REJECTED (422), CASHOUT_COOLDOWN (429), PROVIDER_NOT_AVAILABLE (502).
https://app.lofypay.com/api/health{
"ok": true
}200 com { "ok": true } quando tudo está de pé; 503 com { "ok": false } quando o banco não responde. Detalhes de diagnóstico (db, latência) exigem token interno e não fazem parte da resposta pública.
const res = await fetch("https://app.lofypay.com/api/health");
const { ok } = await res.json();
console.log(ok ? "API Online" : "API Offline");Códigos de erro
A API responde sempre com JSON, mas o envelope de erro varia por endpoint: o gateway usa { status: 'error', message }, o /status usa { error } e o cashout usa { success, code, message }.
| Campo | Tipo | Descrição |
|---|---|---|
400 | number | Body inválido, parâmetro faltando — ou transação não encontrada no /status ({ "error": "No matching record found" }). |
401 | number | Chave de API ausente, inválida ou revogada. |
403 | number | IP não autorizado (allowlist) ou chave live em endpoint de sandbox. |
404 | number | Transação de teste não encontrada (em /sandbox/pay). |
405 | number | Método HTTP não permitido (use POST onde indicado). |
409 | number | Já existe um saque em andamento (CASHOUT_IN_PROGRESS). |
422 | number | Saldo insuficiente ou regra de negócio bloqueou a operação. |
429 | number | Rate limit excedido. Veja header Retry-After. |
500 | number | Erro interno. Tente novamente em alguns segundos. |
{
"status": "error",
"message": "API key rate limit exceeded"
}Nunca exponha a sk_
Use variáveis de ambiente no servidor. Frontend só usa a pk_ (não dá pra criar cobrança com ela).
Use HTTPS nos webhooks
URLs http:// são aceitas, mas o payload trafega sem criptografia. Em produção, use sempre https://.
Verifique a assinatura
No canal de endpoints registrados, confirme X-LofyPay-Signature antes de processar. O notification_url não é assinado — concilie via /api/v1/status.
Habilite IP allowlist
Em /gateway, fixe os IPs do seu servidor. Requisições de fora retornam 403.
Idempotência por external_reference
Use um ID único do seu pedido. Webhooks duplicados ficam fáceis de filtrar no seu lado.
Regenere após vazamento
Se a sk_ apareceu em log, commit, screenshot ou e-mail — regenere imediatamente em /gateway.