URANOPAG - DOCUMENTACAO COMPLETA DA API (texto para assistentes de IA) ==================================================================== Endereco base: https://api.uranopag.app/v1 Ambiente: somente producao (nao existe sandbox nem chave de teste). Contexto para a IA: use apenas os endpoints, campos e formatos descritos abaixo. ## Visão geral A API UranoPag permite gerar cobranças PIX, consultar vendas, acompanhar o saldo e solicitar saques PIX a partir do seu sistema. Todas as requisições e respostas usam JSON (UTF-8) sobre HTTPS. | Item | Valor | | Endereço base | https://api.uranopag.app/v1 | | Protocolo | REST + JSON | | Ambiente | Somente produção. Não existe sandbox nem chave de teste: toda cobrança é real. | | Valores | Sempre inteiros em centavos (1000 = R$ 10,00) | | Datas | ISO 8601 em UTC | ## Autenticação Crie sua chave no painel em Desenvolvedores → API e Webhooks → API Keys. A chave completa aparece uma única vez; guarde-a em um cofre de segredos. Ela começa com up_live_. Authorization: Bearer up_live_SUA_CHAVE Content-Type: application/json - Use a chave somente no seu servidor. Nunca no navegador ou aplicativo. - Você pode restringir a chave a IPs ou faixas (CIDR). Requisições de outros IPs recebem 403 ip_not_allowed. - Revogue a chave no painel se suspeitar de vazamento; o efeito é imediato. ## Idempotência Criação de cobrança e de saque exigem o cabeçalho Idempotency-Key, de 16 a 128 caracteres (letras, números e hífen; use um UUID). Repetir a mesma requisição com a mesma chave devolve o mesmo recurso, sem criar outro. Repetir a chave com dados diferentes devolve 409 idempotency_mismatch. Idempotency-Key: 2f1c9a7e-5b8d-4c3a-9e21-7d6f0b4a1c55 Isso garante que cliques repetidos, novas tentativas de rede ou reenvios nunca gerem uma cobrança ou um saque duplicado. ## Criar cobrança PIX POST /v1/charges | Campo | Tipo | Obrigatório | Regra | | amount_cents | inteiro | sim | Mínimo 100 (R$ 1,00). Teto: ticket máximo da sua conta. | | external_id | texto | não | Seu identificador do pedido (até 255; letras, números, . _ : -). Único por conta. | | description | texto | não | Até 200 caracteres. | | payer.name | texto | sim | 3 a 120 caracteres. | | payer.document | texto | sim | CPF (11) ou CNPJ (14), com ou sem máscara. | | payer.email | texto | não | E-mail válido. | | payer.phone | texto | não | DDD + número, só dígitos. | curl -X POST "https://api.uranopag.app/v1/charges" \ -H "Authorization: Bearer up_live_SUA_CHAVE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 2f1c9a7e-5b8d-4c3a-9e21-7d6f0b4a1c55" \ -d '{ "amount_cents": 15000, "external_id": "pedido-8842", "description": "Pedido 8842", "payer": { "name": "Maria da Silva", "document": "529.982.247-25", "email": "maria@email.com" } }' Resposta 201: { "id": "3b0d7c1e-9f8a-4a52-8d3e-1c2b4a5f6e70", "object": "charge", "external_id": "pedido-8842", "amount_cents": 15000, "fee_cents": 449, "net_cents": 14551, "status": "pending", "pix_copy_paste": "00020101021226...6304ABCD", "end_to_end_id": null, "expires_at": "2026-10-19T03:30:00.000Z", "paid_at": null, "created_at": "2026-10-18T03:30:00.000Z" } Mostre pix_copy_paste como texto copiável (Pix Copia e Cola) e gere o QR Code a partir dele com qualquer biblioteca de QR. O pagamento é confirmado pelo webhook payment.paid. ## Consultar e listar cobranças GET /v1/charges/{id} GET /v1/charges?page=1&limit=20&status=paid&external_id=pedido-8842 limit vai de 1 a 100 (padrão 20). Filtros opcionais: status e external_id. A lista vem da mais recente para a mais antiga, com has_more indicando se há próxima página. { "data": [ { "id": "3b0d7c1e-...", "object": "charge", "status": "paid", "end_to_end_id": "E18236120202610180331s0123456789", "...": "..." } ], "page": 1, "limit": 20, "has_more": false } | status | Significado | | pending | Aguardando pagamento. | | paid | Pago e confirmado. | | expired | Não pago dentro do prazo. | | failed | Falha na emissão ou recusa do banco. | | in_review | Pagamento em análise por contestação (MED). | | defended | Contestação defendida: a venda segue paga. | | refunded | Valor devolvido ao pagador. | end_to_end_id é o código E2E do PIX (o mesmo do comprovante bancário). Ele é preenchido quando o pagamento é confirmado e a instituição o informa. ## Consultar saldo GET /v1/balance { "object": "balance", "available_cents": 125000, "blocked_cents": 0, "updated_at": "2026-10-18T03:31:00.000Z" } available_cents é o que pode ser sacado. blocked_cents está retido (análise ou contestação) e não pode ser sacado. ## Solicitar e consultar saques PIX POST /v1/withdrawals | Campo | Tipo | Regra | | amount_cents | inteiro | Valor debitado do saldo. A taxa de saque da conta é descontada desse valor. | | pix_key_type | texto | cpf, cnpj, email, phone ou random | | pix_key | texto | Chave PIX de destino. | curl -X POST "https://api.uranopag.app/v1/withdrawals" \ -H "Authorization: Bearer up_live_SUA_CHAVE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 7d6f0b4a-1c55-4c3a-9e21-2f1c9a7e5b8d" \ -d '{ "amount_cents": 50000, "pix_key_type": "email", "pix_key": "financeiro@empresa.com" }' { "id": "9c1e2a4b-...", "object": "withdrawal", "amount_cents": 50000, "fee_cents": 200, "net_cents": 49800, "status": "pending", "pix_key": "financeiro@empresa.com", "pix_key_type": "email", "end_to_end_id": null, "failure_reason": null, "created_at": "2026-10-18T03:40:00.000Z", "processed_at": null } GET /v1/withdrawals/{id} GET /v1/withdrawals?page=1&limit=20 | status | Significado | | pending | Solicitado, aguardando processamento. | | processing | Enviado à instituição, aguardando liquidação. | | completed | PIX liquidado no destino. | | rejected | Recusado. O valor volta ao saldo (uma única vez). | | failed | Não liquidado (ex.: chave inválida). O valor volta ao saldo (uma única vez). | Regras: o saldo é debitado no pedido; existe um intervalo mínimo entre pedidos e um limite diário por conta. Pedidos repetidos com a mesma Idempotency-Key nunca geram saque duplicado. ## Webhooks Cadastre a URL (HTTPS pública) em Desenvolvedores → API e Webhooks → Webhooks e escolha os eventos. Ao criar, o painel mostra o segredo de assinatura (whsec_...) uma única vez. | Evento | Quando | | payment.paid | Cobrança paga e confirmada. | | payment.hold | Valor retido para análise. | | payment.released | Retenção encerrada; valor liberado. | | payment.med | Contestação (MED) aberta: status in_review. | | payment.med_defended | Contestação defendida: venda segue paga. | | payment.refunded | Valor devolvido ao pagador. | | withdrawal.completed | Saque liquidado. | | withdrawal.rejected | Saque recusado ou falho; valor devolvido ao saldo. | Cada entrega é um POST JSON com estes cabeçalhos: | Cabeçalho | Conteúdo | | X-UranoPag-Event-Id | Identificador estável do evento (igual em reenvios). Use para não processar duas vezes. | | X-UranoPag-Timestamp | Unix timestamp (segundos) da assinatura. | | X-UranoPag-Signature | sha256= + HMAC-SHA256 hex de timestamp + "." + corpo bruto, com o seu segredo. | | X-UranoPag-Resend | true quando é um reenvio. | { "id": "payment.paid:3b0d7c1e-9f8a-4a52-8d3e-1c2b4a5f6e70", "event": "payment.paid", "created_at": "2026-10-18T03:31:02.000Z", "data": { "object": "charge", "id": "3b0d7c1e-9f8a-4a52-8d3e-1c2b4a5f6e70", "external_id": "pedido-8842", "amount_cents": 15000, "fee_cents": 449, "net_cents": 14551, "status": "paid", "end_to_end_id": "E18236120202610180331s0123456789", "paid_at": "2026-10-18T03:31:00.000Z", "created_at": "2026-10-18T03:30:00.000Z" } } Validação da assinatura (Node.js): import crypto from "node:crypto"; app.post("/webhooks/uranopag", express.raw({ type: "application/json" }), (req, res) => { const ts = req.get("X-UranoPag-Timestamp") ?? ""; const got = req.get("X-UranoPag-Signature") ?? ""; const raw = req.body.toString("utf8"); const expected = "sha256=" + crypto.createHmac("sha256", process.env.URANOPAG_WEBHOOK_SECRET).update(`${ts}.${raw}`).digest("hex"); const ok = got.length === expected.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected)); if (!ok || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(401); const event = JSON.parse(raw); // processe de forma idempotente usando event.id res.sendStatus(200); }); PHP: 300) { http_response_code(401); exit; } $event = json_decode($raw, true); http_response_code(200); Python: import hmac, hashlib, os, time raw = request.get_data() ts = request.headers.get("X-UranoPag-Timestamp", "") exp = "sha256=" + hmac.new(os.environ["URANOPAG_WEBHOOK_SECRET"].encode(), f"{ts}.".encode() + raw, hashlib.sha256).hexdigest() if not hmac.compare_digest(exp, request.headers.get("X-UranoPag-Signature", "")) or abs(time.time() - int(ts or 0)) > 300: abort(401) - Responda 2xx em até 10 segundos. Outros códigos, timeout ou erro de rede contam como falha. - Entregas com falha podem ser reenviadas pelo painel (uma a uma ou "Reenviar falhados"). Um reenvio só repete o estado atual: nunca altera saldo, venda ou saque. - O mesmo evento pode chegar mais de uma vez. Guarde o id e ignore repetições. - Para liberar pedidos de valor alto, confirme também com GET /v1/charges/{id}. ## Erros Erros sempre têm o formato abaixo. Mensagens são genéricas por segurança; use o code no seu tratamento. { "error": { "code": "validation_error", "message": "Dados inválidos.", "fields": ["payer.document"] } } | HTTP | code | Quando | | 400 | invalid_json / idempotency_required | Corpo inválido ou Idempotency-Key ausente. | | 401 | unauthorized | Chave ausente, inválida ou revogada. | | 403 | ip_not_allowed / account_restricted / onboarding_pending | IP fora da lista, conta com restrição ou cadastro não aprovado. | | 404 | not_found | Recurso inexistente ou de outra conta. | | 409 | idempotency_mismatch / external_id_taken | Chave de idempotência ou external_id já usados com outros dados. | | 422 | validation_error / above_max_ticket / amount_below_fee / daily_limit / insufficient_funds | Regra de valor ou de saldo. | | 429 | rate_limited / too_frequent | Muitas requisições (aguarde o Retry-After) ou saques próximos demais. | | 503 | provider_unavailable | Instituição indisponível no momento. Tente de novo com a mesma Idempotency-Key. | | 500 | internal_error | Erro interno. Tente novamente. | ## Limites de uso O limite é por chave de API, por segundo, e é definido para a sua conta (padrão: 50 requisições por segundo). Ao exceder, a API responde 429 com Retry-After: 1. Pagamentos recebidos e webhooks nunca são afetados por esse limite. ## Exemplos completos Node.js (fetch): const res = await fetch("https://api.uranopag.app/v1/charges", { method: "POST", headers: { Authorization: `Bearer ${process.env.URANOPAG_API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID(), }, body: JSON.stringify({ amount_cents: 15000, external_id: "pedido-8842", payer: { name: "Maria da Silva", document: "52998224725" } }), }); const charge = await res.json(); if (!res.ok) throw new Error(charge.error.code); console.log(charge.pix_copy_paste); PHP (cURL): true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('URANOPAG_API_KEY'), 'Content-Type: application/json', 'Idempotency-Key: ' . bin2hex(random_bytes(16)), ], CURLOPT_POSTFIELDS => json_encode(['amount_cents' => 15000, 'payer' => ['name' => 'Maria da Silva', 'document' => '52998224725']]), ]); $charge = json_decode(curl_exec($ch), true); Python (requests): import os, uuid, requests r = requests.post("https://api.uranopag.app/v1/charges", headers={"Authorization": f"Bearer {os.environ['URANOPAG_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())}, json={"amount_cents": 15000, "payer": {"name": "Maria da Silva", "document": "52998224725"}}, timeout=20) r.raise_for_status() print(r.json()["pix_copy_paste"])