👋 Você ainda não tem conta. Crie a sua e gere suas chaves sk_test_ em menos de 1 minuto. Criar conta grátis →
Treevo V1
POST API de cobrança POST API de saque Webhooks Integrar com IA
Documentação · API v1 · Ao vivo

Bem-vindo à API da Trevo

Tudo o que você precisa para integrar pagamentos PIX: cobranças instantâneas (PIX IN) com QR Code, saques programáticos (PIX OUT), webhooks assinados e um sandbox completo para testar sem dinheiro real.

Gateway PIX · PIX IN + PIX OUT

O que é a Trevo?

A Trevo é um gateway de pagamento PIX que nasceu de uma necessidade real: simplificar cobranças (PIX IN) e saques (PIX OUT) para infoprodutores e plataformas brasileiras. Percebemos que os meios existentes eram complexos demais — documentações confusas, processos de homologação longos e múltiplas formas de fazer a mesma coisa. Nossa resposta foi uma API que transforma isso em algo simples e direto:

// amount em CENTAVOS (int): 1990 = R$ 19,90
trevopay.charges.create({
  amount: 1990,
  description: 'Curso de PIX',
  externalId: 'PED-123',
});

REST + JSON, valores sempre em centavos (int), envelope { data, error, success } em toda resposta. Sem mágica, sem armadilhas. Veja as regras essenciais antes da primeira chamada.

Princípios da API

Baseada em intenção

Cada endpoint representa exatamente o que você lê. POST /v1/charges cria uma cobrança. POST /v1/cashouts realiza um saque. Sem aliases, sem ambiguidade.

Consistente

Toda resposta segue o padrão { data, error, success }. Se success for true, os dados estão em data. Se false, a falha está em error com { code, message }.

Idempotente

POSTs que movimentam dinheiro exigem Idempotency-Key. Reenviar com a mesma key + mesmo corpo devolve a mesma resposta — você nunca duplica cobrança nem saque.

Segura por padrão

HTTPS obrigatório, webhooks assinados com HMAC-SHA256, janela anti-replay de 5 minutos e comparação tempo-constante para evitar timing attacks.

Leia antes de integrar

Regras essenciais

Cinco convenções que valem para toda chamada da API — tanto em PIX IN (cobranças) quanto em PIX OUT (saques). Quebrar qualquer uma delas devolve 422 validation_error ou pior. Repetimos cada regra dentro dos endpoints específicos para você não precisar voltar aqui.

Valor sempre em centavos (int)

O campo amount é um inteiro em centavos. Nunca envie float, string formatada ou ponto/vírgula. Conversão mental:
1990 → R$ 19,90 · 1616 → R$ 16,16 · 100 → R$ 1,00 · 50000 → R$ 500,00.
Se você tem R$ 16,00 na cabeça, envie 1600. Se tem R$ 16,16, envie 1616.

Idempotency-Key obrigatória em POSTs

Todo POST /v1/charges e POST /v1/cashouts exige o header Idempotency-Key (8–200 chars). Use o id do seu pedido — reenviar com a mesma key + mesmo corpo devolve a mesma resposta. Você nunca duplica cobrança nem saque.

Webhook é a fonte de verdade

Nunca libere acesso confiando no front-end. Só libere depois de receber o webhook assinado charge.paid (ou após confirmar via GET /v1/charges/{id}). Sempre valide o HMAC-SHA256 em tempo constante antes de qualquer side-effect.

HTTPS obrigatório em produção

Toda chamada para /v1/* e todo endpoint de webhook precisam ser HTTPS. Chamadas HTTP fora de localhost retornam 400 https_required. Sem exceções.

Datas em ISO-8601 UTC, moeda BRL

Datas com sufixo Z: 2026-05-29T14:00:00Z. Moeda sempre "BRL" (PIX only). Campos em camelCase em request e response. Encoding sempre UTF-8.

Prefixos de ID padronizados

ch_ cobranças · bp_co_ saques · whsec_ signing secret de webhook · pk_test_ / sk_test_ / pk_live_ / sk_live_ API keys. Use o prefixo pra rotear ids no seu sistema.

⚠️ Erro mais comum: enviar "amount": 19.90 achando que vai cobrar R$ 19,90. Errado — isso vira 19 centavos (R$ 0,19). O certo é "amount": 1990 (inteiro, centavos).

O que você pode fazer

Recursos disponíveis na API v1. Todos prontos em produção, com versões de teste no sandbox.

Primeiros passos

Em três passos você cria uma cobrança e simula o pagamento no sandbox — sem dinheiro real.

Autenticação

Toda chamada de servidor usa o header Authorization: Bearer SUA_SECRET. Há dois ambientes:

AmbienteChavesComportamento
Testepk_test_… / sk_test_…Nunca movimenta dinheiro real. Use para integrar.
Produçãopk_live_… / sk_live_…Cobranças reais, liquidadas pelo PSP.
A secret (sk_) aparece só uma vez. Guarde-a com segurança e use apenas no backend. A chave publicável (pk_) não autentica chamadas de servidor.

Escopos

Cada chave pode ter um subconjunto de escopos. Por padrão a chave criada no painel recebe todos os escopos do plano.

EscopoPermite
CHARGE:CREATECriar cobranças PIX.
CHARGE:READConsultar cobranças.
CASHOUT:CREATECriar saques PIX.
CASHOUT:READConsultar saques.

Idempotência

POSTs que movimentam dinheiro (/v1/charges e /v1/cashouts) exigem o header Idempotency-Key. Isso protege você contra cobranças duplicadas em casos de retry.

  • String única por operação, de 8 a 200 caracteres.
  • Mesma key + mesmo corpo: devolve a mesma resposta (não duplica).
  • Mesma key + corpo diferente: 409 idempotency_conflict.
  • Outra requisição em voo com a mesma key: 409 idempotency_in_progress.
  • Reaproveitável por 24h; depois disso é liberada.
Dica: use seu identificador interno (id do pedido, id do payout) como Idempotency-Key. Isso torna retries triviais e seguros.

Sandbox / Dev mode

Com sk_test_, crie a cobrança normalmente — o brCode e o QR vêm em formato válido (fictícios — não funcionam no app do banco). Para simular o pagamento e disparar o webhook de saída:

POST /sandbox/simulate-payment/{chargeId}
curl -X POST https://api.treevopay.com/sandbox/simulate-payment/ch_xxx \
  -H "Authorization: Bearer sk_test_SUA_CHAVE"

Dica: passe ?silent=1 para marcar pago sem disparar webhook e testar a reconciliação ativa.

Cashout em sandbox roda livre — mode=test sempre retorna status=confirmed sem bater no PSP.

PIX IN

Criar cobrança PIX (PIX IN)

POST /v1/charges

Cria uma cobrança PIX e retorna o copia-e-cola e o QR. O header Idempotency-Key é obrigatório.

⚠️ Regras críticas pra PIX IN — leia antes de enviar o body:
  • amount em centavos (int). 1990 = R$ 19,90, 1616 = R$ 16,16, 100 = R$ 1,00. Nunca envie 19.90 nem "19,90" — vira 19 centavos.
  • Idempotency-Key obrigatório (8–200 chars). Use o id do seu pedido. Mesma key + mesmo corpo = mesma cobrança (não duplica).
  • HTTPS obrigatório. Chamada HTTP fora de localhost devolve 400 https_required.
  • Só libere acesso pelo webhook charge.paid (HMAC validado) — nunca confie só no retorno do front.

Headers

HeaderDescrição
Authorization obrigatórioBearer sk_test_… ou sk_live_…
Content-Type obrigatórioapplication/json
Idempotency-Key obrigatórioString única (8–200). Repetir com mesmo corpo = mesma cobrança.

Parâmetros (body JSON)

CampoTipoDescrição
amount obrigatóriointValor em centavos. Mín 100, máx 100000000.
descriptionstringNome do produto na hora (até 255).
externalIdstringSeu identificador interno (até 120).
customerobject{ name, email, taxId, cellphone } — opcional.
utmobject{ source, medium, campaign, term, content } — ecoado no webhook.

Exemplo

curl -X POST https://api.treevopay.com/v1/charges \
  -H "Authorization: Bearer sk_test_SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pedido-123" \
  -d '{"amount":1990,"description":"Curso","externalId":"PED-123"}'

Resposta 201

{
    "success": true,
    "data": {
        "id": "ch_2f1a8c9d4e5b6a7c8d9e0f12",
        "amount": 1990,
        "currency": "BRL",
        "status": "pending",
        "description": "Curso",
        "externalId": "PED-123",
        "brCode": "00020101…6304ABCD",
        "brCodeBase64": "data:image/png;base64,iVBOR…",
        "expiresAt": "2026-05-29T14:00:00Z",
        "mode": "test"
    },
    "error": null
}

Consultar cobrança

GET /v1/charges/{id}

data.status é um de: pending, paid, expired, failed, refunded. O status é monotônico: paid nunca volta para pending.

curl https://api.treevopay.com/v1/charges/ch_xxx \
  -H "Authorization: Bearer sk_test_SUA_CHAVE"
PIX OUT

Realizar saque PIX (PIX OUT)

POST /v1/cashouts

Saque PIX programático (B2B/B2C). Útil para plataformas que precisam pagar prestadores, afiliados ou repassar valores ao cliente final.

⚠️ Regras críticas pra PIX OUT — leia antes de enviar o body:
  • amount em centavos (int, ≥ 1). 50000 = R$ 500,00, 1616 = R$ 16,16. Nunca envie float ou string com vírgula — o PSP rejeita.
  • pixKey + pixKeyType precisam casar. Tipo cpf? envie 11 dígitos sem máscara. cnpj? 14 dígitos. phone? só dígitos com DDD (ex.: 11999998888). email? em minúsculas. random? UUID v4.
  • Idempotency-Key obrigatório (8–200). Use o id do payout — mesma key + mesmo corpo nunca paga 2x.
  • Produção exige liberação: bspay_cashout_enabled=1 no painel admin. Desligado devolve 503 cashout_disabled. Sandbox (sk_test_) roda livre.
  • Resposta 202 ≠ saque feito. O 202 só confirma que o PSP aceitou. A liquidação real vem no webhook cashout.confirmed ou cashout.failed.

Parâmetros (body JSON)

CampoTipoDescrição
amount obrigatóriointValor em centavos (≥ 1).
pixKey obrigatóriostringChave PIX do destinatário (até 200).
pixKeyType obrigatóriostringcpf · cnpj · email · phone · random
externalIdstringSeu identificador (até 64). Determinístico se omitido.
descriptionstringDescrição livre (até 140).

Exemplo

curl -X POST https://api.treevopay.com/v1/cashouts \
  -H "Authorization: Bearer sk_live_SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: saque-afiliado-789" \
  -d '{
    "amount": 50000,
    "pixKey": "joao@exemplo.com",
    "pixKeyType": "email",
    "externalId": "PAYOUT-789",
    "description": "Comissao afiliado"
  }'

Resposta 202

{
    "success": true,
    "data": {
        "id": "bp_co_a1b2c3d4e5f6a7b8",
        "amount": 50000,
        "currency": "BRL",
        "status": "pending",
        "externalId": "PAYOUT-789",
        "transactionId": "TXN-PSP-99887766",
        "pixKey": "joao@exemplo.com",
        "pixKeyType": "email",
        "description": "Comissao afiliado",
        "failureReason": null,
        "mode": "live",
        "requestedAt": "2026-05-29T14:00:00Z",
        "confirmedAt": null
    },
    "error": null
}

O HTTP 202 indica que o saque foi aceito e enviado ao PSP. A confirmação final vem via webhook cashout.confirmed ou cashout.failed.

Consultar saque

GET /v1/cashouts/{id}

Status possíveis: pending (enviado ao PSP), confirmed (liquidado na chave do destinatário), failed (ver failureReason).

curl https://api.treevopay.com/v1/cashouts/bp_co_xxx \
  -H "Authorization: Bearer sk_live_SUA_CHAVE"

Webhooks

Cadastre uma URL no painel. Quando a cobrança é paga (ou o saque é confirmado), enviamos um POST assinado para você. A fonte de verdade é o webhook validado no seu servidor — nunca confie no front-end.

Eventos

EventoQuando
charge.paidPagamento confirmado.
charge.expiredCobrança expirou sem pagamento.
charge.failedFalha no processamento.
charge.refundedPagamento reembolsado.
cashout.confirmedSaque liquidado.
cashout.failedSaque falhou (ver failureReason).

Headers enviados

X-Trevo-EventNome do evento.
X-Trevo-TimestampEpoch do envio (anti-replay).
X-Trevo-SignatureHMAC-SHA256 em hex.
X-Trevo-DeliveryID único da entrega (deduplicação).

Validar a assinatura

A assinatura é HMAC-SHA256(timestamp + "." + corpo_cru, signing_secret). Use sempre comparação tempo-constante.

<?php
$secret = 'whsec_SEU_SIGNING_SECRET';
$body = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_TREVO_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_TREVO_SIGNATURE'] ?? '';

// rejeita replay (> 5 min)
if (abs(time() - (int)$ts) > 300) { http_response_code(400); exit; }

$expected = hash_hmac('sha256', $ts . '.' . $body, $secret);
if (!hash_equals($expected, $sig)) { http_response_code(401); exit; }

$evt = json_decode($body, true);
// libere o acesso do cliente aqui — $evt['data']['externalId'] etc.
http_response_code(200);
Retry: se o seu endpoint não responder 2xx em até 10s, reenviamos com backoff exponencial por 24h. Use X-Trevo-Delivery para deduplicar.

Indo para produção

  1. Troque sk_test_ por sk_live_ em todos os ambientes.
  2. Use sempre HTTPS — nunca aceite webhook em endpoint HTTP.
  3. Valide a assinatura HMAC do webhook antes de qualquer side-effect.
  4. Use hash_equals / timingSafeEqual / hmac.compare_digest, nunca ==.
  5. Use a Idempotency-Key por pedido para evitar cobranças duplicadas.
  6. Trate 429 com backoff + jitter. Não retentar 4xx (exceto 429).
  7. Monitore o status na página status.treevopay.com.

Erros

Em erro, data é null e error traz { code, message }. O HTTP status indica a categoria, o code indica a causa específica.

HTTPcodeSignificado
400idempotency_key_requiredFaltou o header Idempotency-Key.
400invalid_jsonBody não é JSON válido.
400https_requiredChamada via HTTP fora de localhost.
401invalid_credentialsChave inválida ou malformada.
401key_revokedChave revogada no painel.
403insufficient_scopeA chave não tem o escopo necessário.
404charge_not_foundCobrança não existe para esta conta.
404cashout_not_foundSaque não existe para esta conta.
409idempotency_conflictMesma Idempotency-Key com corpo diferente.
409idempotency_in_progressMesma key em processamento.
422validation_errorPayload inválido (ex.: amount ≤ 0).
429rate_limitedExcedeu 100 req/min. Use Retry-After.
501cashout_not_supportedProvider ativo não suporta cashout via API.
503cashout_disabledCashout live desabilitado no painel admin.

Saiba mais sobre a Trevo