Introdução

A API do DePix App permite criar e gerenciar checkouts Pix programaticamente. É a mesma API que alimenta o plugin do BTCPay Server e a Área do Lojista.

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.

Para começar, crie uma conta, ative sua conta de lojista na Área do Lojista e gere uma API key.

Autenticação

Use uma API key no header Authorization em todos os requests autenticados.

Header
Authorization: Bearer sk_live_<sua-chave>

Tipos de chave

PrefixoTipoComportamento
sk_live_LiveCheckouts reais. Dinheiro de verdade.
sk_test_TestCheckouts 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.

Guarde sua chave com segurança. Ela só é exibida uma vez no momento da criação. Se perdê-la, revogue e gere uma nova.

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).

ScopeLibera
merchant_readTodos os GETs da superfície de lojista: listar/consultar checkouts, produtos e GET /api/me.
merchant_writeO lado "receber": criar/simular checkouts, o CRUD de produtos e editar os campos leves do perfil da loja (PATCH /api/merchants/me).
wallet_readLer o status do lado carteira: GET /api/deposits/:id e GET /api/withdrawals/:id. Nunca é concedido por padrão.
wallet_writeO lado "pagar" (mover dinheiro): POST /api/deposit, POST /api/withdraw. Nunca é concedido por padrão.
  • Sem hierarquia implícitamerchant_write não inclui merchant_read, e os scopes wallet_* não incluem nenhum deles. Uma chave pode combinar os quatro: ["merchant_read", "merchant_write", "wallet_read", "wallet_write"].
  • Chamada sem o scope exigido → 403 com error.code = "insufficient_scope" e details.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:

CampoTipoDescrição
scopesarrayopcionalSubconjunto de ["merchant_read", "merchant_write", "wallet_read", "wallet_write"], sem duplicatas. Default: ["merchant_read", "merchant_write"] (o comportamento de sempre).
per_tx_limit_centsintegeropcionalLimite por transação em centavos (mínimo 100). Com scope wallet_write, default 10000 (R$ 100,00) quando omitido.
daily_limit_centsintegeropcionalLimite 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_minintegeropcionalRate limit adicional por chave (1–600 req/min). Omitido = sem limite próprio; vale só o budget agregado do merchant.
Resposta — 201 Created
{
  "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
}
Imutável pós-criação. Não existe endpoint de edição de scopes ou limites. Para mudar qualquer coisa numa chave viva, revogue-a e crie uma nova — permissão mutável em credencial ativa é superfície de ataque.

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/deposit
  • POST /api/withdraw
  • POST /api/checkouts

Semântica exata

CenárioResultado
Mesma chave + mesmo bodyReplay da resposta original (mesmo status, mesmo body) + header Idempotency-Replayed: true. Nenhum efeito colateral novo.
Mesma chave + body diferente422 idempotency_key_reuse — o handler nunca executa.
Mesma chave em endpoint diferenteIndependentes — o escopo de unicidade inclui o endpoint.
Request concorrente com a mesma chave409 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 5xx e 429 nunca são armazenadas — o retry re-executa. 4xx determiní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.
Effectively-once, nunca exactly-once. A garantia é de no máximo UMA execução concorrente. Se a invocação dona morrer entre a chamada ao provedor e a persistência da resposta, um takeover após 60s re-executa a operação. Desenhe seu ledger tratando o retry como potencialmente duplicador.

Exemplo

curl
curl -X POST https://api.depixapp.com/api/deposit \
  -H "Authorization: Bearer sk_live_<sua-chave>" \
  -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:

Resposta — 422 Unprocessable Entity
{
  "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.

Resposta de erro
{
  "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 header X-Request-Id. Cite-o em pedidos de suporte.
  • retry_after — segundos até poder repetir; presente em todo 429, 503 e no 409 idempotency_in_flight. Espelhado no header HTTP Retry-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ém blocked: true.
  • Handlers legados fora da superfície de agente ainda podem responder só com response.errorMessage, sem o objeto error.

Catálogo de codes

Catálogo completo e fechado. Cada linha tem âncora estável no formato #error-<code> (ex.: #error-rate_limited).

CodeHTTPQuando
unauthorized401Rota exclusiva de login (JWT) sem token válido.
invalid_api_key401Token com prefixo sk_ não encontrado, revogado ou expirado.
invalid_token401JWT inválido/expirado ou header Authorization ausente/malformado.
insufficient_scope403A API key não tem o scope exigido pela operação — details.required_scope.
account_blocked403Conta bloqueada (irmão legado blocked: true preservado).
merchant_required403A conta autenticada não tem perfil de lojista ativo.
live_access_required403Criação de chave sk_live_ sem aprovação de acesso à produção.
whatsapp_verification_required403WhatsApp do dono não verificado quando o operador exige verificação.
withdraw_disabled403Saques temporariamente desativados (kill switch global).
external_wallet_disabled403Saques para wallet externa temporariamente desativados.
sandbox_only403simulate-payment chamado em checkout live.
validation_error400Input inválido — details.field quando aplicável; o criar checkout preserva response.errors[].
tax_number_required400CPF/CNPJ obrigatório ausente (payer_tax_number / taxNumber).
amount_out_of_range400Valor fora dos limites do endpoint — details: { min_cents, max_cents } com os bounds do próprio endpoint/modo.
account_limit_exceeded400Tetos de conta não verificada (primeiro depósito, valor por transação, acumulado) — os valores dependem do nível de verificação e podem mudar; ver Limites. details: { limit, limit_cents, used_cents? }.
key_limit_exceeded400Limite de gasto da API key — details: { limit: "per_tx" | "daily", limit_cents, used_cents }.
not_found404Rota ou recurso inexistente — inclui recurso de outra conta (ownership nunca é revelado).
conflict409Conflito de estado: transição de checkout inválida, txid duplicado, slug duplicado.
idempotency_in_flight409Request com a mesma Idempotency-Key ainda em execução — retry_after: 5.
idempotency_key_reuse422Idempotency-Key reutilizada com um body diferente.
rate_limited429Rate limit por IP, por usuário ou por chave — retry_after até 60.
merchant_rate_limited429Budget agregado do merchant excedido — retry_after até 60.
payer_velocity_limit429Muitas 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_shutdown503Plataforma em manutenção (kill switch global) — retry_after: 300.
service_unavailable503Dependê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_error502Resposta malformada ou erro do provedor Pix.
internal_error500Erro 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.

POST /api/checkouts

Parâmetros

CampoTipoDescrição
amountintegerobrigatórioValor em centavos. Mínimo: 500 (R$ 5,00). Máximo: 300000 (R$ 3.000,00).
payer_tax_numberstringobrigatórioCPF ou CNPJ do pagador. Aceita CPF (11 dígitos) ou CNPJ (14 caracteres, inclusive o novo formato alfanumérico), com ou sem máscara.
descriptionstringopcionalDescrição do pedido. Máximo 500 caracteres. Exibida na página de pagamento.
expires_inintegeropcionalTempo de expiração em segundos. Padrão: 1200 (20min). Mínimo: 300 (5min). Máximo: 1200 (20min).
image_urlstringopcionalURL HTTPS da imagem do produto. Exibida na página de pagamento.
callback_urlstringopcionalURL HTTPS que recebe os webhooks do checkout.
redirect_urlstringopcionalURL para redirecionar o cliente após o pagamento.
metadataobjectopcionalDados adicionais do seu sistema (order_id, user_id, etc.). Máximo 4KB. Devolvido nos webhooks.

Exemplo

curl -X POST https://api.depixapp.com/api/checkouts \
  -H "Authorization: Bearer sk_live_<sua-chave>" \
  -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());
Resposta — 201 Created
{
  "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
  }
}
💡 Exiba o 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.

GET /api/checkouts/:id
curl
curl https://api.depixapp.com/api/checkouts/chk_01jxxxxxxxxxxxxxxxxxxxxxx \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "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
  }
}

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

StatusSignificado
pendingAguardando pagamento.
processingPix recebido, processando conversão para DePix.
approvedPagamento aprovado pelo banco, aguardando liquidação em DePix.
completedPagamento confirmado. DePix na carteira do merchant.
cancelledCancelado/estornado pelo provedor Pix.
expiredPrazo 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ódigoSignificado
PAYER_MISMATCHPagamento feito com CPF/CNPJ diferente do informado no checkout.
PAST_DAILY_LIMITLimite diário do pagador excedido.
BLOCKED_USERUsuário bloqueado pelo provedor.
HIGH_VELOCITYMuitas transações do pagador em pouco tempo.

Listar checkouts

Lista os checkouts do merchant com filtros e paginação.

GET /api/checkouts

Query params (todos opcionais)

ParâmetroDescrição
statusFiltrar por status: pending, processing, approved, completed, cancelled, expired.
product_idFiltrar por produto. Ex: prd_xxx.
fromData de início (ISO 8601). Ex: 2025-06-01T00:00:00Z.
toData de fim (ISO 8601).
qBusca por ID ou descrição.
limitNúmero de resultados por página. Padrão: 50. Máximo: 100.
offsetPaginação. Padrão: 0.
curl
curl "https://api.depixapp.com/api/checkouts?status=completed&limit=20" \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "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,
      "rejection_reasons": []     // array de motivos quando o pagamento foi devolvido/retido
    }
  ],
  "stats": {
    "total":            47,
    "pending":          2,
    "completed":        40,
    "completed_amount":  189500    // centavos — R$ 1.895,00
  },
  "limit":  20,
  "offset": 0
}

Criar produto

Cria um novo produto com valor fixo. Cada produto gera um link de pagamento permanente que pode ser compartilhado com seus clientes.

POST /api/products

Parâmetros

CampoTipoDescrição
namestringobrigatórioNome do produto exibido na UI. 2-80 caracteres.
slugstringopcionalIdentificador 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.
amountintegerobrigatórioValor em centavos. Mínimo: 500. Máximo: 300000.
descriptionstringopcionalDescrição do produto. Máximo 500 caracteres.
image_urlstringopcionalURL HTTPS da imagem do produto.
callback_urlstringopcionalURL HTTPS para webhooks. Sobrescreve o default do merchant.
redirect_urlstringopcionalURL de redirecionamento. Sobrescreve o default do merchant.
metadataobjectopcionalDados adicionais. Máximo 4KB. Incluído nos webhooks dos checkouts gerados.
expires_inintegeropcionalTempo de expiração dos checkouts em segundos. Padrão: 1200 (20min). Mínimo: 300 (5min). Máximo: 1200 (20min).

Exemplo

curl -X POST https://api.depixapp.com/api/products \
  -H "Authorization: Bearer sk_live_<sua-chave>" \
  -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());
Resposta — 201 Created
{
  "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.

GET /api/products

Query params (todos opcionais)

ParâmetroDescrição
activeFiltrar por status: 1 (ativos) ou 0 (inativos).
qBusca por nome, slug ou descrição.
limitNúmero de resultados. Padrão: 50. Máximo: 100.
offsetPaginação. Padrão: 0.
curl
curl "https://api.depixapp.com/api/products?active=1" \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "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.

GET /api/products/:id
curl
curl https://api.depixapp.com/api/products/prd_xxx \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "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.

PATCH /api/products/:id

Parâmetros (todos opcionais)

CampoTipoDescrição
namestringNovo nome do produto. 2-80 caracteres.
slugstringNovo identificador na URL. Mesmas regras da criação.
amountintegerNovo valor em centavos.
descriptionstringNova descrição.
image_urlstringNova URL de imagem.
callback_urlstringNova URL de webhook.
redirect_urlstringNova URL de redirecionamento.
metadataobjectNovos dados adicionais.
expires_inintegerNovo tempo de expiração dos checkouts. Mínimo: 300 (5min). Máximo: 1200 (20min).
curl
curl -X PATCH https://api.depixapp.com/api/products/prd_xxx \
  -H "Authorization: Bearer sk_live_<sua-chave>" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 3490, "description": "Camiseta tamanho M - Edição Especial" }'
Resposta — 200 OK
{
  "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.

POST /api/products/:id/activate
POST /api/products/:id/deactivate
curl — ativar
curl -X POST https://api.depixapp.com/api/products/prd_xxx/activate \
  -H "Authorization: Bearer sk_live_<sua-chave>"
curl — desativar
curl -X POST https://api.depixapp.com/api/products/prd_xxx/deactivate \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{ "success": true }

Checkouts do produto

Lista os checkouts gerados a partir de um produto específico. Aceita os mesmos filtros da listagem geral de checkouts.

GET /api/products/:id/checkouts
curl
curl "https://api.depixapp.com/api/products/prd_xxx/checkouts?status=completed" \
  -H "Authorization: Bearer sk_live_<sua-chave>"

A resposta segue o mesmo formato da listagem de checkouts.

Produto público

Retorna os dados públicos de um produto ativo. Não requer autenticação.

GET /api/products/:id/public
curl
curl https://api.depixapp.com/api/products/prd_xxx/public
Resposta — 200 OK
{
  "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.

POST /api/products/:id/checkout

Parâmetros

CampoTipoDescrição
payer_tax_numberstringobrigatórioCPF ou CNPJ de quem paga o Pix, com ou sem máscara.
curl
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).

Página do merchant

Retorna os dados públicos do merchant. Não requer autenticação.

GET /api/merchants/:username/public
curl
curl https://api.depixapp.com/api/merchants/joao/public
Resposta — 200 OK
{
  "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.

POST /api/merchants/:username/checkout

Parâmetros

CampoTipoDescrição
amountintegerobrigatórioValor em centavos. Mínimo: 500. Máximo: 300000.
payer_tax_numberstringobrigatórioCPF ou CNPJ de quem paga o Pix, com ou sem máscara.
curl
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).

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.

Fluxos SDK-first. Depósito e saque foram desenhados para serem consumidos pelo SDK oficial (em desenvolvimento), que espelha a UX humana: 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.

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 webhooks deposit.* até o status terminal depix_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 com depositAddress (endereço Liquid do provedor).
  • 2. Envie o DePix para o depositAddress a 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/:id e/ou pelos webhooks withdraw.* 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, acumulado de contas não verificadas, 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, com details informando limit_cents/used_cents vigentes.
  • 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 em GET /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.

POST /api/deposit

Parâmetros

CampoTipoDescrição
amountInCentsintegerobrigatórioValor em centavos. Mínimo: 500 (R$ 5,00). Máximo: 300000 (R$ 3.000,00). Limites da conta e da chave podem restringir mais.
depixAddressstringobrigatórioEndereço Liquid que recebe o DePix quando o Pix liquidar.
payer_tax_numberstringobrigatórioCPF ou CNPJ de quem paga o Pix, com ou sem máscara.

Exemplo

curl -X POST https://api.depixapp.com/api/deposit \
  -H "Authorization: Bearer sk_live_<sua-chave>" \
  -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());
Resposta — 200 OK
{
  "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
  }
}
Resposta — 200 OK (sandbox, sk_test_)
{
  "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
  }
}
Rejeição do provedor chega com HTTP 400. Se o provedor Pix recusar a operação, a resposta é 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.

GET /api/deposits/:id
curl
curl https://api.depixapp.com/api/deposits/qr-id-456 \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "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

StatusTerminalSignificado
pendingQR gerado; Pix ainda não pago.
under_reviewPix pago; pagamento em análise pré-liquidação.
pending_pix2faPix pago; aguardando o pagador completar o 2FA do Pix.
approvedPix aprovado pelo provedor; DePix ainda não enviado.
delayedLiquidação retida pela política de delay (contas novas/valores altos).
will_refundFluxo de reembolso iniciado; o depósito será reembolsado.
depix_sentsimSucesso: DePix entregue no endereço Liquid de destino.
refundedsimDepósito reembolsado ao pagador.
canceledsimCancelado pelo provedor.
errorsimErro de processamento no provedor.
expiredsimQR 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ódigoSignificado
PAYER_MISMATCHPagamento feito com CPF/CNPJ diferente do informado no depósito.
PAST_DAILY_LIMITLimite diário do pagador excedido.
BLOCKED_USERUsuário bloqueado pelo provedor.
HIGH_VELOCITYMuitas transações do pagador em pouco tempo.
Sandbox: com 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.

POST /api/withdraw

Parâmetros

Envie exatamente um entre depositAmountInCents (modo "você envia") e payoutAmountInCents (modo "você recebe") — os mesmos dois modos da UI humana.

CampoTipoDescrição
pixKeystringobrigatórioChave Pix de destino (e-mail, telefone, CPF/CNPJ ou chave aleatória).
depositAmountInCentsintegerum dos doisModo "você envia": quanto DePix você entrega, em centavos. Mínimo: 500. Máximo: 600000 (R$ 6.000,00). Mutuamente exclusivo com payoutAmountInCents.
payoutAmountInCentsintegerum dos doisModo "você recebe": quanto a chave de destino recebe, em centavos. Mínimo: 500. Máximo: 588000 (ajustado pela taxa). Mutuamente exclusivo com depositAmountInCents.
taxNumberstringobrigatórioCPF ou CNPJ do titular da chave Pix de destino.

Exemplo

curl -X POST https://api.depixapp.com/api/withdraw \
  -H "Authorization: Bearer sk_live_<sua-chave>" \
  -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());
Resposta — 200 OK
{
  "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.

Atenção: Enviar os fundos do saque sem incluir, na mesma transação, o valor da taxa resultará em: erro no saque e perda de fundos.
Resposta — 200 OK (sandbox, sk_test_)
{
  "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.

GET /api/withdrawals/:id
curl
curl https://api.depixapp.com/api/withdrawals/wd-123 \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "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

StatusTerminalSignificado
unsentCriado; o DePix ainda não chegou ao provedor.
sendingDePix recebido; Pix de saída em andamento.
sentsimSucesso: Pix entregue na chave de destino.
refundedsimReembolsado.
cancelledsimCancelado.
errorsimErro de processamento no provedor.
expiredsimO DePix nunca chegou — varrido pelo cron.
Sandbox: com 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_url precisa 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 — sempre DePix-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",
    "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",
    "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",
    "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",
    "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",
    "status":      "expired",
    "amount":      2990,
    "expires_at":  "2025-06-01T15:30:00.000Z",
    "metadata":    { "order_id": "ORD-123" }
  }
}

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.

EventoDisparado quando
deposit.pendingDepósito aguardando pagamento Pix (estado inicial).
deposit.under_reviewPix pago; pagamento em análise pré-liquidação.
deposit.pending_pix2faAguardando o pagador completar o 2FA do Pix.
deposit.approvedAprovado pelo provedor; DePix ainda não enviado.
deposit.delayedLiquidação retida pela política de delay.
deposit.will_refundFluxo de reembolso iniciado.
deposit.depix_sentSucesso terminal: DePix entregue no endereço de destino.
deposit.refundedReembolsado ao pagador (terminal).
deposit.canceledCancelado pelo provedor (terminal).
deposit.errorErro de processamento no provedor (terminal).
deposit.expiredQR expirou sem pagamento (terminal).
withdraw.unsentSaque aguardando o envio do DePix (estado inicial).
withdraw.sendingDePix recebido; Pix de saída em andamento.
withdraw.sentSucesso terminal: Pix entregue na chave de destino.
withdraw.refundedReembolsado (terminal).
withdraw.cancelledCancelado (terminal).
withdraw.errorErro de processamento no provedor (terminal).
withdraw.expiredO 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).

Bash
# 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
Node.js
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);
});
Python
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)
PHP
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"]);
}
C#
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"])
    );
}
Go
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"]))
}
Ruby
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
Java
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());
}
Leia o body como raw bytes (antes do parsing JSON). Qualquer reformatação invalida a assinatura.

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_live retorna false.
  • O QR code gerado não é um Pix válido — não pode ser pago num app de banco.
  • Use o endpoint /simulate-payment para 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/deposit e POST /api/withdraw com sk_test_ respondem com payloads sintéticos marcados "sandbox": true: strings SANDBOX-…-DO-NOT-PAY impagáveis, ids sandbox_*, 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/:id e GET /api/withdrawals/:id com id sandbox_* 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 seja sandbox_*404.
O sandbox é independente de produção. Você pode criar e simular checkouts de teste sem risco.

Simular pagamento

Marca um checkout de teste como pago. Só funciona com chaves sk_test_. Dispara o webhook checkout.completed normalmente.

POST /api/checkouts/:id/simulate-payment
curl
# 1. Crie um checkout de teste
curl -X POST https://api.depixapp.com/api/checkouts \
  -H "Authorization: Bearer sk_test_<sua-chave>" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 1000, "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 sk_test_<sua-chave>"
Resposta — 200 OK
{ "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.

GET /api/me
curl
curl https://api.depixapp.com/api/me \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "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.

PATCH /api/merchants/me
Por API key, só estes 5 campos leves. 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)

CampoTipoDescrição
business_namestringNovo 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.
websitestringNovo site da loja (normalizado para https://). Enviar null ou vazio limpa o campo.
logo_urlstringNova URL HTTPS do logo. null ou vazio limpa o campo.
default_callback_urlstringNovo endpoint HTTPS padrão de webhook para eventos deposit.* / withdraw.*. null ou vazio limpa o campo.
default_redirect_urlstringNova URL HTTPS padrão de redirecionamento pós-pagamento dos clientes da loja. null ou vazio limpa o campo.
Todo PATCH por API key notifica o dono por email, nomeando os campos alterados e o id da chave (controle compensatório). Consulte o perfil atual com GET /api/me.
curl
curl -X PATCH https://api.depixapp.com/api/merchants/me \
  -H "Authorization: Bearer sk_live_<sua-chave>" \
  -H "Content-Type: application/json" \
  -d '{ "default_redirect_url": "https://loja.exemplo.com/obrigado" }'
Resposta — 200 OK
{
  "success":        true,
  "merchant_slug":  "loja-do-joao"       // muda apenas se business_name mudou
}
Resposta — 400 (campo só do dono, via API key)
{
  "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.

GET /api/api-keys/:id/audit
Somente JWT. Este endpoint aceita apenas o JWT do dashboard (login do dono) — nunca uma API key. Uma chave não enxerga o próprio audit log.

Query params (todos opcionais)

ParâmetroDescrição
limitResultados por página. Padrão: 50. Mínimo: 1. Máximo: 100.
offsetPaginação. Padrão: 0.
curl
curl "https://api.depixapp.com/api/api-keys/a1b2c3d4e5f6/audit?limit=50&offset=0" \
  -H "Authorization: Bearer <jwt-do-dashboard>"
Resposta — 200 OK
{
  "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.

EndpointLimiteEscopo
POST /api/checkouts30 / minpor IP
POST /api/checkouts/:id/simulate-payment60 / min por IP · 30 / min por chaveautenticado (scope merchant_write)
POST /api/products60 / min por IP · 30 / min por chaveautenticado (scope merchant_write)
POST /api/products/featured60 / min por IP · 30 / min por chaveautenticado (scope merchant_write)
PATCH /api/products/:id60 / min por IP · 30 / min por chaveautenticado (scope merchant_write)
POST /api/products/:id/deactivate60 / min por IP · 30 / min por chaveautenticado (scope merchant_write)
POST /api/products/:id/activate60 / min por IP · 30 / min por chaveautenticado (scope merchant_write)
POST /api/deposit20 / min por IP · 2 / min por chaveautenticado (scope wallet_write)
POST /api/withdraw20 / min por IP · 2 / min por chaveautenticado (scope wallet_write)
GET /api/deposits/:id60 / min por IP · 30 / min por chaveautenticado (scope wallet_read)
GET /api/withdrawals/:id60 / min por IP · 30 / min por chaveautenticado (scope wallet_read)
GET /api/api-keys/:id/audit60 / min por IP · 30 / min por usuárioautenticado (JWT)
PATCH /api/merchants/me30 / min por IP · 10 / min por chaveautenticado (scope merchant_write)
GET /api/checkout-page/:id30 / minpor IP (público)
GET /api/pay/:id60 / minpor IP (público)
POST /api/pay/:id/simulate5 / minpor IP (público, só sandbox)
POST /api/merchants/:username/checkout10 / min por IP · 60 / min por lojistapúblico
POST /api/products/:id/checkout10 / min por IP · 60 / min por lojistapúblico
GET /api/products/:id/public30 / minpor IP (público)
GET /api/merchants/:username/public30 / minpor IP (público)
Por merchant (API key)30 / min (padrão) — configurávelcompartilhado entre todos os endpoints da chave; elevável via suporte
Por chave (rate_limit_per_min)Configurável na criação da chavecheck 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 429 com error.code = "rate_limited" (ou "merchant_rate_limited"), o campo error.retry_after e o header Retry-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 responde 503 service_unavailable com retry_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 429 com error.code = "payer_velocity_limit", details: { window_minutes, max_per_window } e o header Retry-After indicando os segundos até liberar.
Se você precisa de limites maiores para sua integração, entre em contato pelo suporte.