Introdução
Bem-vindo à documentação oficial da API TrackPag — infraestrutura de pagamentos PIX para integrações server-to-server, cobrindo depósitos (cash-in), saques (cash-out), saldo, disputas MED e webhooks assinados.
Nesta referência você encontra:
- •o fluxo completo de autenticação e cache de tokens;
- •cada endpoint com request, response e explicação campo a campo;
- •tabela consolidada de códigos de erro e como remediar cada um;
- •o formato dos webhooks e como verificar a assinatura em Node e Python;
- •nuances operacionais: idempotência, allowlist de IPs, prazos de MED e retentativas.
Visão geral
A TrackPag fica entre o seu sistema e os trilhos de pagamento. Você integra uma única API com schema uniforme — a gente cuida da liquidação, consolida os webhooks e expõe seu ledger em três buckets.
| Cenário | Endpoint | Tempo típico |
|---|---|---|
| Cliente paga via PIX (QR ou Copia e Cola) | POST /v1/charges | < 1s para gerar o QR |
| Confirmação do pagamento | webhook deposit.confirmed | segundos após o pagamento |
| Você envia PIX (saque) | POST /v1/withdrawals | até 1 dia útil |
| Consulta de saldo | GET /v1/balance | tempo real |
| Contestação do pagador (MED) | webhook med.opened | prazo de defesa: 7 dias |
URL base
https://api.trackpag.com/v1
Todas as requisições usam Content-Type: application/json e TLS. Chamadas em HTTP puro são recusadas.
Formato das respostas
- •Valores monetários são inteiros em centavos — 28490 significa R$ 284,90.
- •Datas em ISO 8601, sempre UTC — 2026-07-06T13:32:00Z.
- •Sucesso retorna o recurso direto no corpo, sem envelope.
- •Erro retorna { "error": { "code", "message" } } — veja Códigos de erro.
- •IDs têm prefixo por tipo: ch_ (cobrança), wd_ (saque), dp_ (disputa), evt_ (evento).
Ordem recomendada de leitura
- 01Autenticação — obtenha e cacheie seu token;
- 02Depósitos PIX — crie a primeira cobrança;
- 03Webhooks + Assinatura — receba confirmações com segurança;
- 04Saques PIX — automatize o cash-out;
- 05Códigos de erro — trate as falhas com elegância.
Suporte
Dúvidas de integração: suporte@trackpag.com.br · Central de ajuda. Inclua o id do recurso e o horário (UTC) da chamada — acelera muito o diagnóstico.
Autenticação
A TrackPag usa o par Client ID + Client Secret — os dois estão em Configurações → Credenciais. Troque-os por um access token e envie o token como Bearer nas demais chamadas.
Obter o token
curl -X POST https://api.trackpag.com/v1/auth/token \
-H "Content-Type: application/json" \
-d '{
"client_id": "seunome_A1B2C3D4",
"client_secret": "sk_live_9f2c4a7d..."
}'
# 200 OK
{
"access_token": "tpat_kX91mval...",
"token_type": "Bearer",
"expires_in": 3600
}Cache do token
O token vale expires_in segundos (1 hora). Não gere um token por requisição — cacheie em memória e renove só quando faltar menos de 5 minutos para expirar, ou quando receber 401.
let cached = { token: null, expiresAt: 0 };
async function getToken() {
if (cached.token && Date.now() < cached.expiresAt - 300_000) {
return cached.token; // ainda válido por > 5 min
}
const { access_token, expires_in } = await fetchToken();
cached = { token: access_token, expiresAt: Date.now() + expires_in * 1000 };
return access_token;
}Erros de autenticação
| Resposta | Causa | O que fazer |
|---|---|---|
| 401 unauthorized | client_id/secret incorretos ou token expirado | Confira as credenciais; renove o token e repita uma vez |
| 403 ip_not_allowed | IP fora da allowlist | Adicione o IP do servidor em Credenciais → IPs |
| 429 rate_limited | Gerando tokens em excesso | Implemente o cache acima; respeite o Retry-After |
IPs permitidos
Em Credenciais → IPs permitidos você restringe de quais endereços a API aceita chamadas da sua conta (até 20 IPs). Lista vazia = qualquer origem — recomendamos travar nos IPs de saída dos seus servidores em produção.
Chamada de IP fora da lista → 403 ip_not_allowed. O IP recusado vem na mensagem do erro para facilitar o diagnóstico.
Idempotência
Envie o header Idempotency-Key em todo POST que cria recurso. Se a rede cair e você repetir a chamada com a mesma chave, recebe a mesma resposta — sem cobrança nem saque duplicado. Use um valor único por operação (o ID do pedido no seu sistema). Chaves valem por 24 horas.
curl -X POST https://api.trackpag.com/v1/charges \
-H "Authorization: Bearer tpat_..." \
-H "Idempotency-Key: pedido-4128" \
-d '{ ... }'Mesma chave + corpo diferente → 409 idempotency_conflict.
Códigos de erro
Erros sempre voltam neste formato:
{
"error": {
"code": "insufficient_funds",
"message": "Saldo disponível insuficiente para este saque."
}
}| Status | Código | Causa | Como remediar |
|---|---|---|---|
| 400 | invalid_request | Corpo malformado ou campo obrigatório ausente | Confira o campo apontado na message |
| 401 | unauthorized | Token ausente, inválido ou expirado | Renove o token e repita uma vez |
| 403 | ip_not_allowed | IP de origem fora da allowlist | Adicione o IP em Credenciais → IPs |
| 404 | not_found | Recurso não existe ou pertence a outra conta | Confira o ID e o ambiente |
| 409 | idempotency_conflict | Mesma Idempotency-Key com corpo diferente | Gere uma chave nova para operações novas |
| 422 | insufficient_funds | Saldo disponível não cobre a operação | Consulte GET /v1/balance antes de sacar |
| 422 | charge_expired | Cobrança expirou antes do pagamento | Crie uma nova cobrança |
| 429 | rate_limited | Limite de requisições excedido | Aguarde o Retry-After; use backoff exponencial |
| 5xx | server_error | Falha nossa | Repita com a mesma Idempotency-Key — é seguro |
Depósitos PIX
Crie uma cobrança, mostre o QR (ou o Copia e Cola) ao pagador e receba deposit.confirmed no seu webhook — normalmente segundos após o pagamento.
Criar cobrança
curl -X POST https://api.trackpag.com/v1/charges \
-H "Authorization: Bearer tpat_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-4128" \
-d '{
"amount": 28490,
"method": "pix",
"customer": { "name": "Ana Souza", "email": "ana@studio-fio.com.br", "document": "12345678901" },
"reference": "pedido#4128",
"expires_in": 3600
}'
# 201 Created
{
"id": "ch_2n4kQ8Zr",
"status": "pending",
"amount": 28490,
"method": "pix",
"reference": "pedido#4128",
"qr_code": "00020126580014br.gov.bcb.pix...",
"qr_code_base64": "iVBORw0KGgo...",
"expires_at": "2026-07-06T14:32:00Z",
"created_at": "2026-07-06T13:32:00Z"
}| Campo | Tipo | Descrição |
|---|---|---|
| amount | int · obrigatório | Centavos — mínimo 100 (R$ 1,00) |
| method | string · obrigatório | Por enquanto, sempre "pix" |
| customer.name | string · opcional | Nome do pagador, para conciliação |
| customer.email | string · opcional | E-mail do pagador |
| customer.document | string · opcional | CPF/CNPJ (só dígitos). Se inválido, a cobrança é criada sem trava de pagador |
| reference | string · opcional | Seu identificador interno — ecoado nos webhooks |
| expires_in | int · opcional | Segundos até expirar (padrão 3600, máx. 86400) |
Ciclo de vida
| Status | Significado |
|---|---|
| pending | Aguardando pagamento — QR ativo |
| paid | Pago e liquidado — valor no bucket receivable → available |
| expired | Expirou sem pagamento — QR morto, crie outra |
| refunded | Devolvido ao pagador (voluntário ou por MED perdido) |
Depois de criada, a cobrança é imutável — valor e expiração não mudam. Para corrigir, deixe expirar (ou ignore) e crie outra.
QR e Copia e Cola
- •qr_code é o payload EMV completo — o mesmo texto do "PIX Copia e Cola". Mostre num botão de copiar.
- •qr_code_base64 é um PNG pronto: <img src="data:image/png;base64,{...}" />.
- •O pagador pode pagar quantas vezes tentar, mas só a primeira liquidação conta — as demais são devolvidas automaticamente.
Consultas
Webhook é o caminho primário de confirmação — consultas servem para conciliação e para recuperar estado depois de indisponibilidade do seu lado.
Consultar por ID
curl https://api.trackpag.com/v1/charges/ch_2n4kQ8Zr \ -H "Authorization: Bearer tpat_..." # 200 OK — mesmo formato da criação, com "status" atual # e "paid_at" quando status = "paid"
Listagem e paginação
curl "https://api.trackpag.com/v1/charges?status=paid&limit=50" \
-H "Authorization: Bearer tpat_..."
# 200 OK
{
"data": [ { "id": "tp...", "amount": 28490, "status": "paid", ... } ],
"has_more": true
}| Parâmetro | Descrição |
|---|---|
| status | Filtra: pending · paid · expired · refunded |
| limit | 1 a 100 (padrão 25) |
| created_before | Cursor: created_at (ISO 8601) do último item da página anterior |
A ordenação é sempre da mais recente para a mais antiga. Enquanto has_more for true, repita passando created_before = created_at do último item recebido. Saques saem em GET /v1/withdrawals.
Saldo
curl https://api.trackpag.com/v1/balance \
-H "Authorization: Bearer tpat_..."
# 200 OK
{
"available_cents": 1284900,
"blocked_cents": 0,
"receivable_cents": 45200
}| Bucket | Significado |
|---|---|
| available_cents | Livre para saque agora |
| blocked_cents | Travado — saque em processamento ou bloqueio cautelar (MED) |
| receivable_cents | A liquidar — vira available no prazo do rail |
Saques PIX
Transfira o saldo disponível para qualquer chave PIX. O valor sai de available, fica em blocked durante o processamento e você recebe withdrawal.paid ou withdrawal.rejected no webhook.
Solicitar saque
curl -X POST https://api.trackpag.com/v1/withdrawals \
-H "Authorization: Bearer tpat_..." \
-H "Idempotency-Key: saque-2026-07-06-01" \
-d '{
"amount": 500000,
"pix_key": "13999998888",
"pix_key_type": "phone"
}'
# 201 Created
{ "id": "wd_8xK2mQ", "status": "requested", "amount": 500000 }| Campo | Tipo | Descrição |
|---|---|---|
| amount | int · obrigatório | Centavos — mínimo 1000 (R$ 10,00) |
| pix_key | string · obrigatório | Chave PIX de destino |
| pix_key_type | string · obrigatório | cpf · cnpj · email · phone · random |
Pagar um QR de terceiros (Copia e Cola)
Além de sacar para uma chave, você pode pagar um código PIX (QR ou Copia e Cola de um fornecedor, por exemplo) direto do seu saldo. Envie o payload EMV no campo qr_code — ele substitui pix_key/pix_key_type e os dois modos são mutuamente exclusivos.
curl -X POST https://api.trackpag.com/v1/withdrawals \
-H "Authorization: Bearer tpat_..." \
-H "Idempotency-Key: pgto-fornecedor-889" \
-d '{
"qr_code": "00020126580014br.gov.bcb.pix..."
}'
# 201 Created — o valor vem do próprio código
{ "id": "wd_3fT9zL", "status": "requested", "amount": 152000 }- •O valor é lido do próprio código — se o QR tiver valor aberto, envie também "amount" em centavos;
- •Códigos expirados ou já pagos retornam 422 com o motivo;
- •O fluxo de status e webhooks é o mesmo do saque por chave.
Ciclo de vida
| Status | Significado |
|---|---|
| requested | Recebido — valor reservado em blocked |
| paid | PIX enviado — webhook withdrawal.paid |
| rejected | Recusado — valor devolvido a available, motivo em notes |
Prazo: até 1 dia útil. Se a chave PIX estiver errada ou a conta de destino recusar, o saque volta como rejected com o motivo — nada se perde.
Disputas (MED)
O MED (Mecanismo Especial de Devolução) é o processo do Banco Central para o pagador contestar um PIX. Quando um MED chega contra uma transação sua, a TrackPag abre uma disputa e te avisa.
Como funciona
- 01Chega o MED → o valor contestado entra em bloqueio cautelar (blocked) e você recebe med.opened;
- 02Você tem 7 dias para enviar a defesa com evidências (entrega, comunicação com o cliente, etc.);
- 03A resolução chega via med.resolved — a favor libera o valor, contra devolve ao pagador.
med.opened e med.resolved já avisam seu servidor em tempo real. Os endpoints REST de disputa (GET /v1/disputes e envio de defesa) entram em breve.Enviar defesa
- •Responda pelo painel enquanto o REST não sai — uma defesa por disputa, capriche na primeira;
- •Enviada após o prazo, a defesa é recusada e a disputa tende a ser perdida;
- •Quanto mais evidência objetiva (rastreio, logs, comunicação), maior a chance de ganhar.
Resolução
| Resultado | Efeito no saldo | Webhook |
|---|---|---|
| won (a favor) | Valor sai de blocked e volta para available | med.resolved · outcome: won |
| lost (contra) | Valor sai de blocked e é devolvido ao pagador | med.resolved · outcome: lost |
Webhooks · Eventos
Configure as duas URLs em Credenciais → Webhooks: uma para eventos de depósitos, outra para saques. Todo evento é um POST JSON assinado.
Tabela de eventos
| Evento | URL | Disparo |
|---|---|---|
| deposit.confirmed | Depósitos | Cobrança PIX paga e liquidada |
| deposit.refunded | Depósitos | Devolução ao pagador processada |
| med.opened | Depósitos | MED aberto — valor em bloqueio cautelar |
| med.resolved | Depósitos | MED resolvido (won ou lost) |
| withdrawal.paid | Saques | Saque pago via PIX |
| withdrawal.rejected | Saques | Saque recusado — valor devolvido |
Payload
{
"id": "evt_5Qm2xL9c",
"type": "deposit.confirmed",
"created_at": "2026-07-06T13:32:41Z",
"data": {
"charge_id": "ch_2n4kQ8Zr",
"amount": 28490,
"reference": "pedido#4128",
"customer": { "name": "Ana Souza", "email": "ana@studio-fio.com.br" }
}
}Boas práticas
- •Responda 2xx em até 5 segundos — enfileire e processe de forma assíncrona;
- •Deduplique pelo id do evento — entregas podem repetir e chegar fora de ordem;
- •Nunca confie só no webhook para valores: confirme com GET /v1/charges/{id} antes de liberar mercadoria de alto valor;
- •Verifique a assinatura de TODA entrega — requisição sem assinatura válida é forjada.
Verificando a assinatura dos webhooks
O header
Cada entrega leva X-TrackPag-Signature, gerado com a chave whsec_… das suas credenciais:
X-TrackPag-Signature: t=1751808761,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd # v1 = HMAC-SHA256( t + "." + corpo_bruto , webhook_signature )
Verificar em Node
import crypto from "node:crypto";
import express from "express";
const app = express();
// IMPORTANTE: raw body — parse depois da verificação
app.post("/webhooks/trackpag", express.raw({ type: "*/*" }), (req, res) => {
const header = req.header("X-TrackPag-Signature") ?? "";
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return res.sendStatus(400);
const expected = crypto
.createHmac("sha256", process.env.TP_WEBHOOK_SECRET)
.update(`${t}.${req.body}`)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) {
return res.sendStatus(400);
}
const event = JSON.parse(req.body);
// enfileire e processe async...
res.sendStatus(200);
});Verificar em Python
import hmac, hashlib, time, os
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/webhooks/trackpag")
def trackpag_webhook():
header = dict(p.split("=") for p in request.headers.get("X-TrackPag-Signature", "").split(","))
t, v1 = header.get("t", "0"), header.get("v1", "")
if abs(time.time() - int(t)) > 300:
abort(400)
expected = hmac.new(
os.environ["TP_WEBHOOK_SECRET"].encode(),
f"{t}.".encode() + request.get_data(), # corpo bruto!
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(v1, expected):
abort(400)
event = request.get_json()
# enfileire e processe async...
return "", 200Retentativas
Se seu endpoint não responder 2xx em 5 segundos, a entrega entra na fila de retentativa com backoff:
| Tentativa | Espera após a falha |
|---|---|
| 1ª retentativa | 1 minuto |
| 2ª | 10 minutos |
| 3ª | 1 hora |
| 4ª | 6 horas |
| 5ª (última) | 24 horas |
Depois da última falha o evento fica disponível para reenvio manual pelo suporte. Como entregas podem repetir, deduplique pelo id do evento.
Travou em algum passo? Escreva para suporte@trackpag.com.br com o id do recurso — a gente responde rápido.