API do DePix App
Introdução
A API do DePix App permite criar e gerenciar checkouts Pix programaticamente. É a mesma API que alimenta o plugin do BTCPay Server, a Área do Lojista e agora agentes de IA — pelo MCP e pelo SDK.
URL base
https://api.depixapp.com
Formato
Todos os requests e responses usam JSON (Content-Type: application/json). Valores monetários são sempre em centavos (inteiros). Exemplo: R$ 10,00 = 1000.
Autenticação
Use uma API key no header Authorization em todos os requests autenticados.
Authorization: Bearer sk_live_<sua-chave>
Nos exemplos curl desta documentação, a chave vem da variável de ambiente DEPIX_API_KEY. Defina-a uma vez no seu shell — export DEPIX_API_KEY=sk_live_... (ou sua chave sk_test_ no sandbox) — em vez de colar a chave inline em cada comando.
Tipos de chave
| Prefixo | Tipo | Comportamento |
|---|---|---|
| sk_live_ | Live | Checkouts reais. Dinheiro de verdade. |
| sk_test_ | Test | Checkouts de teste. Nenhum dinheiro real é movimentado. |
Para gerenciar suas chaves (criar, listar, revogar), acesse a Área do Lojista em depixapp.com/#merchant. Máximo de 5 chaves live e 5 chaves test ativas por conta.
Cada chave nasce com scopes explícitos (merchant_read, merchant_write, wallet_read, wallet_write) e, no caso de chaves com scope wallet_write, com limites de gasto obrigatórios. A chave é imutável após a criação — veja Scopes e limites por chave.
Acesso à API de produção
Chaves sk_test_ são liberadas automaticamente após criar sua conta de lojista — comece integrando contra o sandbox sem esperar nada. Chaves sk_live_ exigem aprovação manual: na área de API Keys, clique em Solicitar acesso, responda 5 perguntas curtas sobre sua integração, e nossa equipe avalia. Aprovações costumam sair em algumas horas em dias úteis.
Scopes e limites por chave
Cada API key carrega um conjunto explícito de scopes que define o que ela pode fazer. Os scopes são escolhidos na criação da chave e não podem ser alterados depois — a chave é imutável; para mudar scopes ou limites, revogue e crie outra.
Os scopes se dividem em dois eixos: merchant_* é o lado lojista (o gateway — checkouts e produtos) e wallet_* é o lado carteira (o on/off-ramp Pix — depósitos e saques).
| Scope | Libera |
|---|---|
| merchant_read | Todos os GETs da superfície de lojista: listar/consultar checkouts, produtos e GET /api/me. |
| merchant_write | O lado "receber": criar/simular checkouts, o CRUD de produtos e editar os campos leves do perfil da loja (PATCH /api/merchants/me). |
| wallet_read | Ler o status do lado carteira: GET /api/deposits/:id e GET /api/withdrawals/:id. Nunca é concedido por padrão. |
| wallet_write | O lado "pagar" (mover dinheiro): POST /api/deposit, POST /api/withdraw. Nunca é concedido por padrão. |
- Sem hierarquia implícita —
merchant_writenão incluimerchant_read, e os scopeswallet_*não incluem nenhum deles. Uma chave pode combinar os quatro:["merchant_read", "merchant_write", "wallet_read", "wallet_write"]. - Chamada sem o scope exigido →
403comerror.code = "insufficient_scope"edetails.required_scope(ver Erros). A resposta nunca ecoa a lista de scopes da chave.
Chaves wallet_write nascem com limites obrigatórios
Toda chave com scope wallet_write tem limites de gasto próprios, aplicados além dos limites da conta (os limites de conta sempre prevalecem). Se você não informar valores na criação, os defaults se aplicam: R$ 100,00 por transação (per_tx_limit_cents = 10000) e R$ 500,00 por dia (daily_limit_cents = 50000, janela rolante de 24h somando depósitos e saques atribuídos à chave). Você pode elevar os valores conscientemente na criação — mas uma chave wallet_write sem limites não existe.
Operação que excede um limite da chave → 400 com error.code = "key_limit_exceeded" e details: { limit: "per_tx" | "daily", limit_cents, used_cents }.
Criação de chave com scopes e limites
A gestão de credenciais é exclusiva do dono logado: POST /api/api-keys aceita apenas o JWT do dashboard (nunca outra API key). Além dos campos existentes (type, label, expires_in_days), a criação aceita:
| Campo | Tipo | Descrição | |
|---|---|---|---|
| scopes | array | opcional | Subconjunto de ["merchant_read", "merchant_write", "wallet_read", "wallet_write"], sem duplicatas. Default: ["merchant_read", "merchant_write"] (o comportamento de sempre). |
| per_tx_limit_cents | integer | opcional | Limite por transação em centavos (mínimo 100). Com scope wallet_write, default 10000 (R$ 100,00) quando omitido. |
| daily_limit_cents | integer | opcional | Limite diário em centavos, janela rolante de 24h (mínimo 100). Com scope wallet_write, default 50000 (R$ 500,00) quando omitido. |
| rate_limit_per_min | integer | opcional | Rate limit adicional por chave (1–600 req/min). Omitido = sem limite próprio; vale só o budget agregado do merchant. |
{
"id": "a1b2c3d4e5f6...",
"key": "sk_test_...", // exibida só uma vez
"prefix": "sk_test_",
"label": "agent-payments",
"is_live": false,
"expires_at": null,
"scopes": ["merchant_read", "merchant_write", "wallet_read", "wallet_write"],
"per_tx_limit_cents": 10000,
"daily_limit_cents": 50000,
"rate_limit_per_min": 30
}
Idempotência
Os POSTs com efeito monetário aceitam o header opcional Idempotency-Key (1–255 caracteres ASCII visíveis). Fortemente recomendado para agentes e para qualquer integração com retry automático: um retry com a mesma chave devolve a resposta original em vez de criar um segundo QR ou uma segunda cobrança.
Endpoints cobertos
POST /api/depositPOST /api/withdrawPOST /api/checkouts
Semântica exata
| Cenário | Resultado |
|---|---|
| Mesma chave + mesmo body | Replay da resposta original (mesmo status, mesmo body) + header Idempotency-Replayed: true. Nenhum efeito colateral novo. |
| Mesma chave + body diferente | 422 idempotency_key_reuse — o handler nunca executa. |
| Mesma chave em endpoint diferente | Independentes — o escopo de unicidade inclui o endpoint. |
| Request concorrente com a mesma chave | 409 idempotency_in_flight com retry_after: 5 — repita em alguns segundos e receba o replay. |
- Escopo de unicidade:
(identidade, endpoint, chave)— chaves de merchants diferentes nunca colidem. - Comparação do body: hash do JSON parseado. Whitespace/formatação não afetam; a ordem dos campos afeta — body semanticamente igual com campos reordenados →
422. Retry byte-idêntico sempre casa. - Respostas
5xxe429nunca são armazenadas — o retry re-executa.4xxdeterminísticos (validação, limites) são armazenados e replayados. - TTL de 24 horas — depois disso a mesma chave vale como nova.
- Operações sandbox (
sk_test_) participam normalmente — o agente treina o fluxo completo.
Exemplo
curl -X POST https://api.depixapp.com/api/deposit \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: agent-run-42-deposit-1" \ -d '{ "amountInCents": 1500, "depixAddress": "lq1qq...", "payer_tax_number": "52998224725" }'
Primeira chamada: 200 com o QR. Retry idêntico: 200 com o mesmo body + Idempotency-Replayed: true. Retry com amountInCents alterado:
{
"response": { "errorMessage": "Idempotency-Key já utilizada com um corpo diferente." },
"error": {
"code": "idempotency_key_reuse",
"message": "This Idempotency-Key was already used with a different request body.",
"request_id": "gru1::abcd-1234",
"docs_url": "https://depixapp.com/docs/en/#errors"
}
}
Erros
Erros carregam um envelope duplo: response.errorMessage (mensagem legada em português, sempre presente e sempre legível para humanos) e o objeto error (contrato estruturado para máquinas, mensagens em inglês). Programe contra error.code — nunca contra o texto das mensagens.
{
"response": { "errorMessage": "Muitas requisições. Tente novamente em 1 minuto." },
"error": {
"code": "rate_limited",
"message": "Too many requests for this scope.",
"request_id": "gru1::iad1::v9x4k-1751476800000-abc123",
"retry_after": 37,
"docs_url": "https://depixapp.com/docs/en/#errors",
"details": { "scope": "deposit" }
}
}
request_id— id de correlação, devolvido em toda resposta (sucesso incluso) no headerX-Request-Id. Cite-o em pedidos de suporte.retry_after— segundos até poder repetir; presente em todo429,503e no409 idempotency_in_flight. Espelhado no header HTTPRetry-After.details— extras opcionais e normativos por code: campo ofensor, scope exigido, limites em centavos (ver a tabela abaixo).- Irmãos legados preservados: erros de validação do criar checkout mantêm
response.errors[](lista por campo) e o 403 de conta bloqueada mantémblocked: true. - Handlers legados fora da superfície de agente ainda podem responder só com
response.errorMessage, sem o objetoerror.
Catálogo de codes
Catálogo completo e fechado. Cada linha tem âncora estável no formato #error-<code> (ex.: #error-rate_limited).
| Code | HTTP | Quando |
|---|---|---|
| unauthorized | 401 | Rota exclusiva de login (JWT) sem token válido. |
| invalid_api_key | 401 | Token com prefixo sk_ não encontrado, revogado ou expirado. |
| invalid_token | 401 | JWT inválido/expirado ou header Authorization ausente/malformado. |
| insufficient_scope | 403 | A API key não tem o scope exigido pela operação — details.required_scope. |
| invalid_password | 401 | A senha da conta informada está incorreta. |
| password_required | 400 | A senha da conta é obrigatória para esta operação sensível. |
| agent_account_no_password | 403 | Contas de agente não têm senha — autentique com o par de chaves do agente. |
| oauth_account_not_linked | 403 | A identidade Google/GitHub não está vinculada a uma conta DePix. Vincule no painel e tente de novo. |
| account_already_linked | 409 | A conta já está vinculada a outra identidade OAuth. Desvincule primeiro. |
| workos_identity_in_use | 409 | Esta identidade OAuth já está vinculada a outra conta DePix. |
| registration_blocked | 403 | O cadastro não pode prosseguir. |
| operator_oauth_failed | 502 | Não foi possível verificar a identidade do operador com o provedor. Tente novamente. |
| account_blocked | 403 | Conta bloqueada (irmão legado blocked: true preservado). |
| merchant_required | 403 | A conta autenticada não tem perfil de lojista ativo. |
| live_access_required | 403 | Criação de chave sk_live_ sem aprovação de acesso à produção. |
| whatsapp_verification_required | 403 | WhatsApp do dono não verificado quando o operador exige verificação. |
| withdraw_disabled | 403 | Saques temporariamente desativados (kill switch global). |
| external_wallet_disabled | 403 | Saques para wallet externa temporariamente desativados. |
| first_withdraw_tax_number_mismatch | 403 | Enquanto a conta não tiver nenhum saque concluído (status sent), todo saque precisa ir para o mesmo CPF/CNPJ que pagou o primeiro depósito concluído da conta. Depois desse primeiro saque, a conta saca para qualquer chave Pix. Conta sem depósito concluído com documento não fica travada, e chaves sk_test_ (sandbox) são isentas. details.anchor_tax_number traz o documento esperado, mascarado. |
| sandbox_only | 403 | simulate-payment chamado em checkout live. |
| validation_error | 400 | Input inválido — details.field quando aplicável; o criar checkout preserva response.errors[]. |
| tax_number_required | 400 | CPF/CNPJ obrigatório ausente (payer_tax_number / taxNumber). |
| amount_out_of_range | 400 | Valor fora dos limites do endpoint — details: { min_cents, max_cents } com os bounds do próprio endpoint/modo. |
| account_limit_exceeded | 400 | Limite de conta atingido. details.limit diz qual: "receive_cap" — o teto de recebimento da janela móvel, com { current_level, cap_cents, used_cents, amount_cents, window_days, resets_at, next_level_requirements, kyc_url }; "first_deposit" — a conta ainda não concluiu nenhum depósito pessoal; "per_tx" — teto legado por transação, aplicado só enquanto o teto por nível não estiver ativo. ("cumulative", o teto vitalício, foi removido em 31/07/2026.) Não dependem de a conta ser verificada: verificar libera as ferramentas de lojista e não altera limite nenhum. Ver Limites. |
| key_limit_exceeded | 400 | Limite de gasto da API key — details: { limit: "per_tx" | "daily", limit_cents, used_cents }. |
| not_found | 404 | Rota ou recurso inexistente — inclui recurso de outra conta (ownership nunca é revelado). |
| conflict | 409 | Conflito de estado: transição de checkout inválida, txid duplicado, slug duplicado. |
| idempotency_in_flight | 409 | Request com a mesma Idempotency-Key ainda em execução — retry_after: 5. |
| idempotency_key_reuse | 422 | Idempotency-Key reutilizada com um body diferente. |
| rate_limited | 429 | Rate limit por IP, por usuário ou por chave — retry_after até 60. |
| merchant_rate_limited | 429 | Budget agregado do merchant excedido — retry_after até 60. |
| payer_velocity_limit | 429 | Muitas transações para o mesmo CPF/CNPJ do pagador em pouco tempo (máx. 2 por janela deslizante de 30 min; depósitos + checkouts combinados) — details: { window_minutes, max_per_window }, retry_after até o restante da janela. |
| platform_shutdown | 503 | Plataforma em manutenção (kill switch global) — retry_after: 300. |
| service_unavailable | 503 | Dependência de infra indisponível — inclui o fail-closed de rotas wallet_* via API key quando o rate limit não pode ser checado — retry_after: 30. |
| upstream_error | 502 | Resposta malformada ou erro do provedor Pix. |
| internal_error | 500 | Erro interno inesperado. Tente novamente em alguns instantes. |
Criar checkout
Cria um novo checkout Pix. Retorna o QR code e a URL de pagamento para exibir ao seu cliente.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| amount | integer | obrigatório | Valor em centavos. Mínimo: 500 (R$ 5,00). Máximo: 600000 (R$ 6.000,00). No trilho depix é o valor de face, antes do desconto do lojista. |
| payer_tax_number | string | obrigatório | CPF ou CNPJ do pagador. Aceita CPF (11 dígitos) ou CNPJ (14 caracteres, inclusive o novo formato alfanumérico), com ou sem máscara. Deve ser um CPF/CNPJ real e registrado — o processador de pagamentos valida além do dígito verificador ao gerar o QR. Obrigatório apenas no trilho pix; no trilho depix é ignorado. |
| payment_method | string | opcional | pix (padrão) ou depix. Com depix a cobrança é paga direto em DePix na rede Liquid, sem QR Pix — ver Receber DePix direto. Se o lojista não tiver o recebimento direto ativado, a criação falha com depix_not_enabled (400). |
| expected_discount_pct | integer | opcional | Só no trilho depix: o desconto (0–90) que a sua página mostrou ao cliente. Se o lojista tiver mudado o desconto nesse meio-tempo, a criação falha com discount_changed (409) trazendo os valores atuais, em vez de cobrar um preço diferente do exibido. |
| description | string | opcional | Descrição do pedido. Máximo 500 caracteres. Exibida na página de pagamento. |
| expires_in | integer | opcional | Tempo de expiração em segundos. Trilho pix: padrão 1200 (20min), mínimo 300 (5min), máximo 1200 (20min). Trilho depix: padrão 1800 (30min), mínimo 300 (5min), máximo 3600 (1h). |
| image_url | string | opcional | URL HTTPS da imagem do produto. Exibida na página de pagamento. |
| callback_url | string | opcional | URL HTTPS que recebe os webhooks do checkout. |
| redirect_url | string | opcional | URL para redirecionar o cliente após o pagamento. |
| metadata | object | opcional | Dados adicionais do seu sistema (order_id, user_id, etc.). Máximo 4KB. Devolvido nos webhooks. |
payer_tax_number além do dígito verificador ao gerar o QR. Um número com checksum válido mas não registrado falha na criação com o erro genérico "Error generating QR Code. Please contact an admin." — se você receber esse erro ao criar, o CPF/CNPJ do pagador quase certamente não é um número real e registrado.
Exemplo
curl -X POST https://api.depixapp.com/api/checkouts \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 2990, "payer_tax_number": "529.982.247-25", "description": "Camiseta tamanho M", "expires_in": 900, "callback_url": "https://minha-loja.com/webhook/depix", "metadata": { "order_id": "ORD-123" } }'
const res = await fetch("https://api.depixapp.com/api/checkouts", { method: "POST", headers: { "Authorization": "Bearer sk_live_<sua-chave>", "Content-Type": "application/json", }, body: JSON.stringify({ amount: 2990, payer_tax_number: "529.982.247-25", description: "Camiseta tamanho M", expires_in: 900, callback_url: "https://minha-loja.com/webhook/depix", metadata: { order_id: "ORD-123" }, }), }); const data = await res.json(); console.log(data.id, data.payment_url);
import requests resp = requests.post( "https://api.depixapp.com/api/checkouts", headers={"Authorization": "Bearer sk_live_<sua-chave>"}, json={ "amount": 2990, "payer_tax_number": "529.982.247-25", "description": "Camiseta tamanho M", "expires_in": 900, "callback_url": "https://minha-loja.com/webhook/depix", "metadata": {"order_id": "ORD-123"}, }, ) data = resp.json() print(data["id"], data["payment_url"])
$ch = curl_init("https://api.depixapp.com/api/checkouts"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer sk_live_<sua-chave>", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "amount" => 2990, "payer_tax_number" => "529.982.247-25", "description" => "Camiseta tamanho M", "expires_in" => 900, "callback_url" => "https://minha-loja.com/webhook/depix", "metadata" => ["order_id" => "ORD-123"], ]), ]); $response = curl_exec($ch); $data = json_decode($response, true); echo $data["id"] . " " . $data["payment_url"];
using var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer sk_live_<sua-chave>"); var payload = new { amount = 2990, payer_tax_number = "529.982.247-25", description = "Camiseta tamanho M", expires_in = 900, callback_url = "https://minha-loja.com/webhook/depix", metadata = new { order_id = "ORD-123" } }; var res = await client.PostAsync( "https://api.depixapp.com/api/checkouts", new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json") ); var json = await res.Content.ReadAsStringAsync(); Console.WriteLine(json);
body := `{"amount":2990,"payer_tax_number":"529.982.247-25","description":"Camiseta tamanho M","expires_in":900,"callback_url":"https://minha-loja.com/webhook/depix","metadata":{"order_id":"ORD-123"}}` req, _ := http.NewRequest("POST", "https://api.depixapp.com/api/checkouts", strings.NewReader(body)) req.Header.Set("Authorization", "Bearer sk_live_<sua-chave>") req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() io.Copy(os.Stdout, resp.Body)
require "net/http" require "json" uri = URI("https://api.depixapp.com/api/checkouts") req = Net::HTTP::Post.new(uri, { "Authorization" => "Bearer sk_live_<sua-chave>", "Content-Type" => "application/json", }) req.body = { amount: 2990, payer_tax_number: "529.982.247-25", description: "Camiseta tamanho M", expires_in: 900, callback_url: "https://minha-loja.com/webhook/depix", metadata: { order_id: "ORD-123" } }.to_json res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts JSON.parse(res.body)
HttpClient client = HttpClient.newHttpClient(); String json = """ {"amount":2990,"payer_tax_number":"529.982.247-25","description":"Camiseta tamanho M","expires_in":900, "callback_url":"https://minha-loja.com/webhook/depix", "metadata":{"order_id":"ORD-123"}}"""; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.depixapp.com/api/checkouts")) .header("Authorization", "Bearer sk_live_<sua-chave>") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body());
{
"id": "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
"status": "pending",
"amount": 2990,
"description": "Camiseta tamanho M",
"image_url": null,
"expires_at": "2025-06-01T15:30:00.000Z",
"is_live": true,
"payment_url": "https://pay.depixapp.com/chk_01jxxxxxxxxxxxxxxxxxxxxxx",
"pix": {
"qr_code": "00020126580014br.gov.bcb.pix..." // payload EMV para QR code
}
}
Cobrar direto em DePix: envie "payment_method": "depix" no mesmo endpoint. A resposta troca o bloco pix por um bloco depix com endereço, valor exato e link de pagamento — ver Receber DePix direto.
Formato da resposta: o POST /api/checkouts devolve o checkout plano, na raiz do JSON (como acima). Já o GET /api/checkouts/:id devolve o mesmo objeto embrulhado em { "checkout": { ... } } — ver Consultar checkout.
payment_url ao seu cliente ou gere um QR code a partir do pix.qr_code. O QR code é compatível com qualquer app de banco.
Consultar checkout
Retorna os detalhes de um checkout específico.
curl https://api.depixapp.com/api/checkouts/chk_01jxxxxxxxxxxxxxxxxxxxxxx \ -H "Authorization: Bearer $DEPIX_API_KEY"
{
"checkout": {
"id": "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
"status": "completed", // pending | processing | approved | completed | cancelled | expired
"amount": 2990,
"description": "Camiseta tamanho M",
"image_url": null,
"callback_url": "https://minha-loja.com/webhook/depix",
"redirect_url": null,
"metadata": { "order_id": "ORD-123" },
"expires_at": "2025-06-01T15:30:00.000Z",
"is_live": true,
"created_at": "2025-06-01T15:00:00.000Z",
"processing_at": "2025-06-01T15:02:00.000Z",
"approved_at": "2025-06-01T15:03:00.000Z",
"completed_at": "2025-06-01T15:22:00.000Z",
"cancelled_at": null,
"blockchain_tx_id": "abc123...def456", // txid Liquid (presente quando completed)
"rejection_reasons": [] // array de motivos quando o pagamento foi devolvido/retido
}
}
Formato da resposta: aqui o checkout vem embrulhado em { "checkout": { ... } }, enquanto o POST /api/checkouts devolve o objeto plano na raiz do JSON — atenção ao parsear os dois.
Enquanto o checkout está pending, a resposta também traz pix_payload (o payload EMV do QR Pix); o campo é omitido assim que o status sai de pending. approved_at está sempre presente (null até a aprovação).
Status possíveis
| Status | Significado |
|---|---|
| pending | Aguardando pagamento. |
| processing | Pix recebido, processando conversão para DePix. |
| approved | Pagamento aprovado pelo banco, aguardando liquidação em DePix. |
| completed | Pagamento confirmado. DePix na carteira do merchant. |
| cancelled | Cancelado/estornado pelo provedor Pix. |
| expired | Prazo de pagamento expirou. |
Motivos de devolução (rejection_reasons)
Quando o pagamento de um checkout é devolvido ou retido pelo provedor, o campo rejection_reasons traz um array com os motivos ([] quando não houve recusa). Novos códigos podem surgir — trate valores desconhecidos de forma genérica.
| Código | Significado |
|---|---|
PAYER_MISMATCH | Pagamento feito com CPF/CNPJ diferente do informado no checkout. |
PAST_DAILY_LIMIT | Limite diário do pagador excedido. |
BLOCKED_USER | Usuário bloqueado pelo provedor. |
HIGH_VELOCITY | Muitas transações do pagador em pouco tempo. |
Listar checkouts
Lista os checkouts do merchant com filtros e paginação.
Query params (todos opcionais)
| Parâmetro | Descrição |
|---|---|
| status | Filtrar por status: pending, processing, approved, completed, cancelled, expired. |
| product_id | Filtrar por produto. Ex: prd_xxx. |
| from | Data de início (ISO 8601). Ex: 2025-06-01T00:00:00Z. |
| to | Data de fim (ISO 8601). |
| q | Busca por ID ou descrição. |
| limit | Número de resultados por página. Padrão: 50. Máximo: 100. |
| offset | Paginação. Padrão: 0. |
curl "https://api.depixapp.com/api/checkouts?status=completed&limit=20" \ -H "Authorization: Bearer $DEPIX_API_KEY"
{
"checkouts": [
{
"id": "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
"status": "completed",
"amount": 2990,
"description": "Camiseta tamanho M",
"product_name": "Camiseta Preta", // null se checkout não vier de um produto
"metadata": "{\"order_id\":\"42\"}", // string JSON, null se ausente
"created_at": "2025-06-01T15:00:00.000Z",
"processing_at": "2025-06-01T15:02:14.000Z",
"approved_at": "2025-06-01T15:03:00.000Z",
"expires_at": "2025-06-01T15:30:00.000Z",
"is_live": true,
"payment_method": "depix", // "pix" ou "depix" — a trilha em que a venda foi liquidada
"depix_discount_pct": 10, // só na trilha depix: desconto oferecido, em %
"depix_due_cents": 2691, // só na trilha depix: o valor que o pagador realmente envia
"rejection_reasons": [], // array de motivos quando o pagamento foi devolvido/retido
"delay_until": null, // quando o dinheiro é liberado, se a venda estiver retida — ISO-8601 COM fuso ("2025-06-15T09:03:00-03:00")
"vault_hours": 0 // horas que essa venda ficou marcada para esperar (0 = nenhuma; null = sem registro)
}
],
"stats": {
"total": 47,
"pending": 2,
"completed": 40,
"completed_amount": 189500 // centavos — R$ 1.895,00
},
"limit": 20,
"offset": 0
}
amount é sempre o preço de tabela. Quando payment_method é "depix", o valor que o cliente realmente envia é depix_due_cents — o preço com o desconto aplicado e ajustado em alguns centavos para baixo, que é como reconhecemos qual pagamento é de qual venda. Somar amount em vendas com desconto superestima cada uma delas. Os dois campos depix_* só aparecem na trilha depix; numa venda em Pix eles não vêm.
processing. Quem diz que ela está retida é vault_hours (as horas para as quais ela foi marcada na criação); quem diz quando o valor cai é delay_until, e só ele. Até a resposta do provedor chegar, delay_until vem null e simplesmente ainda não existe data — created_at + vault_hours não é essa data: a espera conta a partir do pagamento, que é depois da criação, então essa conta cai sempre antes da hora.
vault_hours tem três respostas diferentes: um número maior que zero (ficou retida), 0 (a política olhou e não reteve) e null (não há registro de decisão para essa linha — checkout de sandbox, ou criada antes/enquanto o mecanismo estava desligado). null não é zero.
Atenção ao formato de
delay_until: ele é repassado literalmente pelo provedor de liquidação e vem em ISO-8601 com fuso ("2025-06-15T09:03:00-03:00"), diferente de created_at, expires_at e processing_at, que são UTC sem fuso ("2025-06-01 15:00:00"). Faça o parse como instante ISO-8601 completo: um parser que assume o formato sem fuso e acrescenta um Z gera data inválida em todos os valores reais.
Para conciliar "quanto eu já vendi mas ainda não recebi", liste com
?status=processing e some o amount das linhas que têm delay_until ou vault_hours maior que zero. E lembre que a lista é paginada (limit vai até 100): compare com stats.total, que é contado sobre o filtro inteiro e não sobre a página, ou peça as páginas seguintes por offset — somar uma página só e chamar de total é como uma conciliação sai silenciosamente menor do que a realidade.
Criar produto
Cria um novo produto com valor fixo. Cada produto gera um link de pagamento permanente que pode ser compartilhado com seus clientes.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| name | string | obrigatório | Nome do produto exibido na UI. 2-80 caracteres. |
| slug | string | opcional | Identificador na URL. Se omitido, é gerado automaticamente a partir do name. Letras minúsculas, números e hífens. 2-60 caracteres. Não pode começar/terminar com hífen. |
| amount | integer | obrigatório | Valor em centavos. Mínimo: 500. Máximo: 600000. |
| description | string | opcional | Descrição do produto. Máximo 500 caracteres. |
| image_url | string | opcional | URL HTTPS da imagem do produto. |
| callback_url | string | opcional | URL HTTPS para webhooks. Sobrescreve o default do merchant. |
| redirect_url | string | opcional | URL de redirecionamento. Sobrescreve o default do merchant. |
| metadata | object | opcional | Dados adicionais. Máximo 4KB. Incluído nos webhooks dos checkouts gerados. |
| expires_in | integer | opcional | Tempo de expiração dos checkouts em segundos. Padrão: 1200 (20min). Mínimo: 300 (5min). Máximo: 1200 (20min). |
| kind | string | opcional | product (padrão) ou charge. Uma cobrança (charge) é um link de pagamento com vencimento e encargos, servido em pay.depixapp.com/c/{id} — nunca aparece na loja pública, no carrinho, na vitrine nem na listagem padrão de produtos. Imutável depois de criado. |
| due_date | string | cobrança | Obrigatório quando kind=charge. Primeiro vencimento, YYYY-MM-DD. É a âncora da recorrência. Pode estar no passado — uma cobrança retroativa nasce vencida, que é o caso de quem cobra o mês anterior. |
| recurrence | string | cobrança | null (cobrança única) ou weekly, monthly, quarterly, semiannual, yearly. Mensal e acima ancoram no dia do vencimento, ajustando para o último dia em meses mais curtos (dia 31 → 28/29 em fevereiro). |
| late_fine_bps | integer | cobrança | Multa única por atraso, em basis points do valor base (200 = 2%). Padrão: 0. Máximo: 2000 (20%). |
| late_interest_monthly_bps | integer | cobrança | Juros ao mês em basis points (100 = 1% a.m.), aplicados pro-rata por dia de atraso. Padrão: 0. Máximo: 1000 (10% a.m.). |
Exemplo
curl -X POST https://api.depixapp.com/api/products \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Camiseta M", "slug": "camiseta-m", "amount": 2990, "description": "Camiseta tamanho M" }'
const res = await fetch("https://api.depixapp.com/api/products", { method: "POST", headers: { "Authorization": "Bearer sk_live_<sua-chave>", "Content-Type": "application/json", }, body: JSON.stringify({ name: "Camiseta M", slug: "camiseta-m", amount: 2990, description: "Camiseta tamanho M", }), }); const data = await res.json(); console.log(data.product.payment_url);
import requests resp = requests.post( "https://api.depixapp.com/api/products", headers={"Authorization": "Bearer sk_live_<sua-chave>"}, json={ "name": "Camiseta M", "slug": "camiseta-m", "amount": 2990, "description": "Camiseta tamanho M", }, ) data = resp.json() print(data["product"]["payment_url"])
$ch = curl_init("https://api.depixapp.com/api/products"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer sk_live_<sua-chave>", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "name" => "Camiseta M", "slug" => "camiseta-m", "amount" => 2990, "description" => "Camiseta tamanho M", ]), ]); $response = curl_exec($ch); $data = json_decode($response, true); echo $data["product"]["payment_url"];
using var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer sk_live_<sua-chave>"); var payload = new { name = "Camiseta M", slug = "camiseta-m", amount = 2990, description = "Camiseta tamanho M" }; var res = await client.PostAsync( "https://api.depixapp.com/api/products", new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json") ); Console.WriteLine(await res.Content.ReadAsStringAsync());
body := `{"name":"Camiseta M","slug":"camiseta-m","amount":2990,"description":"Camiseta tamanho M"}` req, _ := http.NewRequest("POST", "https://api.depixapp.com/api/products", strings.NewReader(body)) req.Header.Set("Authorization", "Bearer sk_live_<sua-chave>") req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() io.Copy(os.Stdout, resp.Body)
require "net/http" require "json" uri = URI("https://api.depixapp.com/api/products") req = Net::HTTP::Post.new(uri, { "Authorization" => "Bearer sk_live_<sua-chave>", "Content-Type" => "application/json", }) req.body = { name: "Camiseta M", slug: "camiseta-m", amount: 2990, description: "Camiseta tamanho M" }.to_json res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts JSON.parse(res.body)
HttpClient client = HttpClient.newHttpClient(); String json = """ {"name":"Camiseta M","slug":"camiseta-m","amount":2990,"description":"Camiseta tamanho M"}"""; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.depixapp.com/api/products")) .header("Authorization", "Bearer sk_live_<sua-chave>") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body());
{
"product": {
"id": "prd_xxx",
"name": "Camiseta M",
"slug": "camiseta-m",
"amount": 2990,
"description": "Camiseta tamanho M",
"image_url": null,
"callback_url": null,
"redirect_url": null,
"metadata": null,
"expires_in": 1200,
"active": true,
"is_live": true,
"payment_url": "https://pay.depixapp.com/joao/camiseta-m"
}
}
Listar produtos
Lista os produtos do merchant com filtros e paginação.
Query params (todos opcionais)
| Parâmetro | Descrição |
|---|---|
| kind | Filtrar por tipo: product (padrão), charge ou all. O padrão garante que cobranças nunca apareçam em integrações escritas antes delas; use charge para listar cobranças (cada linha traz charge_state). |
| active | Filtrar por status: 1 (ativos) ou 0 (inativos). |
| q | Busca por nome, slug ou descrição. |
| limit | Número de resultados. Padrão: 50. Máximo: 100. |
| offset | Paginação. Padrão: 0. |
curl "https://api.depixapp.com/api/products?active=1" \ -H "Authorization: Bearer $DEPIX_API_KEY"
{
"products": [
{
"id": "prd_xxx",
"name": "Camiseta M",
"slug": "camiseta-m",
"amount": 2990,
"description": "Camiseta tamanho M",
"active": true,
"is_live": true,
"position": 0,
"payment_url": "https://pay.depixapp.com/joao/camiseta-m"
}
],
"stats": {
"total": 5,
"active": 4
},
"limit": 50,
"offset": 0
}
position — inteiro ou null. Ordem de exibição na vitrine pública. null = não fixado (ordenado por mais vendidos); inteiro = fixado, exibido na posição indicada (menor primeiro).
Consultar produto
Retorna os detalhes de um produto específico, incluindo estatísticas de checkouts.
curl https://api.depixapp.com/api/products/prd_xxx \ -H "Authorization: Bearer $DEPIX_API_KEY"
{
"product": {
"id": "prd_xxx",
"name": "Camiseta M",
"slug": "camiseta-m",
"amount": 2990,
"description": "Camiseta tamanho M",
"image_url": null,
"callback_url": null,
"redirect_url": null,
"metadata": null,
"expires_in": 1200,
"active": true,
"is_live": true,
"position": 0,
"payment_url": "https://pay.depixapp.com/joao/camiseta-m",
"created_at": "2025-06-01T00:00:00.000Z"
}
}
position — inteiro ou null. Ordem de exibição na vitrine pública. null = não fixado (ordenado por mais vendidos); inteiro = fixado, exibido na posição indicada (menor primeiro).
Editar produto
Atualiza um ou mais campos de um produto existente. Envie apenas os campos que deseja alterar.
Parâmetros (todos opcionais)
| Campo | Tipo | Descrição |
|---|---|---|
| name | string | Novo nome do produto. 2-80 caracteres. |
| slug | string | Novo identificador na URL. Mesmas regras da criação. |
| amount | integer | Novo valor em centavos. Mínimo: 500. Máximo: 600000. |
| description | string | Nova descrição. |
| image_url | string | Nova URL de imagem. |
| callback_url | string | Nova URL de webhook. |
| redirect_url | string | Nova URL de redirecionamento. |
| metadata | object | Novos dados adicionais. |
| expires_in | integer | Novo tempo de expiração dos checkouts. Mínimo: 300 (5min). Máximo: 1200 (20min). |
curl -X PATCH https://api.depixapp.com/api/products/prd_xxx \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 3490, "description": "Camiseta tamanho M - Edição Especial" }'
{
"product": {
"id": "prd_xxx",
"name": "Camiseta M",
"slug": "camiseta-m",
"amount": 3490,
"description": "Camiseta tamanho M - Edição Especial",
"active": true,
"is_live": true,
"payment_url": "https://pay.depixapp.com/joao/camiseta-m"
}
}
Ativar / Desativar produto
Ativa ou desativa um produto. Produtos inativos retornam erro 404 quando acessados pelo link de pagamento.
curl -X POST https://api.depixapp.com/api/products/prd_xxx/activate \ -H "Authorization: Bearer $DEPIX_API_KEY"
curl -X POST https://api.depixapp.com/api/products/prd_xxx/deactivate \ -H "Authorization: Bearer $DEPIX_API_KEY"
{ "success": true }
Destacar produtos na vitrine
Define a lista ordenada de produtos fixados no topo da página pública do lojista (a "vitrine"). Os produtos fixados aparecem primeiro, na ordem informada; todo produto que não estiver na lista é desfixado e volta à ordenação por mais vendidos. Enviar uma lista vazia remove todos os destaques. Esta requisição reconcilia o conjunto inteiro de fixados de uma só vez (não é incremental).
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| productIds | array<string> | obrigatório | Lista ordenada de IDs de produtos a fixar no topo da vitrine. Lista vazia remove todos os destaques. Máximo 50. Todos os IDs devem pertencer ao lojista; IDs repetidos são rejeitados. |
Exemplo
curl -X POST https://api.depixapp.com/api/products/featured \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "productIds": ["prd_abc123", "prd_def456"] }'
const res = await fetch("https://api.depixapp.com/api/products/featured", { method: "POST", headers: { "Authorization": "Bearer sk_live_<sua-chave>", "Content-Type": "application/json", }, body: JSON.stringify({ productIds: ["prd_abc123", "prd_def456"], }), }); const data = await res.json(); console.log(data.featured);
import requests resp = requests.post( "https://api.depixapp.com/api/products/featured", headers={"Authorization": "Bearer sk_live_<sua-chave>"}, json={ "productIds": ["prd_abc123", "prd_def456"], }, ) data = resp.json() print(data["featured"])
$ch = curl_init("https://api.depixapp.com/api/products/featured"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer sk_live_<sua-chave>", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "productIds" => ["prd_abc123", "prd_def456"], ]), ]); $response = curl_exec($ch); $data = json_decode($response, true); print_r($data["featured"]);
using var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer sk_live_<sua-chave>"); var payload = new { productIds = new[] { "prd_abc123", "prd_def456" } }; var res = await client.PostAsync( "https://api.depixapp.com/api/products/featured", new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json") ); Console.WriteLine(await res.Content.ReadAsStringAsync());
body := `{"productIds":["prd_abc123","prd_def456"]}` req, _ := http.NewRequest("POST", "https://api.depixapp.com/api/products/featured", strings.NewReader(body)) req.Header.Set("Authorization", "Bearer sk_live_<sua-chave>") req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() io.Copy(os.Stdout, resp.Body)
require "net/http" require "json" uri = URI("https://api.depixapp.com/api/products/featured") req = Net::HTTP::Post.new(uri, { "Authorization" => "Bearer sk_live_<sua-chave>", "Content-Type" => "application/json", }) req.body = { productIds: ["prd_abc123", "prd_def456"] }.to_json res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts JSON.parse(res.body)
HttpClient client = HttpClient.newHttpClient(); String json = """ {"productIds":["prd_abc123","prd_def456"]}"""; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.depixapp.com/api/products/featured")) .header("Authorization", "Bearer sk_live_<sua-chave>") .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body());
{
"success": true,
"featured": ["prd_abc123", "prd_def456"]
}
Erros possíveis: 400 (productIds não é uma lista, IDs repetidos ou mais de 50), 404 (algum produto não pertence ao lojista) e 403 (sem conta de lojista). Veja a seção Erros para o formato das respostas de erro.
Checkouts do produto
Lista os checkouts gerados a partir de um produto específico. Aceita os mesmos filtros da listagem geral de checkouts.
curl "https://api.depixapp.com/api/products/prd_xxx/checkouts?status=completed" \ -H "Authorization: Bearer $DEPIX_API_KEY"
A resposta segue o mesmo formato da listagem de checkouts.
Cobranças
Uma cobrança é um produto com kind=charge: o mesmo endpoint, o mesmo checkout e os mesmos webhooks, com três diferenças — tem data de vencimento, pode ter multa e juros por atraso, e vive num link privado que nunca aparece na sua loja pública. É o formato certo para aluguel, mensalidade, parcela — qualquer valor que vence numa data e pode atrasar.
Crie com POST /api/products passando kind: "charge" e due_date; liste com GET /api/products?kind=charge. O link vem em payment_url no formato https://pay.depixapp.com/c/prd_xxx — endereçado por id (não por slug), com noindex, e a pré-visualização em apps de mensagem mostra o nome da sua loja e o título da cobrança, nunca o valor. Os demais endpoints de produto (GET, PATCH, ativar/desativar, checkouts do produto) funcionam em cobranças sem nenhuma mudança.
Se você tiver o recebimento direto em DePix ativado, a página da cobrança também oferece Pagar com DePix, ao lado do Pix — mesmo fluxo das páginas de loja e de produto. O valor cobrado no trilho DePix é o total do dia (valor original + multa + juros), com o seu desconto aplicado sobre esse total, e a quitação entra na fila da cobrança igual a um pagamento Pix. Nesse trilho não se pede CPF/CNPJ do pagador.
Como o valor é calculado
O valor não é fixo: ele é calculado no momento em que o pagador abre o link e gera o QR.
dias_de_atraso = dias corridos após o vencimento (fuso America/Sao_Paulo) multa = valor_base × late_fine_bps / 10000 // uma vez só juros = valor_base × late_interest_monthly_bps / 10000 × dias_de_atraso / 30 total = min(valor_base + multa + juros, 600000) // teto por transação
O dia do vencimento não conta como atraso — o atraso começa no dia seguinte. Não há adiamento para fim de semana ou feriado: o Pix funciona 24/7. Os juros são lineares (pro-rata por dia), nunca compostos. Não há correção monetária.
Recorrência e quitação (FIFO)
Com recurrence, a cobrança vira uma série de vencimentos ancorada em due_date, e o mesmo link serve para sempre. Cada pagamento quita a competência mais antiga em aberto; a próxima visita já mostra a seguinte. A competência quitada por cada checkout vai em metadata.charge_cycle, e o webhook traz data.product_id — juntos, dizem exatamente qual mês foi pago.
Duas regras que decidem qual competência um QR cobra, e valem a leitura antes de integrar:
- Um QR em aberto reserva a competência dele. O valor e a competência são congelados quando o QR é criado, mas a posição na série é resolvida na liquidação. Se dois QRs vivos fossem precificados como a mesma competência e ambos fossem pagos, o segundo pagador levaria a multa e os juros de uma competência que não estava atrasada. Por isso um QR ainda válido conta para a posição: o próximo QR já sai precificado na competência que ele realmente vai quitar. QRs expirados não contam.
- A posição nunca anda para trás. Conta-se competência que já liquidou alguma vez. Um estorno (MED, reembolso por divergência de CPF) portanto não faz o link recobrar uma competência já paga nem aplicar os encargos do ciclo errado — e também não recobra sozinho a competência estornada. Isso é decisão do lojista: você recebe
checkout.cancellede vê a venda cancelada, e decide o que fazer.
charge_state
Presente em cada linha de GET /api/products?kind=charge e na resposta 201 do checkout de uma cobrança.
{
"settled": false, // true = cobrança única já paga (demais campos ausentes)
"cycle_due_date": "2026-08-05", // competência corrente (mais antiga em aberto)
"days_late": 10,
"base_cents": 250000,
"fine_cents": 5000,
"interest_cents": 833,
"total_today_cents": 255833, // o que um QR criado agora cobra
"capped": false, // true = base + encargos passou do teto e foi limitado
"status": "late", // late | due_today | upcoming
"open_past_due_cycles": 1, // > 1 = competências acumuladas
"in_flight": false // um Pix pago desta cobrança está liquidando
}
Erros específicos
| Código | HTTP | Quando |
|---|---|---|
charge_already_paid | 409 | Cobrança única já quitada — não há competência a pagar. |
charge_payment_in_progress | 409 | Já existe um Pix pago desta cobrança em processamento. Gerar outro QR agora viraria pagamento em dobro. |
charge_payment_pending | 409 | Um QR ainda válido já reserva a única competência restante (cobrança única). Não é o mesmo que "já foi paga": ninguém pagou nada ainda. |
Links de pagamento
O DePix App gera links de pagamento permanentes para produtos e para a página do merchant. Esses links criam checkouts sob demanda quando o cliente acessa.
Tipos de link
| Tipo | URL | Comportamento |
|---|---|---|
| Produto | https://pay.depixapp.com/{merchant_slug}/{slug} | Valor fixo. O cliente vê o produto e clica "Pagar com PIX". |
| Merchant | https://pay.depixapp.com/{merchant_slug} | Valor livre. O cliente digita o valor e clica "Pagar com PIX". |
/api/merchants/:username/public e /api/products/:id/public (o segmento :username na URL é, na prática, o merchant_slug — o nome do parâmetro foi mantido por compatibilidade). Quando o lojista altera o nome do negócio, a slug é regenerada — links antigos enviados a clientes deixam de funcionar e precisam ser reenviados.
Ciclo de vida
- Quando o cliente acessa o link e inicia o pagamento, um checkout individual é criado automaticamente.
- A partir daí, o ciclo de vida é idêntico a um checkout criado via API (status, webhooks, expiração).
- O
callback_urlsegue a cadeia: campo do produto (se houver) → default do merchant → null. - O
redirect_urlsegue a mesma cadeia.
Produto público
Retorna os dados públicos de um produto ativo. Não requer autenticação.
curl https://api.depixapp.com/api/products/prd_xxx/public
{
"product": {
"id": "prd_xxx",
"name": "Camiseta M",
"slug": "camiseta-m",
"amount": 2990,
"description": "Camiseta tamanho M",
"image_url": null
},
"merchant": {
"name": "Loja do Joao",
"merchant_slug": "joao",
"username": "joao"
}
}
Checkout do produto
Cria um checkout a partir de um produto ativo. Não requer autenticação. O valor é herdado do produto.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| payer_tax_number | string | obrigatório | CPF ou CNPJ de quem paga o Pix, com ou sem máscara. Deve ser um CPF/CNPJ real e registrado — o processador de pagamentos valida além do dígito verificador ao gerar o QR. Obrigatório apenas no trilho pix. |
| payment_method | string | opcional | pix (padrão) ou depix — ver Receber DePix direto. No trilho depix não se envia CPF/CNPJ. |
| expected_discount_pct | integer | opcional | Só no trilho depix: o desconto (0–90) que a sua página mostrou. Divergiu do atual? discount_changed (409). |
curl -X POST https://api.depixapp.com/api/products/prd_xxx/checkout \ -H "Content-Type: application/json" \ -d '{ "payer_tax_number": "52998224725" }'
A resposta segue o mesmo formato do criar checkout (status 201).
error.details quando ela fala de quem paga, do que a pessoa digitou ou do que está sendo pago — que é o caso de todos os códigos listados acima. Recusas que pertencem à conta do lojista (limite de recebimento, pendências de cadastro, configuração da conta) respondem uma mensagem neutra e sem error.details. O error.code continua sendo o real. Se a sua integração precisa dos números, faça a chamada autenticada como lojista em POST /api/checkouts.
Página do merchant
Retorna os dados públicos do merchant. Não requer autenticação.
curl https://api.depixapp.com/api/merchants/joao/public
{
"merchant": {
"name": "Loja do Joao",
"merchant_slug": "joao",
"username": "joao"
}
}
Checkout do merchant
Cria um checkout com valor customizado a partir da página do merchant. Não requer autenticação.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| amount | integer | obrigatório | Valor em centavos. Mínimo: 500. Máximo: 600000. |
| payer_tax_number | string | obrigatório | CPF ou CNPJ de quem paga o Pix, com ou sem máscara. Deve ser um CPF/CNPJ real e registrado — o processador de pagamentos valida além do dígito verificador ao gerar o QR. Obrigatório apenas no trilho pix. |
| payment_method | string | opcional | pix (padrão) ou depix — ver Receber DePix direto. No trilho depix não se envia CPF/CNPJ. |
| expected_discount_pct | integer | opcional | Só no trilho depix: o desconto (0–90) que a sua página mostrou. Divergiu do atual? discount_changed (409). |
curl -X POST https://api.depixapp.com/api/merchants/joao/checkout \ -H "Content-Type: application/json" \ -d '{ "amount": 5000, "payer_tax_number": "52998224725" }'
A resposta segue o mesmo formato do criar checkout (status 201).
error.details. Os números completos só saem na chamada autenticada, em POST /api/checkouts.
Receber DePix direto (Liquid)
Todo checkout pode ser cobrado por um de dois trilhos, escolhido no campo payment_method. O padrão (pix) é o QR Pix de sempre. O alternativo (depix) cobra direto em DePix: quem paga envia DePix de carteira para carteira na rede Liquid, para um endereço exclusivo daquele lojista, e o DePix App acompanha a rede para confirmar o pagamento. Não existe QR Pix, não se pede CPF do pagador, e o dinheiro cai direto na carteira do lojista.
Como o lojista ativa
O recebimento direto é ligado pelo próprio dono da conta no DePix App, em Receber com DePix (confirmação de senha). Ao ativar, o app cria um endereço exclusivo para esses recebimentos — separado do endereço que recebe as liquidações de Pix — e o lojista escolhe um desconto de 0% a 90% para quem pagar por esse trilho. Não existe endpoint de API key para ligar o trilho nem para mudar o desconto: é sempre o dono, logado, com senha.
Enquanto o lojista não ativar, qualquer criação com payment_method: "depix" devolve depix_not_enabled (400) — o trilho Pix continua funcionando normalmente.
Criar uma cobrança em DePix
Mesmos endpoints de sempre (POST /api/checkouts, checkout do produto e checkout do merchant), trocando só o trilho.
curl -X POST https://api.depixapp.com/api/checkouts \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 9990, "payment_method": "depix", "description": "Pedido #124", "expires_in": 1800, "expected_discount_pct": 10 }'
const res = await fetch("https://api.depixapp.com/api/checkouts", { method: "POST", headers: { "Authorization": "Bearer sk_live_<sua-chave>", "Content-Type": "application/json", }, body: JSON.stringify({ amount: 9990, payment_method: "depix", description: "Pedido #124", expires_in: 1800, expected_discount_pct: 10, }), }); const data = await res.json(); console.log(data.depix.amount, data.depix.uri);
import requests resp = requests.post( "https://api.depixapp.com/api/checkouts", headers={"Authorization": "Bearer sk_live_<sua-chave>"}, json={ "amount": 9990, "payment_method": "depix", "description": "Pedido #124", "expires_in": 1800, "expected_discount_pct": 10, }, ) data = resp.json() print(data["depix"]["amount"], data["depix"]["uri"])
{
"id": "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
"status": "pending",
"amount": 9990,
"description": "Pedido #124",
"image_url": null,
"expires_at": "2026-07-29 12:30:00",
"is_live": true,
"payment_url": "https://pay.depixapp.com/chk_01jxxxxxxxxxxxxxxxxxxxxxx",
"payment_method": "depix",
"depix": {
"address": "lq1qqw8re6vg9dqfazzsx4h9pkq6trxfmk8n0h0ykr7v9k8xn7pdrjq...",
"amount_cents": 8991,
"amount": "89.91",
"asset_id": "02f22f8d9c76ab41661a2729e4752e2c5d1a263012141b86ea98af5472df5189",
"uri": "liquidnetwork:lq1qqw8re6...?amount=89.91&assetid=02f22f8d...&depixid=chk_01jxxx...",
"discount_pct": 10,
"original_amount_cents": 9990,
"detected": false
}
}
O checkout em DePix não traz o bloco pix — ele simplesmente não existe nesse trilho. Um checkout Pix, por sua vez, não traz o bloco depix. Sempre olhe payment_method antes de ler o payload de pagamento.
O objeto depix
| Campo | Tipo | Descrição |
|---|---|---|
address | string | Endereço Liquid confidencial (lq1…) exclusivo dos recebimentos diretos desse lojista. Em modo teste é um endereço de mentira: não envie nada para ele. |
amount_cents | integer | Valor exato a enviar, em centavos. É o valor de face menos o desconto do lojista e menos um ajuste de até 99 centavos (sempre para baixo) que torna esse valor único entre as cobranças abertas do lojista. |
amount | string | O mesmo valor no formato que a carteira assina ("89.91"). Exiba e transmita exatamente assim, sem arredondar. |
asset_id | string | Identificador do DePix na rede Liquid. Enviar qualquer outra moeda para esse endereço perde o dinheiro. |
uri | string | Link de pagamento com endereço, valor, moeda e o id desta cobrança já embutidos (liquidnetwork:…?amount=…&assetid=…&depixid=…). É o que você entrega para a carteira — assim ninguém digita valor à mão. Qualquer carteira BIP21 ignora o depixid; o DePix App usa ele para reler a cobrança e conferir endereço e situação antes de oferecer o pagamento, então uma URI escrita por outra pessoa é recusada em vez de paga. O link nunca leva o nome da loja: nome dentro da URI é nome na área de transferência do pagador, onde qualquer um escreve. null em modo teste. |
discount_pct | integer | Desconto do lojista aplicado nesse trilho, de 0 a 90. |
original_amount_cents | integer | Valor de face, antes do desconto e do ajuste de centavos — o mesmo amount do checkout. |
detected | boolean | true quando um pagamento compatível já apareceu na rede mas ainda não foi confirmado. Serve só para a tela dizer "recebemos, confirmando"; o status continua pending e nenhum webhook é disparado ainda. |
Fluxo de status
| Status | Quando acontece | Webhook |
|---|---|---|
pending | Aguardando o pagamento. depix.detected vira true assim que a transação aparece na rede (segundos). | — |
approved | 1ª confirmação na rede (~1 minuto) e valor casado com essa cobrança. O dinheiro já está na carteira do lojista — este é o ponto seguro para liberar o pedido. | checkout.approved |
completed | 2ª confirmação. Terminal. | checkout.completed |
expired | Terminal, disparado 15 minutos depois de expires_at: é a janela de tolerância para que um pagamento enviado nos últimos segundos ainda confirme e seja creditado. | checkout.expired |
Os status processing e cancelled não são usados nesse trilho. Enquanto o tempo mostra 0 e o status ainda é pending, continue consultando: é a janela de tolerância acima, não uma cobrança travada.
Por que o valor tem centavos "quebrados"?
O pagamento é identificado pelo valor exato. Para que duas cobranças abertas do mesmo lojista nunca tenham o mesmo valor, a API pode baixar alguns centavos — o valor pode variar até R$ 0,99 para baixo, sempre a favor de quem paga. Por isso o valor cobrado (depix.amount_cents) pode ser alguns centavos menor que o valor de face menos o desconto. Cobre e concilie sempre pelo valor que a API devolveu, nunca por um valor recalculado por você.
Uma consequência prática: pagamento com valor diferente do informado não é creditado automaticamente — ele vira um recebimento não atribuído (o lojista vê no app e recebe o webhook checkout.unmatched_payment). O dinheiro está na carteira do lojista; só a associação automática com o pedido não aconteceu.
Webhooks e conciliação
Nos eventos checkout.* desse trilho, amount continua sendo o valor de face, e o valor efetivamente pago vem em amount_received (com discount_pct e payment_method ao lado). Libere o pedido pelo amount_received. Se você concilia consultando a cobrança em vez de escutar o webhook, o bloco depix do GET /api/checkouts/{id} continua lá depois do pagamento — depix.amount_cents é o valor que entrou (o uri vem null, porque não há mais nada a pagar). Recebimentos que não casam com nenhuma cobrança geram o evento checkout.unmatched_payment, enviado para o default_callback_url do lojista.
depix_not_enabled 400 lojista não aceita recebimento direto em DePix depix_busy 409 sem valor único disponível agora — ofereça o Pix discount_changed 409 o desconto mudou (details traz os valores atuais) depix_address_unsupported 400 endereço de recebimento não é um lq1... confidencial depix_address_conflict 400 o endereço precisa ser exclusivo desse recebimento invalid_blinding_key 400 a chave de leitura não corresponde ao endereço
Os três últimos só aparecem no fluxo de ativação feito pelo dono da conta no app; uma integração por API key nunca os encontra.
sk_test_) o trilho DePix não emite destino pagável: o endereço é um placeholder, uri vem null e não há QR. Conclua a cobrança de teste pelo simular pagamento, como no trilho Pix.
Depósito e Saque (scopes wallet_*)
Além de receber por checkouts, uma API key com os scopes wallet_* movimenta o lado "pagar" da conta: gera QR Pix de depósito pessoal (on-ramp BRL → DePix) e cria saques DePix → Pix (off-ramp). É a mesma superfície que a UI humana usa — limites, delays e verificação da conta valem por construção.
sdk.deposit(...) gera o QR e acompanha a liquidação; sdk.withdraw(...) cota, monta a transação, assina do lado do cliente e acompanha a liquidação. O REST abaixo é o contrato completo que o próprio SDK consome — documentado integralmente para quem preferir integrar direto.
Taxas
As taxas são descontadas do valor — o que o pagador envia não é o que o destino recebe. Planeje pelo valor líquido:
| Fluxo | Taxa | R$ 100,00 viram |
|---|---|---|
| Depósito (BRL → DePix) | 2% + R$ 0,99 | R$ 97,01 em DePix |
| Saque até R$ 100,00 | 1% + R$ 1,00 | R$ 98,00 na chave Pix |
| Saque acima de R$ 100,00 | 2% | — |
As duas faixas de saque se encontram sem degrau: em exatamente R$ 100,00 as duas regras custam R$ 2,00. As taxas podem mudar — a tabela canônica e sempre atualizada é o quadro de taxas em depixapp.com; trate os valores acima como ilustração do formato, não como contrato.
O ativo DePix na Liquid
O DePix é um ativo emitido na Liquid mainnet, com 8 casas decimais e paridade 1:1 com o BRL. O id do ativo é:
02f22f8d9c76ab41661a2729e4752e2c5d1a263012141b86ea98af5472df5189
Você precisa dele sempre que montar a transação Liquid por conta própria em vez de deixar o SDK montar — em especial a saída da taxa de saque, que precisa pagar fee_cents para fee_address como saída DePix explícita (não-blindada) na mesma transação. Pagar o ativo errado, ou pagar blindado, faz o saque falhar e pode perder os fundos.
O fluxo de depósito (on-ramp)
- 1.
POST /api/deposit→ QR Pix (qrCopyPaste) +id. - 2. O dono da conta paga o QR num app de banco.
- 3. Acompanhe por polling em
GET /api/deposits/:id(5–15s) e/ou pelos webhooksdeposit.*até o status terminaldepix_sent— o DePix chegou no endereço Liquid informado.
O depósito pessoal conta para a verificação da conta. Alternativa de on-ramp: criar um checkout contra si mesmo — note que pagamentos de checkout não contam para a verificação.
O fluxo de saque (off-ramp)
- 1.
POST /api/withdraw→ cotação comdepositAddress(endereço Liquid do provedor). - 2. Envie o DePix para o
depositAddressa partir da sua wallet — a assinatura é sempre do lado do cliente; a API jamais toca chave privada ou custodia fundos. - 3. Acompanhe por
GET /api/withdrawals/:ide/ou pelos webhookswithdraw.*atésent— o Pix chegou na chave de destino.
Limites
Duas camadas, sempre em conjunto (AND):
- Limites de conta — herdados da conta do dono e sempre prevalecentes. Cobrem primeiro depósito, valor por transação, atraso nos primeiros depósitos e tetos máximos de depósito e saque. Os valores dependem do nível de verificação da conta e podem mudar — não os codifique. Ao exceder →
400 account_limit_exceeded, comdetailsinformandolimit_cents/used_centsvigentes. - Limites de chave — os limites de gasto que o dono definiu ao criar a chave
wallet_write(per_tx_limit_cents,daily_limit_cents; ver Scopes e limites), visíveis emGET /api/api-keys. Ao exceder →400 key_limit_exceeded(details.limit= per_tx | daily).
Sandbox sintético (sk_test_)
Com uma chave sk_test_, deposit e withdraw respondem com payloads sintéticos marcados "sandbox": true: strings SANDBOX-…-DO-NOT-PAY impagáveis, ids sandbox_*, zero dinheiro, zero chamada ao provedor, zero registro criado. Todos os gates de conta e o limite por transação da chave são checados normalmente — o sandbox ensina os limites reais. Ver Sandbox.
Criar depósito
Gera um QR Pix de depósito pessoal. Quando o Pix é pago, o DePix é entregue no endereço Liquid informado. Exige scope wallet_write. Aceita Idempotency-Key.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| amountInCents | integer | obrigatório | Valor em centavos. Mínimo: 500 (R$ 5,00). Máximo: 600000 (R$ 6.000,00). Limites da conta e da chave podem restringir mais. |
| depixAddress | string | obrigatório | Endereço Liquid que recebe o DePix quando o Pix liquidar. |
| payer_tax_number | string | obrigatório | CPF ou CNPJ de quem paga o Pix, com ou sem máscara. Deve ser um CPF/CNPJ real e registrado — o processador de pagamentos valida além do dígito verificador ao gerar o QR. |
payer_tax_number além do dígito verificador ao gerar o QR. Um número com checksum válido mas não registrado falha na criação com o erro genérico "Error generating QR Code. Please contact an admin." — se você receber esse erro ao criar, o CPF/CNPJ do pagador quase certamente não é um número real e registrado.
Exemplo
curl -X POST https://api.depixapp.com/api/deposit \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: dep-pedido-42" \ -d '{ "amountInCents": 5000, "depixAddress": "lq1qq...", "payer_tax_number": "529.982.247-25" }'
const res = await fetch("https://api.depixapp.com/api/deposit", { method: "POST", headers: { "Authorization": "Bearer sk_live_<sua-chave>", "Content-Type": "application/json", "Idempotency-Key": "dep-pedido-42", }, body: JSON.stringify({ amountInCents: 5000, depixAddress: "lq1qq...", payer_tax_number: "529.982.247-25", }), }); const data = await res.json(); console.log(data.response.qrCopyPaste, data.response.id);
import requests resp = requests.post( "https://api.depixapp.com/api/deposit", headers={ "Authorization": "Bearer sk_live_<sua-chave>", "Idempotency-Key": "dep-pedido-42", }, json={ "amountInCents": 5000, "depixAddress": "lq1qq...", "payer_tax_number": "529.982.247-25", }, ) data = resp.json() print(data["response"]["qrCopyPaste"], data["response"]["id"])
$ch = curl_init("https://api.depixapp.com/api/deposit"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer sk_live_<sua-chave>", "Content-Type: application/json", "Idempotency-Key: dep-pedido-42", ], CURLOPT_POSTFIELDS => json_encode([ "amountInCents" => 5000, "depixAddress" => "lq1qq...", "payer_tax_number" => "529.982.247-25", ]), ]); $response = curl_exec($ch); $data = json_decode($response, true); echo $data["response"]["qrCopyPaste"];
using var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer sk_live_<sua-chave>"); client.DefaultRequestHeaders.Add("Idempotency-Key", "dep-pedido-42"); var payload = new { amountInCents = 5000, depixAddress = "lq1qq...", payer_tax_number = "529.982.247-25" }; var res = await client.PostAsync( "https://api.depixapp.com/api/deposit", new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json") ); var json = await res.Content.ReadAsStringAsync(); Console.WriteLine(json);
body := `{"amountInCents":5000,"depixAddress":"lq1qq...","payer_tax_number":"529.982.247-25"}` req, _ := http.NewRequest("POST", "https://api.depixapp.com/api/deposit", strings.NewReader(body)) req.Header.Set("Authorization", "Bearer sk_live_<sua-chave>") req.Header.Set("Content-Type", "application/json") req.Header.Set("Idempotency-Key", "dep-pedido-42") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() io.Copy(os.Stdout, resp.Body)
require "net/http" require "json" uri = URI("https://api.depixapp.com/api/deposit") req = Net::HTTP::Post.new(uri, { "Authorization" => "Bearer sk_live_<sua-chave>", "Content-Type" => "application/json", "Idempotency-Key" => "dep-pedido-42", }) req.body = { amountInCents: 5000, depixAddress: "lq1qq...", payer_tax_number: "529.982.247-25" }.to_json res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts JSON.parse(res.body)
HttpClient client = HttpClient.newHttpClient(); String json = """ {"amountInCents":5000,"depixAddress":"lq1qq...","payer_tax_number":"529.982.247-25"}"""; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.depixapp.com/api/deposit")) .header("Authorization", "Bearer sk_live_<sua-chave>") .header("Content-Type", "application/json") .header("Idempotency-Key", "dep-pedido-42") .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body());
{
"async": false,
"response": {
"qrCopyPaste": "00020126580014br.gov.bcb.pix...", // payload EMV para QR code
"qrImageUrl": "https://depix.eulen.app/qr/qr-id-456.png",
"id": "qr-id-456" // use no GET /api/deposits/:id
}
}
{
"async": false,
"response": {
"qrCopyPaste": "SANDBOX-DEPIX-TEST-MODE-DO-NOT-PAY-a1b2c3d4e5f60708",
"qrImageUrl": null,
"id": "sandbox_3uw_a1b2c3d4e5f60708", // sandbox_<amount36>_<hex> — 5000 → 3uw
"sandbox": true
}
}
400 com error.code = "validation_error" e a mensagem do provedor preservada em response.errorMessage (mesmo comportamento do POST /api/withdraw). Programe contra o status HTTP: 2xx = QR emitido, 4xx = recusa. A mensagem humana continua em response.errorMessage.
Status do depósito
Consulta o status de um depósito criado via POST /api/deposit. Ownership é obrigatório: id de outra conta → 404. Faça polling a cada 5–15 segundos até um status terminal — depix_sent é o sucesso terminal.
curl https://api.depixapp.com/api/deposits/qr-id-456 \ -H "Authorization: Bearer $DEPIX_API_KEY"
{
"id": "qr-id-456",
"type": "deposit",
"amount_cents": 5000,
"status": "depix_sent",
"created_at": "2026-07-01 12:00:00",
"updated_at": "2026-07-01 12:34:56",
"rejection_reasons": [] // sempre presente; [] quando não houve recusa
}
Status possíveis
| Status | Terminal | Significado |
|---|---|---|
| pending | — | QR gerado; Pix ainda não pago. |
| under_review | — | Pix pago; pagamento em análise pré-liquidação. |
| pending_pix2fa | — | Pix pago; aguardando o pagador completar o 2FA do Pix. |
| approved | — | Pix aprovado pelo provedor; DePix ainda não enviado. |
| delayed | — | Liquidação retida pela política de delay (contas novas/valores altos). |
| will_refund | — | Fluxo de reembolso iniciado; o depósito será reembolsado. |
| depix_sent | sim | Sucesso: DePix entregue no endereço Liquid de destino. |
| refunded | sim | Depósito reembolsado ao pagador. |
| canceled | sim | Cancelado pelo provedor. |
| error | sim | Erro de processamento no provedor. |
| expired | sim | QR expirou sem pagamento. |
Motivos de devolução (rejection_reasons)
A resposta sempre traz rejection_reasons como array — [] quando não houve recusa. Assim um agente em polling lê o campo incondicionalmente, sem checar existência. Ele é preenchido quando o pagamento foi devolvido ou retido pelo provedor (tipicamente nos status will_refund, refunded e error). Novos códigos podem surgir — exiba valores desconhecidos como recebidos. Saques não têm rejection_reasons.
| Código | Significado |
|---|---|
PAYER_MISMATCH | Pagamento feito com CPF/CNPJ diferente do informado no depósito. |
PAST_DAILY_LIMIT | Limite diário do pagador excedido. |
BLOCKED_USER | Usuário bloqueado pelo provedor. |
HIGH_VELOCITY | Muitas transações do pagador em pouco tempo. |
sk_test_, um id sandbox_* devolve sempre a resposta sintética fixa { "id": "sandbox_3uw_a1b2c3d4e5f60708", "type": "deposit", "amount_cents": 5000, "status": "depix_sent", "created_at": "2026-01-01 00:00:00", "updated_at": "2026-01-01 00:00:00", "sandbox": true, "rejection_reasons": [] } — mesmo shape da resposta live (inclui amount_cents, decodificado do id, e timestamps determinísticos) para exercitar o loop de polling completo em modo test. O amount_cents é null apenas em ids legados sem valor embutido. Qualquer outro id via sk_test_ → 404; ids sandbox_* via chave live ou JWT → 404.
Criar saque
Cota um saque DePix → Pix: a resposta traz o endereço Liquid do provedor para onde você envia o DePix. Depois de transmitir a transação, acompanhe o status. Exige scope wallet_write. Aceita Idempotency-Key.
Parâmetros
Envie exatamente um entre depositAmountInCents (modo "você envia") e payoutAmountInCents (modo "você recebe") — os mesmos dois modos da UI humana.
| Campo | Tipo | Descrição | |
|---|---|---|---|
| pixKey | string | obrigatório | Chave Pix de destino (e-mail, telefone, CPF/CNPJ ou chave aleatória). |
| depositAmountInCents | integer | um dos dois | Modo "você envia": quanto DePix você entrega, em centavos. Mínimo: 500. Máximo: 600000 (R$ 6.000,00). Mutuamente exclusivo com payoutAmountInCents. |
| payoutAmountInCents | integer | um dos dois | Modo "você recebe": quanto a chave de destino recebe, em centavos. Mínimo: 500. Máximo: 588000 (ajustado pela taxa). Mutuamente exclusivo com depositAmountInCents. |
| taxNumber | string | obrigatório | CPF ou CNPJ do titular da chave Pix de destino. |
Exemplo
curl -X POST https://api.depixapp.com/api/withdraw \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: saque-pedido-42" \ -d '{ "pixKey": "alguem@exemplo.com", "depositAmountInCents": 10000, "taxNumber": "529.982.247-25" }'
const res = await fetch("https://api.depixapp.com/api/withdraw", { method: "POST", headers: { "Authorization": "Bearer sk_live_<sua-chave>", "Content-Type": "application/json", "Idempotency-Key": "saque-pedido-42", }, body: JSON.stringify({ pixKey: "alguem@exemplo.com", depositAmountInCents: 10000, taxNumber: "529.982.247-25", }), }); const data = await res.json(); console.log(data.response.withdrawalId, data.response.depositAddress);
import requests resp = requests.post( "https://api.depixapp.com/api/withdraw", headers={ "Authorization": "Bearer sk_live_<sua-chave>", "Idempotency-Key": "saque-pedido-42", }, json={ "pixKey": "alguem@exemplo.com", "depositAmountInCents": 10000, "taxNumber": "529.982.247-25", }, ) data = resp.json() print(data["response"]["withdrawalId"], data["response"]["depositAddress"])
$ch = curl_init("https://api.depixapp.com/api/withdraw"); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer sk_live_<sua-chave>", "Content-Type: application/json", "Idempotency-Key: saque-pedido-42", ], CURLOPT_POSTFIELDS => json_encode([ "pixKey" => "alguem@exemplo.com", "depositAmountInCents" => 10000, "taxNumber" => "529.982.247-25", ]), ]); $response = curl_exec($ch); $data = json_decode($response, true); echo $data["response"]["depositAddress"];
using var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer sk_live_<sua-chave>"); client.DefaultRequestHeaders.Add("Idempotency-Key", "saque-pedido-42"); var payload = new { pixKey = "alguem@exemplo.com", depositAmountInCents = 10000, taxNumber = "529.982.247-25" }; var res = await client.PostAsync( "https://api.depixapp.com/api/withdraw", new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json") ); var json = await res.Content.ReadAsStringAsync(); Console.WriteLine(json);
body := `{"pixKey":"alguem@exemplo.com","depositAmountInCents":10000,"taxNumber":"529.982.247-25"}` req, _ := http.NewRequest("POST", "https://api.depixapp.com/api/withdraw", strings.NewReader(body)) req.Header.Set("Authorization", "Bearer sk_live_<sua-chave>") req.Header.Set("Content-Type", "application/json") req.Header.Set("Idempotency-Key", "saque-pedido-42") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() io.Copy(os.Stdout, resp.Body)
require "net/http" require "json" uri = URI("https://api.depixapp.com/api/withdraw") req = Net::HTTP::Post.new(uri, { "Authorization" => "Bearer sk_live_<sua-chave>", "Content-Type" => "application/json", "Idempotency-Key" => "saque-pedido-42", }) req.body = { pixKey: "alguem@exemplo.com", depositAmountInCents: 10000, taxNumber: "529.982.247-25" }.to_json res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) } puts JSON.parse(res.body)
HttpClient client = HttpClient.newHttpClient(); String json = """ {"pixKey":"alguem@exemplo.com","depositAmountInCents":10000,"taxNumber":"529.982.247-25"}"""; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.depixapp.com/api/withdraw")) .header("Authorization", "Bearer sk_live_<sua-chave>") .header("Content-Type", "application/json") .header("Idempotency-Key", "saque-pedido-42") .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body());
{
"response": {
"withdrawalId": "wd-123", // use no GET de status
"depositAddress": "lq1qq2v9wxkyz...", // envie o DePix do saque para cá
"depositAmountInCents": 9900, // o que o provedor recebe
"payoutAmountInCents": 9700, // o que a chave Pix recebe
"totalDepositAmountInCents": 10000, // saída bruta da wallet (provedor + taxa)
"split": { "address": "ex1qfee...", "amountCentavos": 100 },
"fee_cents": 100, // taxa da plataforma — OBRIGATÓRIA na mesma transação
"fee_address": "ex1qfee..." // endereço da taxa (forma não-confidencial)
}
}
A resposta via API key inclui fee_cents e fee_address: a taxa da plataforma que a sua transação Liquid deve pagar como uma segunda saída explícita (não-blindada, asset DePix) para o fee_address, na mesma transação da saída principal para o depositAddress. Pague o fee_address exatamente como recebido — ele vem na forma não-confidencial (ex1...) de propósito: uma saída confidencial/blindada não pode ser verificada e conta como taxa não paga. A taxa é verificada automaticamente na transação Liquid que paga o saque.
{
"response": {
"withdrawalId": "sandbox_0011223344556677",
"depositAddress": "SANDBOX-LIQUID-ADDRESS-DO-NOT-PAY",
"depositAmountInCents": 10000,
"payoutAmountInCents": 9800, // taxa sintética fixa de 2%; a taxa real varia
"fee_cents": 100, // taxa sintética determinística de 1%
"fee_address": "SANDBOX-LIQUID-FEE-ADDRESS-DO-NOT-PAY",
"sandbox": true
}
}
Status do saque
Consulta o status de um saque criado via POST /api/withdraw. Ownership é obrigatório: id de outra conta → 404. Faça polling a cada 5–15 segundos até um status terminal — sent é o sucesso terminal.
curl https://api.depixapp.com/api/withdrawals/wd-123 \ -H "Authorization: Bearer $DEPIX_API_KEY"
{
"id": "wd-123",
"type": "withdraw",
"amount_cents": 10000, // lado enviado (depositAmountInCents)
"status": "sent",
"created_at": "2026-07-01 10:00:00",
"updated_at": "2026-07-01 10:00:00",
"liquid_txid": "abab...ab" // presente após a detecção on-chain da transferência
}
Status possíveis
| Status | Terminal | Significado |
|---|---|---|
| unsent | — | Criado; o DePix ainda não chegou ao provedor. |
| sending | — | DePix recebido; Pix de saída em andamento. |
| sent | sim | Sucesso: Pix entregue na chave de destino. |
| refunded | sim | Reembolsado. |
| cancelled | sim | Cancelado. |
| error | sim | Erro de processamento no provedor. |
| expired | sim | O DePix nunca chegou — varrido pelo cron. |
sk_test_, um id sandbox_* devolve sempre { "id": "sandbox_7ps_…", "type": "withdraw", "amount_cents": 10000, "status": "confirmed", "created_at": "2026-01-01 00:00:00", "updated_at": "2026-01-01 00:00:00", "sandbox": true } — estado sintético fixo, exclusivo do sandbox (status: "confirmed" fica fora do enum live); amount_cents é decodificado do id (null em ids legados sem valor). Qualquer outro id via sk_test_ → 404; ids sandbox_* via chave live ou JWT → 404.
Webhooks
Quando o status de um checkout muda, a API envia um POST para o callback_url que você informou ao criar o checkout (ou configurado no produto/merchant). Depósitos e saques criados via API key também disparam webhooks (eventos deposit.*/withdraw.*) para o default_callback_url do merchant — ver Eventos.
Como funciona
- O request é enviado com timeout de 30 segundos.
- Se falhar (resposta não-2xx, timeout ou erro de rede), a API tenta novamente até 5 vezes: após 1 minuto, 10 minutos, 1 hora, 4 horas e 12 horas (6 tentativas no total, cobrindo cerca de 17 horas).
- Sua endpoint deve responder com status 2xx para confirmar recebimento.
- O
callback_urlprecisa ser HTTPS e de acesso público (sem IPs privados).
Headers enviados
X-DePix-Signature— assinatura HMAC-SHA256 (ver seção Verificar assinatura).X-DePix-Event— nome do evento (ex:checkout.completed).X-DePix-Event-Id— identificador único e estável entre tentativas deste evento (ex:evt_abc123…). É a chave recomendada para deduplicação.X-DePix-Delivery-Attempt— número da tentativa atual (1, 2, … até 6). Muda a cada retry; não use para dedupe.User-Agent— sempreDePix-Webhook/1.0.
Entrega at-least-once e idempotência (obrigatório)
Webhooks são entregues com semântica at-least-once — é o padrão do mercado (Stripe, PayPal, Mercado Pago funcionam da mesma forma). Isso significa que o mesmo evento pode chegar ao seu endpoint mais de uma vez, mesmo que tudo esteja funcionando corretamente. Cenários comuns:
- Seu servidor processa o webhook mas responde lentamente — nossa API faz timeout em 30s, marca como falha e tenta novamente; você processa duas vezes.
- Seu servidor responde 200 mas a conexão cai antes da gente ler a resposta — mesma coisa: retry e processamento duplicado.
- O time de operações reenvia manualmente um evento (via comando administrativo) que você já processou.
Para evitar entregar produto duas vezes, creditar saldo em duplicidade, ou disparar acionamentos múltiplos, seu endpoint precisa ser idempotente. A forma mais simples e robusta é deduplicar por X-DePix-Event-Id: guarde os IDs de eventos já processados e ignore silenciosamente qualquer evento cujo ID já esteja na sua tabela.
// Exemplo de dedupe (Node.js, pseudo-código) app.post("/webhook", async (req, res) => { // 1. Valide a assinatura HMAC primeiro (ver seção Verificar assinatura). const eventId = req.headers["x-depix-event-id"]; // 2. Processe E marque como processado dentro da MESMA transação — se // processCheckout falhar, o INSERT também é revertido e o nosso retry // consegue entregar o evento novamente. try { await db.transaction(async (tx) => { await tx.query( "INSERT INTO processed_webhooks (event_id, received_at) VALUES (?, NOW())", [eventId] ); await processCheckout(req.body, tx); }); } catch (err) { if (err.code === "ER_DUP_ENTRY") { // Já processamos antes — responde 200 e ignora. return res.sendStatus(200); } throw err; // Deixa nossa API tentar novamente. } res.sendStatus(200); });
Se preferir não manter uma tabela separada, você também pode deduplicar pelo campo event_id dentro do JSON (data.event_id) — ele carrega o mesmo valor estável do header X-DePix-Event-Id. Não use (data.id, event) como chave de dedupe: um reenvio manual feito pela equipe de operações reusa o mesmo id de checkout e o mesmo nome de evento, então uma chave baseada nessa tupla descartaria silenciosamente o reenvio.
Reduzindo retries
Para evitar entregas duplicadas no caminho feliz, responda o mais rápido possível — alvos comuns ficam abaixo de poucos segundos, para deixar folga para a latência de rede antes do nosso timeout de 30s. O padrão recomendado é: validar a assinatura, responder 200 imediatamente e processar o evento em background (fila, worker, etc.). Isso evita retries causados por timeout do nosso lado.
Eventos
checkout.processing
Disparado quando o pagamento Pix é recebido e a conversão está sendo processada.
{
"event": "checkout.processing",
"data": {
"event_id": "evt_01jxxxxxxxxxxxxxxxxxxxxxx",
"id": "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
"product_id": null,
"status": "processing",
"amount": 2990,
"processing_at": "2025-06-01T15:02:00.000Z",
"metadata": { "order_id": "ORD-123" }
}
}
checkout.approved
Disparado quando o pagamento é aprovado pelo banco e aguarda a liquidação final em DePix.
{
"event": "checkout.approved",
"data": {
"event_id": "evt_01jxxxxxxxxxxxxxxxxxxxxxx",
"id": "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
"product_id": null,
"status": "approved",
"amount": 2990,
"approved_at": "2025-06-01T15:05:00.000Z",
"metadata": { "order_id": "ORD-123" }
}
}
checkout.completed
Disparado quando o pagamento é confirmado e o DePix chega na carteira do merchant.
{
"event": "checkout.completed",
"data": {
"event_id": "evt_01jxxxxxxxxxxxxxxxxxxxxxx",
"id": "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
"product_id": null,
"status": "completed",
"amount": 2990,
"completed_at": "2025-06-01T15:22:00.000Z",
"metadata": { "order_id": "ORD-123" }
}
}
checkout.cancelled
Disparado quando o pagamento do checkout é cancelado, estornado ou entra em reembolso pelo provedor Pix.
{
"event": "checkout.cancelled",
"data": {
"event_id": "evt_01jxxxxxxxxxxxxxxxxxxxxxx",
"id": "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
"product_id": null,
"status": "cancelled",
"amount": 2990,
"cancelled_at": "2025-06-01T15:05:00.000Z",
"metadata": { "order_id": "ORD-123" }
}
}
checkout.expired
Disparado quando o checkout expira sem receber pagamento.
{
"event": "checkout.expired",
"data": {
"event_id": "evt_01jxxxxxxxxxxxxxxxxxxxxxx",
"id": "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
"product_id": null,
"status": "expired",
"amount": 2990,
"expires_at": "2025-06-01T15:30:00.000Z",
"metadata": { "order_id": "ORD-123" }
}
}
checkout.unmatched_payment
Só no trilho DePix direto: chegou um pagamento no endereço do lojista que não casa com nenhuma cobrança (valor diferente do informado, pagamento depois da janela de tolerância, ou um segundo pagamento para uma cobrança já quitada). O dinheiro está na carteira do lojista — só a associação automática com o pedido não aconteceu, e a conciliação é uma decisão humana. Como não há checkout, a entrega vai para o default_callback_url do merchant.
{
"event": "checkout.unmatched_payment",
"data": {
"event_id": "evt_01jxxxxxxxxxxxxxxxxxxxxxx",
"id": "dout_9f8e7d6c5b4a",
"type": "depix_output",
"status": "unattributed",
"amount_cents": 8997,
"txid": "abab…",
"vout": 1,
"first_seen_at": "2026-07-29 12:07:00",
"reason": "no_matching_checkout"
}
}
status é unattributed (nenhuma cobrança aberta com esse valor exato) ou duplicate (segundo pagamento de uma cobrança já quitada). first_seen_at é quando o pagamento foi visto no endereço do lojista — pode ser alguns minutos antes desse aviso — e é o mesmo horário que aparece no app. reason detalha o motivo (no_matching_checkout, duplicate_payment, ambiguous_candidates, value_not_whole_cents, max_attributions_per_tx, transition_lost); concilie pelo id, não por esse texto.
checkout.* do trilho DePix direto vêm também payment_method, amount_received (o valor efetivamente pago, em centavos) e discount_pct. O campo amount continua sendo o valor de face — libere o pedido pelo amount_received.
Eventos deposit.* e withdraw.*
Depósitos e saques criados via API key (scope wallet_write) disparam um evento por transição real de status, nomeado 1:1 com o status cru: deposit.<status> / withdraw.<status>. Operações humanas (dashboard/SPA) não disparam. A entrega vai para o default_callback_url do merchant — sem ele configurado, nada é enviado. Operações sandbox (sk_test_) não criam registros e portanto nunca disparam webhooks.
Criar um depósito ou saque não emite webhook — o primeiro evento que você recebe é a próxima mudança de status. Por isso os eventos de estado inicial (deposit.pending, withdraw.unsent) só aparecem na reversão incomum de volta a esse estado.
| Evento | Disparado quando |
|---|---|
| deposit.pending | Depósito aguardando pagamento Pix (estado inicial). |
| deposit.under_review | Pix pago; pagamento em análise pré-liquidação. |
| deposit.pending_pix2fa | Aguardando o pagador completar o 2FA do Pix. |
| deposit.approved | Aprovado pelo provedor; DePix ainda não enviado. |
| deposit.delayed | Liquidação retida pela política de delay. |
| deposit.will_refund | Fluxo de reembolso iniciado. |
| deposit.depix_sent | Sucesso terminal: DePix entregue no endereço de destino. |
| deposit.refunded | Reembolsado ao pagador (terminal). |
| deposit.canceled | Cancelado pelo provedor (terminal). |
| deposit.error | Erro de processamento no provedor (terminal). |
| deposit.expired | QR expirou sem pagamento (terminal). |
| withdraw.unsent | Saque aguardando o envio do DePix (estado inicial). |
| withdraw.sending | DePix recebido; Pix de saída em andamento. |
| withdraw.sent | Sucesso terminal: Pix entregue na chave de destino. |
| withdraw.refunded | Reembolsado (terminal). |
| withdraw.cancelled | Cancelado (terminal). |
| withdraw.error | Erro de processamento no provedor (terminal). |
| withdraw.expired | O DePix nunca chegou — varrido pelo cron (terminal). |
O payload usa o mesmo shape em inglês dos GETs de status, mais o event_id — a chave de dedupe (mesmo valor do header X-DePix-Event-Id, estável entre retries). Payloads deposit.* sempre trazem rejection_reasons (array, [] quando não houve recusa) — com conteúdo em deposit.refunded, deposit.will_refund e deposit.error; vazio nos demais eventos. Payloads withdraw.* não têm esse campo:
{
"event": "deposit.depix_sent",
"data": {
"id": "qr-id-456",
"type": "deposit",
"amount_cents": 5000,
"status": "depix_sent",
"created_at": "2026-07-01 12:00:00",
"updated_at": "2026-07-01 12:34:56",
"rejection_reasons": [], // ex.: ["PAYER_MISMATCH"] em deposit.refunded
"event_id": "evt_9f8e7d6c5b4a"
}
}
{
"event": "withdraw.sent",
"data": {
"id": "wd-123",
"type": "withdraw",
"amount_cents": 10000,
"status": "sent",
"created_at": "2026-07-01 10:00:00",
"updated_at": "2026-07-01 10:00:00",
"liquid_txid": "abab...ab",
"event_id": "evt_1a2b3c4d5e6f"
}
}
Verificar assinatura
Cada webhook vem com um header X-DePix-Signature. Sempre valide a assinatura antes de processar o evento — isso garante que o request veio da API do DePix App e não de terceiros.
Formato do header
X-DePix-Signature: t=1717257600,v1=abc123def456...
- t — timestamp Unix do envio (segundos).
- v1 — assinatura HMAC-SHA256 em hexadecimal.
Como validar
A assinatura é calculada sobre a string timestamp.payload usando o Webhook Secret da sua conta (disponível na Área do Lojista).
# Calcular a assinatura esperada EXPECTED=$(echo -n "${TIMESTAMP}.${RAW_BODY}" | \ openssl dgst -sha256 -hmac "${WEBHOOK_SECRET}" | awk '{print $2}') # Comparar com o v1 recebido if [ "$EXPECTED" = "$RECEIVED_V1" ]; then echo "Assinatura válida" fi
import crypto from "node:crypto"; function verifyWebhook(rawBody, sigHeader, secret) { const parts = Object.fromEntries( sigHeader.split(",").map(p => p.split("=", 2)) ); const timestamp = parts["t"]; const received = parts["v1"]; const expected = crypto .createHmac("sha256", secret) .update(`${timestamp}.${rawBody}`) .digest("hex"); // Use timingSafeEqual to prevent timing attacks const a = Buffer.from(expected, "hex"); const b = Buffer.from(received, "hex"); if (a.length !== b.length) return false; return crypto.timingSafeEqual(a, b); } // Exemplo com Express app.post("/webhook/depix", express.raw({ type: "application/json" }), (req, res) => { const sig = req.headers["x-depix-signature"]; if (!verifyWebhook(req.body.toString(), sig, process.env.DEPIX_WEBHOOK_SECRET)) { return res.status(401).send("Assinatura inválida"); } const { event, data } = JSON.parse(req.body); // processa o evento... res.sendStatus(200); });
import hmac, hashlib def verify_webhook(raw_body: str, sig_header: str, secret: str) -> bool: parts = dict(p.split("=", 1) for p in sig_header.split(",")) timestamp = parts["t"] received = parts["v1"] expected = hmac.new( secret.encode(), f"{timestamp}.{raw_body}".encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, received)
function verifyWebhook(string $rawBody, string $sigHeader, string $secret): bool { $parts = []; foreach (explode(",", $sigHeader) as $pair) { [$k, $v] = explode("=", $pair, 2); $parts[$k] = $v; } $expected = hash_hmac("sha256", $parts["t"] . "." . $rawBody, $secret); return hash_equals($expected, $parts["v1"]); }
static bool VerifyWebhook(string rawBody, string sigHeader, string secret) { var parts = sigHeader.Split(',') .ToDictionary(p => p.Split('=', 2)[0], p => p.Split('=', 2)[1]); using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var expected = Convert.ToHexString( hmac.ComputeHash(Encoding.UTF8.GetBytes($"{parts["t"]}.{rawBody}")) ).ToLower(); return CryptographicOperations.FixedTimeEquals( Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(parts["v1"]) ); }
func verifyWebhook(rawBody, sigHeader, secret string) bool { parts := make(map[string]string) for _, p := range strings.Split(sigHeader, ",") { kv := strings.SplitN(p, "=", 2) parts[kv[0]] = kv[1] } mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(parts["t"] + "." + rawBody)) expected := hex.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(expected), []byte(parts["v1"])) }
def verify_webhook(raw_body, sig_header, secret) parts = sig_header.split(",").to_h { |p| p.split("=", 2) } expected = OpenSSL::HMAC.hexdigest("sha256", secret, "#{parts['t']}.#{raw_body}") Rack::Utils.secure_compare(expected, parts["v1"]) end
static boolean verifyWebhook(String rawBody, String sigHeader, String secret) throws Exception { Map<String, String> parts = new HashMap<>(); for (String p : sigHeader.split(",")) { String[] kv = p.split("=", 2); parts.put(kv[0], kv[1]); } Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256")); String expected = HexFormat.of().formatHex( mac.doFinal((parts.get("t") + "." + rawBody).getBytes()) ); return MessageDigest.isEqual(expected.getBytes(), parts.get("v1").getBytes()); }
Sandbox
Use chaves do tipo sk_test_... para testar sem movimentar dinheiro real. Checkouts criados com chave test nunca geram Pix real e ficam isolados dos checkouts de produção.
Diferenças do modo test
- O campo
is_liveretornafalse. - O QR code gerado não é um Pix válido — não pode ser pago num app de banco.
- Use o endpoint
/simulate-paymentpara marcar o checkout como pago. - Webhooks são enviados normalmente — ótimo para testar sua integração de ponta a ponta.
Depósito e saque em modo test
POST /api/depositePOST /api/withdrawcomsk_test_respondem com payloads sintéticos marcados"sandbox": true: stringsSANDBOX-…-DO-NOT-PAYimpagáveis, idssandbox_*, cotação sintética fixa de 2% no saque.- Zero dinheiro e zero registro: nenhuma chamada ao provedor Pix, nenhuma linha gravada — os contadores econômicos da conta não são afetados.
- Validações e limites reais são exercitados: os gates da conta e o limite por transação da chave rodam normalmente. O limite diário não acumula (nada é gravado).
GET /api/deposits/:ideGET /api/withdrawals/:idcom idsandbox_*devolvem um status sintético fixo (depix_sent/confirmed) para treinar o loop de polling.- Sem registros → operações sandbox nunca disparam webhooks
deposit.*/withdraw.*. Para testar webhooks de ponta a ponta, use checkout +simulate-payment. - Chave
sk_test_jamais lê ou escreve dados live: qualquer id que não sejasandbox_*→404.
Simular pagamento
Marca um checkout de teste como pago. Só funciona com chaves sk_test_. Dispara o webhook checkout.completed normalmente.
# 1. Crie um checkout de teste curl -X POST https://api.depixapp.com/api/checkouts \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": 1000, "payer_tax_number": "529.982.247-25", "callback_url": "https://minha-loja.com/webhook" }' # 2. Simule o pagamento curl -X POST https://api.depixapp.com/api/checkouts/chk_01jxxxxxxxxxxxxxxxxxxxxxx/simulate-payment \ -H "Authorization: Bearer $DEPIX_API_KEY"
{ "success": true }
Após a simulação, seu callback_url receberá o evento checkout.completed em alguns segundos — exatamente como num pagamento real.
Verificar chave (GET /api/me)
Retorna as informações do merchant autenticado. Útil para verificar se a API key é válida e consultar dados da conta.
curl https://api.depixapp.com/api/me \ -H "Authorization: Bearer $DEPIX_API_KEY"
{
"merchant_id": "mrc_xxx",
"name": "Loja do Joao",
"username": "joao",
"merchant_slug": "joao",
"is_live": true,
"created_at": "2025-06-01T00:00:00.000Z"
}
Editar perfil da loja (PATCH /api/merchants/me)
Atualiza parcialmente o perfil da loja autenticada. Envie apenas os campos que deseja alterar. Aceita tanto o JWT do dashboard quanto uma API key com scope merchant_write.
liquid_address (redireciona dinheiro), cnpj e a senha da conta não são editáveis por chave — enviá-los numa requisição autenticada por API key retorna 400 com error.code = "validation_error" e details.field nomeando o campo recusado. O split_address nunca é editável por este endpoint (só admin). Esses campos sensíveis só mudam pelo painel web, pelo dono da conta (endereço Liquid exige senha).
Parâmetros (todos opcionais — envie só o que muda)
| Campo | Tipo | Descrição |
|---|---|---|
| business_name | string | Novo nome do negócio (2–100 caracteres). Alterar rotaciona o merchant_slug público e aposenta o antigo — qualquer link de pagamento ou URL de checkout construído sobre o slug antigo passa a retornar 404. Renomeie ciente disso. |
| website | string | Novo site da loja (normalizado para https://). Enviar null ou vazio limpa o campo. |
| logo_url | string | Nova URL HTTPS do logo. null ou vazio limpa o campo. |
| default_callback_url | string | Novo endpoint HTTPS padrão de webhook para eventos deposit.* / withdraw.*. null ou vazio limpa o campo. |
| default_redirect_url | string | Nova URL HTTPS padrão de redirecionamento pós-pagamento dos clientes da loja. null ou vazio limpa o campo. |
GET /api/me.
curl -X PATCH https://api.depixapp.com/api/merchants/me \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "default_redirect_url": "https://loja.exemplo.com/obrigado" }'
{
"success": true,
"merchant_slug": "loja-do-joao" // muda apenas se business_name mudou
}
{
"response": { "errorMessage": "Este campo só pode ser alterado pelo dono da conta no painel web." },
"error": {
"code": "validation_error",
"message": "This field can only be changed by the account owner in the web dashboard.",
"request_id": "gru1::abcd-1234",
"docs_url": "https://depixapp.com/docs/en/#errors",
"details": { "field": "liquid_address" }
}
}
Audit log da chave
Toda operação de escrita feita com uma API key é auditada: ação, valor, recurso, request_id, IP, flag de sandbox e replays idempotentes — inclusive negações autenticadas (ex.: insufficient_scope, account_blocked, com a ação sufixada *.denied:<code>). O dono consulta o histórico de cada chave com o endpoint abaixo. Retenção: 90 dias. GETs e respostas 429 não são logados.
Query params (todos opcionais)
| Parâmetro | Descrição |
|---|---|
| limit | Resultados por página. Padrão: 50. Mínimo: 1. Máximo: 100. |
| offset | Paginação. Padrão: 0. |
curl "https://api.depixapp.com/api/api-keys/a1b2c3d4e5f6/audit?limit=50&offset=0" \ -H "Authorization: Bearer <jwt-do-dashboard>"
{
"audit": [
{
"id": "aud_01jxxxxxxxxxxxxxxxxxxxxxx",
"action": "withdraw.create",
"method": "POST",
"path": "/api/withdraw",
"status_code": 200,
"amount_cents": 10000,
"resource_id": "wd-123",
"is_sandbox": 0,
"is_replay": 0, // 1 = replay idempotente (handler não rodou)
"ip": "203.0.113.9",
"request_id": "gru1::iad1::v9x4k-1751476800000-abc123",
"created_at": "2026-07-02 14:03:11"
}
],
"total": 123
}
Rate limits
A API aplica limites de requisições para garantir estabilidade e proteger contra abusos.
| Endpoint | Limite | Escopo |
|---|---|---|
| POST /api/checkouts | 30 / min | por IP |
| POST /api/checkouts/:id/simulate-payment | 60 / min por IP · 30 / min por chave | autenticado (scope merchant_write) |
| POST /api/products | 60 / min por IP · 30 / min por chave | autenticado (scope merchant_write) |
| POST /api/products/featured | 60 / min por IP · 30 / min por chave | autenticado (scope merchant_write) |
| PATCH /api/products/:id | 60 / min por IP · 30 / min por chave | autenticado (scope merchant_write) |
| POST /api/products/:id/deactivate | 60 / min por IP · 30 / min por chave | autenticado (scope merchant_write) |
| POST /api/products/:id/activate | 60 / min por IP · 30 / min por chave | autenticado (scope merchant_write) |
| POST /api/deposit | 20 / min por IP · 2 / min por chave | autenticado (scope wallet_write) |
| POST /api/withdraw | 20 / min por IP · 2 / min por chave | autenticado (scope wallet_write) |
| GET /api/deposits/:id | 60 / min por IP · 30 / min por chave | autenticado (scope wallet_read) |
| GET /api/withdrawals/:id | 60 / min por IP · 30 / min por chave | autenticado (scope wallet_read) |
| GET /api/api-keys/:id/audit | 60 / min por IP · 30 / min por usuário | autenticado (JWT) |
| PATCH /api/merchants/me | 30 / min por IP · 10 / min por chave | autenticado (scope merchant_write) |
| GET /api/checkout-page/:id | 30 / min | por IP (público) |
| GET /api/pay/:id | 60 / min | por IP (público) |
| POST /api/pay/:id/simulate | 5 / min | por IP (público, só sandbox) |
| POST /api/merchants/:username/checkout | 10 / min por IP · 60 / min por lojista | público |
| POST /api/products/:id/checkout | 10 / min por IP · 60 / min por lojista | público |
| GET /api/products/:id/public | 30 / min | por IP (público) |
| GET /api/merchants/:username/public | 30 / min | por IP (público) |
| Por merchant (API key) | 30 / min (padrão) — configurável | compartilhado entre todos os endpoints da chave; elevável via suporte |
| Por chave (rate_limit_per_min) | Configurável na criação da chave | check adicional por API key (1–600 req/min) |
- Para requests autenticados com API key, um rate limit adicional é aplicado por merchant (padrão 30 req/min, compartilhado entre todos os endpoints — fale com o suporte se precisar de aumento) e, quando definido na criação, por chave (
rate_limit_per_min). - As escritas autenticadas alcançáveis por API key — criar/simular checkout e o CRUD de produtos — têm limite por-endpoint (60/min por IP · 30/min por chave), listado na tabela acima. As leituras do lado "receber" (listar checkouts e produtos,
GET /api/me, etc.) não têm limite por-endpoint próprio — são cobertas pelo orçamento por merchant (padrão 30 req/min, configurável). O caminho JWT/SPA também é limitado, não só o de API key. - Nas rotas de deposit/withdraw, o contador por usuário vale por chave — cada API key tem budget próprio, sem competir com o dono na SPA.
- Para endpoints públicos (sem auth), o rate limit é aplicado apenas por IP.
- Quando o limite é atingido, a API retorna
429comerror.code = "rate_limited"(ou"merchant_rate_limited"), o campoerror.retry_aftere o headerRetry-After— aguarde os segundos indicados antes de tentar de novo. - Em rotas
wallet_*autenticadas por API key, falha de infraestrutura no check de rate limit responde503 service_unavailablecomretry_after(fail-closed) em vez de deixar o request passar. - Velocidade por pagador: no máximo 2 QRs por CPF/CNPJ do pagador em uma janela deslizante de 30 minutos (depósitos e checkouts contam juntos). A partir do 3º, a API retorna
429comerror.code = "payer_velocity_limit",details: { window_minutes, max_per_window }e o headerRetry-Afterindicando os segundos até liberar.
Abrir um ticket
Abre um ticket de suporte. Os endpoints /api/tickets funcionam tanto para usuários humanos (o JWT do dashboard) quanto para agentes de IA (uma chave sk_live_/sk_test_) — nenhum escopo é exigido. Uma requisição assinada com sk_ se comporta exatamente como a com JWT. Cada sessão ou chave só enxerga os tickets que ela mesma criou; os tickets de outro principal são invisíveis.
GET /api/tickets/{id} a cada poucos minutos, não segundos. Para um agente, esse polling é como ele lê a resposta.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| subject | string | obrigatório | Resumo curto. 4–120 caracteres. |
| body | string | obrigatório | A mensagem. 1–4000 caracteres. |
| category | string | opcional | Um de bug, question, account, payment, other. Padrão other. |
Campos do ticket
| Campo | Valores | Descrição |
|---|---|---|
status | awaiting_reply · answered · closed | awaiting_reply = é a vez do suporte responder (nunca fecha sozinho). answered = o suporte respondeu e aguarda você (fecha sozinho após 2 dias úteis sem resposta do usuário). closed = terminal, embora um ticket fechado automaticamente reabra se você responder em até 7 dias. |
opener_type | human · agent | Quem abriu o ticket. |
closed_reason | null · user · admin · auto | Por que fechou. null enquanto aberto. |
category | bug · question · account · payment · other | Definido na criação. |
curl -X POST https://api.depixapp.com/api/tickets \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "subject": "Saque preso como pendente", "category": "payment", "body": "O saque wtd_123 está pendente há 2 horas." }'
{
"ticket": {
"id": "tkt_ab12cd34ef",
"opener_type": "human",
"status": "awaiting_reply",
"subject": "Saque preso como pendente",
"category": "payment",
"created_at": "2026-07-22 12:00:00",
"last_activity_at": "2026-07-22 12:00:00",
"closed_reason": null,
"closed_at": null
}
}
validation_error 400 campo inválido (details.field; irmão legado response.errors[]) ticket_open_cap 429 tickets abertos demais (details.max_open) unauthorized 401 credencial ausente / inválida
O limite é de 5 tickets abertos simultaneamente (1 para contas suspensas); error.details.max_open traz o teto. Erros de validação também expõem o irmão legado response.errors[] ao lado de error.details.field.
Listar seus tickets
Lista os tickets criados pelo principal que faz a chamada (esta sessão JWT ou esta API key), da atividade mais recente para a mais antiga. Paginado.
Parâmetros de query
| Campo | Tipo | Descrição | |
|---|---|---|---|
| limit | integer | opcional | Tamanho da página. Padrão 50. |
| offset | integer | opcional | Linhas a pular. Padrão 0. |
curl "https://api.depixapp.com/api/tickets?limit=50&offset=0" \ -H "Authorization: Bearer $DEPIX_API_KEY"
{
"tickets": [
{
"id": "tkt_ab12cd34ef",
"opener_type": "human",
"status": "awaiting_reply",
"subject": "Saque preso como pendente",
"category": "payment",
"created_at": "2026-07-22 12:00:00",
"last_activity_at": "2026-07-22 12:00:00",
"closed_reason": null,
"closed_at": null
}
],
"total": 1,
"limit": 50,
"offset": 0
}
Detalhe do ticket & mensagens
Retorna um ticket com toda a thread de mensagens, da mais antiga para a mais recente. Fazer polling deste endpoint é como você (ou um agente) lê uma resposta do suporte.
curl https://api.depixapp.com/api/tickets/tkt_ab12cd34ef \ -H "Authorization: Bearer $DEPIX_API_KEY"
{
"ticket": {
"id": "tkt_ab12cd34ef",
"opener_type": "human",
"status": "answered",
"subject": "Saque preso como pendente",
"category": "payment",
"created_at": "2026-07-22 12:00:00",
"last_activity_at": "2026-07-22 13:15:00",
"closed_reason": null,
"closed_at": null
},
"messages": [
{ "id": "tmsg_1", "sender": "user", "body": "O saque wtd_123 está pendente há 2 horas.", "created_at": "2026-07-22 12:00:00" },
{ "id": "tmsg_2", "sender": "admin", "body": "Acabou de liquidar — pode confirmar?", "created_at": "2026-07-22 13:15:00" }
]
}
O sender de uma mensagem é um de user, admin ou system.
not_found 404 ticket inexistente — ou pertence a outro principal
Um ticket que não é seu retorna o mesmo 404 not_found de um inexistente — a posse nunca é revelada.
Responder
Anexa uma mensagem do usuário à thread. Responder um ticket answered o traz de volta para awaiting_reply. Responder um ticket fechado automaticamente em até 7 dias o reabre. Tickets fechados por um usuário ou por um admin são terminais — uma resposta ali falha com 409 ticket_closed.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| body | string | obrigatório | A resposta. 1–4000 caracteres. |
curl -X POST https://api.depixapp.com/api/tickets/tkt_ab12cd34ef/messages \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "body": "Confirmado, os fundos chegaram. Obrigado!" }'
{
"message": { "id": "tmsg_3", "sender": "user", "body": "Confirmado, os fundos chegaram. Obrigado!", "created_at": "2026-07-22 13:20:00" },
"ticket": {
"id": "tkt_ab12cd34ef",
"opener_type": "human",
"status": "awaiting_reply",
"subject": "Saque preso como pendente",
"category": "payment",
"created_at": "2026-07-22 12:00:00",
"last_activity_at": "2026-07-22 13:20:00",
"closed_reason": null,
"closed_at": null
}
}
validation_error 400 body inválido (details.field) not_found 404 ticket inexistente (ou não é seu) ticket_closed 409 ticket fechado por um usuário ou admin (terminal)
Anexar arquivo
Envia um arquivo (imagem, PDF, log ou JSON) para a equipe de suporte — ideal para um print de tela ou um arquivo de diagnóstico ao relatar um bug. Os bytes vão em file_b64 (base64, sem o prefixo data:), até ~3 MB. O arquivo é encaminhado ao suporte, não é armazenado nem servido de volta — a resposta registra apenas o nome e o tipo. Anexar conta como uma resposta: um ticket answered volta para awaiting_reply e um ticket fechado automaticamente em até 7 dias reabre.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| filename | string | obrigatório | Nome do arquivo mostrado ao suporte. 1–200 caracteres. |
| content_type | string | obrigatório | Um de: image/png, image/jpeg, image/webp, application/pdf, text/plain, application/json. |
| file_b64 | string | obrigatório | Os bytes do arquivo em base64 (sem o prefixo data:). Máx. ~3 MB decodificado. |
| caption | string | opcional | Nota curta exibida junto ao arquivo. Máx. 400 caracteres. |
curl -X POST https://api.depixapp.com/api/tickets/tkt_ab12cd34ef/attachments \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "filename": "erro-checkout.png", "content_type": "image/png", "file_b64": "iVBORw0KGgo…" }'
{
"message": {
"id": "tmsg_5",
"sender": "user",
"body": "erro-checkout.png",
"created_at": "2026-07-22 14:05:00",
"attachment": { "name": "erro-checkout.png", "mime": "image/png" }
},
"ticket": {
"id": "tkt_ab12cd34ef",
"opener_type": "human",
"status": "awaiting_reply",
"subject": "Saque preso como pendente",
"category": "payment",
"created_at": "2026-07-22 12:00:00",
"last_activity_at": "2026-07-22 14:05:00",
"closed_reason": null,
"closed_at": null
}
}
validation_error 400 content_type/file_b64 inválido (details.field) attachment_too_large 413 arquivo acima de ~3 MB not_found 404 ticket inexistente (ou não é seu) ticket_closed 409 ticket fechado por um usuário ou admin (terminal) attachment_unavailable 503 sem canal de suporte agora — tente de novo ou use texto
Fechar um ticket
Fecha o ticket você mesmo, definindo closed_reason como user. Um ticket fechado pelo usuário é terminal — para continuar a conversa você abre um novo ticket.
curl -X POST https://api.depixapp.com/api/tickets/tkt_ab12cd34ef/close \ -H "Authorization: Bearer $DEPIX_API_KEY"
{
"ticket": {
"id": "tkt_ab12cd34ef",
"opener_type": "human",
"status": "closed",
"subject": "Saque preso como pendente",
"category": "payment",
"created_at": "2026-07-22 12:00:00",
"last_activity_at": "2026-07-22 13:25:00",
"closed_reason": "user",
"closed_at": "2026-07-22 13:25:00",
"rating": null,
"rated_at": null
}
}
not_found 404 ticket inexistente (ou não é seu) ticket_closed 409 já fechado
Avaliar o atendimento
Dá uma nota de 0 a 10 ao atendimento de um ticket já encerrado (0 = pior, 10 = melhor). A avaliação é única: a segunda chamada devolve 409 already_rated, então a primeira nota nunca é sobrescrita em silêncio.
Avaliar apenas registra o dado — não reabre o ticket, não conta como mensagem e não altera last_activity_at.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
rating | integer | sim | Nota inteira de 0 a 10. Valores fora da faixa, fracionários ou não numéricos retornam 400 validation_error. |
curl -X POST https://api.depixapp.com/api/tickets/tkt_ab12cd34ef/rating \ -H "Authorization: Bearer $DEPIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{"rating": 10}'
{
"ticket": {
"id": "tkt_ab12cd34ef",
"opener_type": "human",
"status": "closed",
"subject": "Saque preso como pendente",
"category": "payment",
"created_at": "2026-07-22 12:00:00",
"last_activity_at": "2026-07-22 13:25:00",
"closed_reason": "admin",
"closed_at": "2026-07-22 13:25:00",
"rating": 10,
"rated_at": "2026-07-22 13:31:00"
}
}
validation_error 400 nota ausente, fora de 0–10 ou não inteira not_found 404 ticket inexistente (ou não é seu) ticket_open 409 o ticket ainda está aberto already_rated 409 este ticket já foi avaliado
Contas de agente
Uma conta de agente é um merchant que um agente de IA cria e opera sozinho — sem e-mail, WhatsApp ou captcha. É provisionada e operada inteiramente pelos endpoints /api/agents/* abaixo. A maioria dos agentes os acessa pelo SDK DePix (a classe DepixAgent), que assina cada requisição por você.
Autenticação por requisição assinada
Os endpoints de agente não usam uma API key estática. Cada requisição é assinada com a chave Ed25519 da conta (gerada e guardada localmente pelo agente). Envie estes headers em cada chamada:
| Header | Valor |
|---|---|
x-agent-public-key | Chave pública Ed25519 crua, 64 hex |
x-agent-signature | Assinatura Ed25519 da string canônica, 128 hex |
x-agent-nonce | Única por requisição — uso único, válida ~11 min |
x-agent-timestamp | Segundos Unix, dentro de ±300s do horário do servidor |
depix-agent-auth:v1 api.depixapp.com <METHOD> <path-without-query> <timestamp> <nonce> <sha256hex(raw-request-body)>
DepixAgent.create() gera o par de chaves e cada chamada agent.* é assinada automaticamente. A referência abaixo é para quem constrói o próprio cliente.
Onboarding e graduação
Um agente novo começa com uma chave sandbox sk_test_ e uma chave sk_live_ starter (só wallet), limitada a R$100/tx e R$500/dia. A conta gradua — e passa a emitir chaves sk_live_ completas — quando fica verificada. Para um agente, verificar a conta é provar um domínio por DNS: é o mesmo domínio que libera receber de terceiros (checkouts, escopos merchant_*), então uma coisa destrava a outra.
O atraso de 24h (inter_deposit_delay_hours) aplicado aos depósitos 2–5 segura a liquidação (a entrega do DePix) daqueles depósitos — ele não bloqueia criar o próximo depósito, que pode ser criado em seguida.
A posição do depósito é contada por conta, não por canal: um checkout Pix conta igual a um QR pessoal. E o prazo é exato — nenhuma outra regra estende as 24h desses depósitos. O 1º liquida na hora; do 6º em diante valem o teto de recebimento e a faixa instantânea. Um checkout pago pelo trilho DePix (Liquid) fica fora de todas essas regras, por não passar pelo trilho Pix.
agent_invalid_signature 401 assinatura não confere agent_signature_expired 401 timestamp fora de ±300s agent_replay_detected 401 nonce já usado agent_unknown_key 401 chave não registrada (rotas autenticadas) account_suspended 403 conta pausada (rotas mutantes) agents_disabled 503 kill-switch de onboarding de agente ligado
Registrar um agente
Cria uma conta de agente — um merchant, um endereço Liquid de recebimento e as chaves starter. Exige um operator token (op_…) que um humano obtém uma vez conectando uma identidade (GitHub/Google) no painel — a âncora anti-abuso.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| name | string | obrigatório | Nome de exibição. 2–100 caracteres. |
| operator_token | string | obrigatório | O token op_… do operador humano. |
| operator_email | string | obrigatório | E-mail de notificação (nunca um login). |
| liquid_address | string | obrigatório | Endereço de recebimento da wallet. Imutável após o registro. |
| username | string | opcional | Handle minúsculo. Padrão: agent_<prefixo-da-pubkey>. |
| default_callback_url | string | opcional | URL HTTPS de webhook. |
| ref | string | opcional | Username de indicação, só para atribuição. |
curl -X POST https://api.depixapp.com/api/agents/register \ -H "x-agent-public-key: <64hex>" \ -H "x-agent-signature: <128hex>" \ -H "x-agent-nonce: <unique>" -H "x-agent-timestamp: <unix>" \ -H "Content-Type: application/json" \ -d '{ "name": "My Agent", "operator_token": "op_...", "operator_email": "me@example.com", "liquid_address": "lq1..." }'
{
"response": {
"agent": { "username": "agent_9f3c2a1b0d", "public_key": "<64hex>", "account_type": "agent" },
"merchant": { "id": "mrc_...", "merchant_slug": "agent_9f3c2a1b0d", "liquid_address": "lq1...", "webhook_secret": "whsec_..." },
"keys": {
"test": { "id": "...", "key": "sk_test_...", "scopes": "merchant_read merchant_write wallet_read wallet_write" },
"live_starter": { "id": "...", "key": "sk_live_...", "scopes": "wallet_read wallet_write", "per_tx_limit_cents": 10000, "daily_limit_cents": 50000, "starter": true }
},
"graduation": {
"requires": "domain_proof", // o que falta: provar um domínio
"verify_domain_endpoint": "POST /api/agents/verify-domain", // onde provar
"allowed_tlds_endpoint": "GET /api/agents/domain-tlds" // TLDs aceitos — consulte antes
},
"pacing": {
"first_deposit_max_cents": 2000,
"unverified_per_tx_max_cents": 10000,
"inter_deposit_delay_hours": 24, // atrasa a liquidação dos depósitos 2–5 — não bloqueia criar o próximo
"payer_velocity": { "max_per_window": 2, "window_minutes": 30 },
"verified_per_tx_deposit_max_cents": 600000,
"verified_per_tx_withdraw_max_cents": 600000
}
}
}
As chaves em texto plano e o webhook_secret são devolvidos uma única vez.
validation_error 400 campo inválido (details.field) invalid_operator_token 401 operator token inválido operator_token_revoked 403 agent_pubkey_exists 409 essa chave já tem uma conta username_taken 409 agents_disabled 503
Criar uma chave
Emite uma nova API key para a conta do agente. Chaves live exigem graduação; escopos merchant_* exigem um domínio verificado.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| live | boolean | opcional | Padrão false. true emite sk_live_ (exige graduação). |
| scopes | string[] | opcional | Subconjunto de merchant_read, merchant_write, wallet_read, wallet_write. Padrão ["merchant_read","merchant_write"]. |
| label | string | opcional | Até 100 caracteres. |
| per_tx_limit_cents | integer | opcional | Mín 100. Obrigatório quando a chave tem wallet_write (padrão 10000). |
| daily_limit_cents | integer | opcional | Mín 100. Padrão 50000 com wallet_write. |
{
"response": {
"id": "...", "key": "sk_live_...", "prefix": "sk_live_",
"is_live": true, "scopes": "wallet_read wallet_write",
"per_tx_limit_cents": 10000, "daily_limit_cents": 50000
}
}
O key em texto plano é devolvido uma vez. Máximo de 5 chaves ativas por tipo (live / test).
validation_error 400 scopes / label / limites inválidos, ou limite de 5 chaves graduation_pending 403 live:true antes da graduação domain_required 403 escopo merchant_* sem domínio verificado
Revogar uma chave
Revoga uma das chaves da conta. Idempotente — revogar uma chave já revogada ainda retorna sucesso.
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| id | string | obrigatório | O id da chave a revogar. Deve pertencer a este agente. |
{ "response": { "id": "...", "revoked": true } }
not_found (404) quando a chave não pertence ao merchant deste agente.
Status da conta
Lê o estado da conta e o progresso de graduação. Continua disponível mesmo com a conta suspensa, para o agente conseguir ler o motivo.
Assimetria importante: a suspensão da sua conta mantém esta leitura disponível (200, com o campo reason). Já o kill-switch global do programa de agentes responde 503 agents_disabled até para esta leitura — trate 503 agents_disabled como pausa da plataforma inteira, não como suspensão da sua conta.
{
"response": {
"account_status": "active", // active | suspended
"graduated": false,
"graduation": { "blocked_on": "domain_proof" }, // "domain_proof": prove um domínio (é com você)
// "gate_review": verificado, esperando nossa análise
// null: já graduou
"keys": [
{ "id": "...", "prefix": "sk_test_", "is_live": false, "starter": false, "scopes": "...", "revoked_at": null }
]
// "reason": "..." — presente só quando suspensa
}
}
Logs de entrega de webhook
Auditoria somente-leitura das entregas dos eventos checkout.*, deposit.* e withdraw.* às callback URLs desta conta — o que foi entregue, retentado ou falhou. A listagem retorna as 50 tentativas mais recentes (da mais nova para a mais antiga) sem os corpos; busque um log pelo id para ver os payloads de requisição/resposta. Um log de outra conta responde 404.
curl https://api.depixapp.com/api/agents/webhook-logs \ -H "x-agent-public-key: <64hex>" \ -H "x-agent-signature: <128hex>" \ -H "x-agent-nonce: <unique>" -H "x-agent-timestamp: <unix>"
{
"logs": [
{ "id": "...", "checkout_id": null, "event": "deposit.depix_sent", "url": "https://...", "status_code": 200, "error": null, "attempt": 1, "sent_at": "..." }
]
}
{
"log": {
"id": "...", "event": "checkout.completed", "url": "https://...", "status_code": 200,
"request_body": "{...}", // o payload assinado enviado (X-DePix-Signature cobre estes bytes)
"response_body": "{...}", // o que o receptor respondeu
"error": null, "attempt": 1, "next_retry_at": null, "sent_at": "..."
}
}
merchant_required 403 sem perfil de lojista ATIVO (ex.: conta suspensa) not_found 404 log não existe ou pertence a outra conta
Verificar um domínio
Prova por DNS TXT em duas fases. Libera o recebimento de terceiros (checkouts / escopos merchant_*). O domínio é normalizado para sua raiz registrável (ex.: shop.acme.com.br → acme.com.br).
Parâmetros
| Campo | Tipo | Descrição | |
|---|---|---|---|
| domain | string | obrigatório | O domínio a verificar. O TLD precisa estar na allowlist. |
| confirm | boolean | opcional | Omita na fase 1 (pegar o token). Envie true na fase 2 (checar o registro TXT). |
confirm) — 200 OK{
"record_name": "_depix-verify.acme.com.br",
"record_value": "depix-verify=<token 32-hex>"
}
confirm: true, após adicionar o registro TXT) — 200 OK{ "verified_domain": "acme.com.br", "verified": true } // verified: a conta ficou verificada — a graduação vem em seguida
validation_error 400 details.field: "domain" domain_tld_not_allowed 422 details.allowed_tlds: [...] domain_free_host 422 vercel.app / netlify.app / github.io / ... negados domain_txt_not_found 422 TXT ausente/divergente — tente após propagar o DNS
Allowlist de TLDs
Público. Retorna os sufixos de TLD aceitos pela verificação de domínio.
curl https://api.depixapp.com/api/agents/domain-tlds
{ "allowed_tlds": [".com", ".net", ".org", ".io", ".ai", ".dev", ".app", ".com.br", ".br", ".store", ".shop", "..."] }
Gateway MCP
O gateway MCP da DePix é um servidor Model Context Protocol hospedado que deixa qualquer cliente MCP — Claude, Cursor, ChatGPT — receber Pix sem custódia: criar checkouts e produtos, e ler status de pagamento. É um cliente fino e stateless na frente desta mesma API REST; não guarda chaves e nunca move dinheiro.
O transporte é MCP Streamable HTTP (stateless). O pacote @depixapp/mcp também roda localmente via stdio (npx -y @depixapp/mcp).
depix-wallet-mcp, stdio) expondo a carteira do agente como ferramentas. São dois servidores, com alcances diferentes: este gateway não move fundos, aquele move.
Conectar um cliente
Autentique com uma API key DePix no header Authorization — sk_test_ para sandbox, sk_live_ para produção. A chave é repassada verbatim à API a cada requisição e nunca é armazenada.
claude mcp add --transport http depix https://mcp.depixapp.com/mcp \ --header "Authorization: Bearer sk_test_YOUR_KEY"
{
"mcpServers": {
"depix": {
"url": "https://mcp.depixapp.com/mcp",
"headers": { "Authorization": "Bearer sk_test_YOUR_KEY" }
}
}
}
{
"mcpServers": {
"depix": {
"command": "npx",
"args": ["-y", "@depixapp/mcp"],
"env": { "DEPIX_API_KEY": "sk_test_YOUR_KEY" }
}
}
}
claude.ai / ChatGPT conectam por OAuth (sem header custom). Faça login pelo conector e depois vincule esse login à sua conta DePix no painel. Sessões OAuth ficam limitadas a escopos de leitura + merchant e nunca movem dinheiro — para isso use uma chave sk_. Teste qualquer conexão com a ferramenta get_account.
Escopos
Cada ferramenta precisa de um escopo na chave: merchant_read, merchant_write, wallet_read. Uma chamada sem o escopo retorna insufficient_scope dizendo o que falta.
Ferramentas
Dezesseis ferramentas. Todos os valores em centavos. O gateway nunca cria depósitos ou saques — isso é papel do SDK da wallet.
| Ferramenta | Escopo | O que faz |
|---|---|---|
create_checkout | merchant_write | Cria um checkout Pix (exige payer_tax_number). |
get_checkout | merchant_read | Busca um checkout por id. |
list_checkouts | merchant_read | Lista/filtra checkouts. |
wait_for_checkout | merchant_read | Poll no servidor até o estado final; emite progresso. |
simulate_checkout_payment | merchant_write | Só sandbox: marca um checkout como pago. |
create_product | merchant_write | Cria um link de pagamento reutilizável. |
list_products | merchant_read | Lista/filtra produtos. |
get_product | merchant_read | Busca um produto + agregados. |
update_product | merchant_write | Atualiza campos do produto. |
activate_product / deactivate_product | merchant_write | Liga/desliga um produto. |
set_featured_products | merchant_write | Fixa produtos na vitrine. |
list_product_checkouts | merchant_read | Checkouts de um produto. |
get_account | merchant_read | Verifica a chave / a conexão. |
get_deposit_status | wallet_read | Lê o status de um depósito. |
get_withdrawal_status | wallet_read | Lê o status de um saque. |
Código e referência completa: github.com/depixapp/depix-mcp (@depixapp/mcp).
SDK da wallet
@depixapp/sdk é uma wallet Liquid não-custodial para Node — a seed é gerada e cifrada localmente, cada assinatura acontece no lado do agente, e o backend nunca guarda uma chave. Traz duas classes:
| Classe | Propósito |
|---|---|
DepixWallet | Mover dinheiro: depositar, sacar, converter, enviar, saldos. |
DepixAgent | Self-onboarding: registrar uma conta e gerenciar API keys (opera os endpoints de agente). |
npm install @depixapp/sdk
Só ESM. Node ≥ 22.4, Linux / macOS. Valores on-chain são bigint em sats de 8 casas (R$1,00 = 100_000_000n DePix); valores Pix são inteiros em centavos de BRL.
Início rápido
Cria uma wallet, financia com Pix, converte para L-BTC — tudo client-side.
import { DepixWallet } from "@depixapp/sdk"; // cria + faz backup (headless) + abre o gate de recebimento const { wallet } = await DepixWallet.create({ passphrase: process.env.DEPIX_WALLET_PASSPHRASE, // ≥ 12 chars mnemonicSecured: true, }); await wallet.confirmBackup(); // depósito: o dono paga o QR (precisa de DEPIX_API_KEY) const dep = await wallet.deposit({ amountCents: 1000, payerTaxNumber: "OWNER_CPF" }); console.log("Pague isto:", dep.qrCopyPaste); await wallet.waitForDeposit(dep.id, { timeoutMs: 15 * 60_000 }); // sempre limite esperas humanas // converte R$5 de DePix para L-BTC (client-side) await wallet.convert({ from: "DEPIX", to: "LBTC", amount: 500_000_000n }); await wallet.close();
Referência da wallet
DepixWallet.create() / open() / restore() retornam uma wallet. Seus métodos de dinheiro e leitura:
| Método | O que faz |
|---|---|
deposit({ amountCents, payerTaxNumber }) | Cria um depósito Pix → { id, qrCopyPaste }. O dono paga; creditado líquido de taxas. |
withdraw({ pixKey, recipientTaxNumber, amountCents, mode }) | Saque Pix. mode: "send" | "payout". |
convert({ from, to, amount, … }) | Converte entre DEPIX / LBTC / USDT / BTC entre redes. amount é bigint em sats. |
| Pagar / receber via Lightning | Paga e recebe faturas Lightning. A liquidação passa por submarine swaps da Boltz — o DePix não é emitido na Lightning, então toda perna Lightning é um swap contra L-BTC. Compras de gift card liquidam da mesma forma. |
send({ asset, amountSats, address }) | Envio on-chain de DEPIX / USDT / LBTC. |
quote(intent) | Cotações de rota (read-only) para uma conversão. |
getBalances() | Saldos por ativo + uma estimativa em BRL. |
getReceiveAddress() | Um endereço Liquid novo de recebimento (após confirmar o backup). |
waitForDeposit(id, { timeoutMs }) | Faz poll de um depósito até liquidar. Sempre passe timeoutMs. |
custodial: true.
Onboarding do agente
DepixAgent opera os endpoints de agente — gera a identidade Ed25519 e assina cada requisição. Crie com DepixAgent.create() (ou open() para recarregar).
| Método | O que faz |
|---|---|
register({ name, operatorToken, operatorEmail, liquidAddress, … }) | Cria a conta → merchant + chaves starter. |
status() | Status da conta + progresso de graduação + chaves. |
createKey({ live, scopes, … }) | Emite uma nova chave (devolvida uma vez). |
revokeKey(id) | Revoga uma chave. |
rotateWebhookSecret() | Rotaciona o secret de assinatura de webhook. |
Erros e runtime
Todo erro estende DepixSdkError com um .code estável; refine com isDepixSdkError(err, code?). Códigos comuns:
BACKUP_REQUIRED sem receber/depositar até confirmar o backup API_KEY_REQUIRED deposit/withdraw precisam de DEPIX_API_KEY graduation_pending chave live pedida antes da graduação domain_required escopo merchant sem domínio verificado GUARDRAIL_PER_TX_LIMIT valor acima do guardrail por transação GUARDRAIL_DAILY_LIMIT valor acima do guardrail de 24h corridas MULTIPLE_ROUTES_AVAILABLE escolha um route id de quote() e repita POLL_TIMEOUT um wait*() estourou seu timeoutMs
A spec agent-facing (assinaturas exatas, tabela de rotas, receitas) fica no AGENTS.md; código completo em github.com/depixapp/depix-sdk (@depixapp/sdk).