# Zypher Global — API > API de pagamentos Zypher Global, PIX (BRL), SPEI (MXN) + USDT numa única integração. Cash-in, cash-out, consulta de status, splits e webhooks, autenticados por Client ID/Secret. Base URL: https://zypher.lat Docs: https://zypher.lat/docs ## Sobre Uma única integração serve tanto pra quem recebe/paga com a própria conta quanto pra quem opera como plataforma/adquirente: cash-in (dinâmico e chave/CLABE fixa), cash-out, splits, saldo, conversão de moeda, consulta de transações e webhooks. # Introdução Integre com seu `Client ID` e `Client Secret` para criar cobranças (cash in), solicitar saques (cash out) e consultar status. Opcionalmente, informe um `webhook_url` para ser notificado automaticamente. Dica: gere sua chave em Dashboard → API. As chaves já são criadas com os acessos iniciais necessários. **URL Base** ``` https://api.zypher.lat ``` ### Primeiros passos - Gere suas credenciais em Dashboard → API (Client ID e Client Secret). - Crie uma cobrança (cash in) na moeda desejada, veja Cobrança PIX (BRL). - Configure seu `webhook_url` para receber as confirmações automaticamente. - Para pagar, use o cash out por chave PIX, BR Code ou CLABE, veja Saque PIX (chave). # Autenticação Envie credenciais via cabeçalhos ou Basic Auth. **Headers** ``` X-Client-Id: Client ID X-Client-Secret: Client Secret ``` **Basic Auth** ``` Authorization: Basic base64(ClientID:ClientSecret) ``` A autenticação e a criação de cobranças (cash in) NÃO exigem IP configurado. A restrição por IP (IPv4 na allowlist da chave) aplica-se apenas ao cash out via API. # Sandbox O Sandbox deixa você validar toda a sua integração (criar cobrança, receber o webhook, checar status) SEM mover dinheiro real. Nada é confirmado de verdade, é 100% isolado. Uma requisição com a Chave de Teste NUNCA toca em adquirente, saldo ou transação real. As respostas têm o mesmo formato da produção (+ o campo `"sandbox": true`). ### Chave de Teste (pública) Use estas credenciais já disponíveis, não precisa gerar nada. São públicas de propósito (o sandbox não move dinheiro). **Headers** ``` X-Client-Id: xpagsandbox_00000000 X-Client-Secret: 202620262026202620262026 ``` ### 1) Criar uma cobrança de teste POST `/cashin` · mesma rota da produção. Troque `currency` por `BRL` · `MXN` para testar cada moeda. **Body** ``` { "currency": "BRL", "amount": 100.00, "external_id": "TESTE-001", "webhook_url": "https://seusistema.com/webhooks/zypherglobal" } ``` **Resposta** ``` { "sandbox": true, "ok": true, "status": "pending", "currency": "BRL", "amount": 100, "fee": 3, "request_number": "sbx_...", "transaction_id": "SBX-...", "copy_paste": "00020101...TEST...", "expire_minutes": 30 } ``` Guarde o `transaction_id`, é ele que você usa para simular o pagamento no próximo passo. Cada moeda devolve na resposta a instrução de pagamento correspondente (ex.: `clabe` no MXN, `payee_data` no COP). ### 2) Simular o pagamento (dispara o webhook) POST `/sandbox/simulate` · confirma (ou falha) a cobrança de teste e dispara o webhook para a sua `webhook_url`, idêntico ao de produção. **Body** ``` { "transaction_id": "SBX-...", "outcome": "paid" } ``` `outcome`: `paid` · `failed` · `expired` — teste todos os cenários do seu handler. **Webhook enviado à sua URL** ``` { "sandbox": true, "type": "cashin", "status": "confirmed", "amount": 100, "fee": 3, "transaction_id": "SBX-...", "external_id": "TESTE-001", "e2e": "E...", "provider": "Zypher Global" } ``` ### Saldo e saque de teste No sandbox o `/balance` devolve um saldo de teste alto, então você consegue testar o `/cashout` (saque) sem fundo real. O saque de teste também aceita o `/sandbox/simulate` para simular a baixa. Conversão de moeda ainda NÃO está disponível no sandbox (ela mexe em saldo real). Todo o resto, cash in, cash out, status, saldo e webhook, funciona. # Cobrança PIX (BRL) POST `/cashin` · `"currency": "BRL"` Rota única para todas as moedas, a moeda é definida pelo campo `currency` (padrão `BRL`). O campo `amount` decide o MODO: `com amount` = cobrança DINÂMICA (valor fixo por cobrança); `sem amount` = sua CHAVE PIX FIXA (valor aberto, o pagador escolhe quanto). ### Cobrança dinâmica (por valor) Envie o `amount` → devolvemos um QR PIX de VALOR FIXO para AQUELE pagamento. Cada cobrança é única e some depois de paga/expirada. É o fluxo do dia a dia (checkout, pedido único). **Body** ``` { "currency": "BRL", "amount": 123.45, "name": "Cliente Pix", "document": "12345678900", "description": "Pedido #123", "external_id": "PEDIDO-123", "webhook_url": "https://seusistema.com/webhooks/zypherglobal", "splits": [ { "username": "usuario1", "percentage": 10 }, { "username": "usuario2", "percentage": 5 } ] } ``` **Resposta** ``` { "ok": true, "qr_img": "data:image/png;base64,...", "copyPaste": "000201...", "code": "...", "request_number": "pr_...", "transaction_id": "pr_..." } ``` Consulte depois pelo `request_number` (um pagamento só). Veja Consultar Transações. ### Chave PIX fixa (valor aberto · estática) Chame a MESMA rota `/cashin` SEM o campo `amount` → devolvemos um QR de VALOR ABERTO (o pagador escolhe quanto pagar). CADA chamada gera um QR NOVO (copy-paste próprio) amarrado ao `external_id` que VOCÊ envia. Autenticação, KYC e conta são os mesmos do cash-in normal. **Body** ``` { "currency": "BRL", "external_id": "PEDIDO-123", "webhook_url": "https://seusistema.com/webhooks/zypherglobal", "splits": [ { "username": "usuario1", "percentage": 10 } ] } ``` **Resposta** ``` { "ok": true, "static": true, "open_value": true, "external_id": "PEDIDO-123", "copyPaste": "00020126580014br.gov.bcb.pix...", "qr_url": "https://quickchart.io/qr?text=...", "transaction_id": "acc000094602601" } ``` - O `external_id` é SEU (você envia, a gente NÃO gera). Fica amarrado ao QR e volta no webhook. Como cada chamada gera um QR novo, gere UM por pedido → rastreio por pedido. - Reutilizou o MESMO QR em vários pagamentos? Todos voltam com o MESMO `external_id`, reconcilie pelo `e2e` (único por pagamento). O crédito é sempre individual (trava anti-duplicidade pelo `e2e`). - O campo `origin: "static"` distingue este fluxo do QR dinâmico no webhook. Revende pros seus próprios clientes? O `external_id` é o pedido do SEU sistema, o valor cai no seu saldo e você repassa o webhook pro cliente final. Splits, webhook e consulta funcionam igual nos dois modos: Splits (repasse) · Webhooks · Consultar Transações. # Cobrança SPEI (MXN) POST `/cashin` · `"currency": "MXN"` Gera uma referência SPEI/CLABE para o pagador no México. O campo `amount` decide o MODO: `com amount` = cobrança DINÂMICA (CLABE por valor); `sem amount` = a CLABE FIXA da conta (valor aberto). A confirmação chega no seu webhook. ### Cobrança dinâmica (por valor) Envie o `amount` → devolvemos uma CLABE/referência para AQUELE pagamento. É o fluxo do dia a dia (pedido único). **Body** ``` { "currency": "MXN", "amount": 500.00, "name": "Juan Pérez", "document": "PEPJ800101HDFRRL09", "description": "Pedido #123", "external_id": "PEDIDO-123", "webhook_url": "https://seusistema.com/webhooks/zypherglobal", "splits": [ { "username": "operador_mx", "percentage": 10 } ] } ``` **Resposta** ``` { "ok": true, "reference": "REF123456", "clabe": "012345678901234567", "amount": 500.00, "fee": 5.00, "currency": "MXN", "request_number": "pr_...", "transaction_id": "pr_...", "status": "pending", "bank_name": "STP (Sistema de Transferencia y Pagos)", "beneficiary": "Zypher" } ``` Consulte depois pelo `request_number`/`transaction_id` (um pagamento só). Veja Consultar Transações. Toda resposta MXN (dinâmica e CLABE fixa) traz `bank_name` e `beneficiary`. Mostre esses dados na sua tela de pagamento junto da CLABE: o pagador ver o banco (`STP`) e o nome do beneficiário aumenta a confiança e a conversão do SPEI. ### CLABE por venda (valor aberto · estática) Chame a MESMA rota `/cashin` com `"currency": "MXN"` SEM o campo `amount`, mandando um `external_id` por venda → devolvemos uma CLABE de VALOR ABERTO só daquela venda. Mesmo `external_id` devolve a MESMA CLABE (idempotente no retry); `external_id` novo = CLABE nova. O pagador envia qualquer valor via SPEI. Autenticação, KYC e SPEI ativo são os mesmos do cash-in normal. **Body** ``` { "currency": "MXN", "external_id": "PEDIDO-123", "webhook_url": "https://seusistema.com/webhooks/zypherglobal", "splits": [ { "username": "operador_mx", "percentage": 10 } ] } ``` **Resposta** ``` { "ok": true, "static": true, "open_value": true, "currency": "MXN", "external_id": "PEDIDO-123", "clabe": "012345678901234567", "transaction_id": "spc...", "bank_name": "STP (Sistema de Transferencia y Pagos)", "beneficiary": "Zypher" } ``` - Cada venda ganha a SUA CLABE (18 dígitos). Dois leads da MESMA conta (ex.: Lucas e Maria) NUNCA colidem, cada um tem a sua CLABE e o seu `external_id`. Uma venda não cai por cima da outra. - O `external_id` é SEU (você envia, a gente NÃO gera; só ecoa no webhook). Ele é a CHAVE do reuso: mesmo `external_id` = mesma CLABE (idempotente); `external_id` novo = CLABE nova. - Cada CLABE é de valor ABERTO e pode receber vários pagamentos, cada um vira um webhook próprio com `e2e` único (chave de reconciliação por pagamento). - Quando paga, a `receiverClabe` no webhook identifica a venda → disparamos o webhook com o `external_id` certo. O campo `origin: "static"` distingue este fluxo do depósito por valor. SEM `external_id` → devolvemos uma CLABE PERMANENTE da conta (endereço de recebimento/wallet); aí a atribuição por venda não existe, reconcilie por `e2e`. Para automatizar vendas, mande SEMPRE um `external_id` por venda. Revende pros seus próprios clientes? O `external_id` é o pedido do SEU sistema, o valor cai no seu saldo MXN e você repassa o webhook pro cliente final. Como a CLABE fixa não tem `transaction_id` por pagamento, consulte a LISTA de recebimentos por `clabe`/`external_id` em Consultar Transações. Splits e webhook: Splits (repasse) · Webhooks. ### OXXO (voucher pago em dinheiro) Mesma rota `/cashin` e MESMO saldo MXN, mas com `"method": "OXXO"`: geramos um voucher (boleto) que o pagador leva a qualquer loja OXXO e paga em dinheiro. A resposta já traz a referência e o código de barras na hora, para você exibir na sua tela. Faixa aceita: 10 a 10.000 MXN, e o pagador deve pagar o valor exato. **Body** ``` { "currency": "MXN", "method": "OXXO", "amount": 250.00, "payerData": { "name": "Luis Hernandez", "email": "luis@correo.com" }, "external_id": "PEDIDO-123", "webhook_url": "https://seusistema.com/webhooks/zypherglobal", "splits": [ { "username": "operador_mx", "percentage": 10 } ] } ``` - `method` — `"OXXO"`. Obrigatório (é o que distingue do SPEI/CLABE). - `amount` — entre 10 e 10.000 MXN. Fora da faixa retorna `amount_below_min`/`amount_above_limit`. - `payerData` (`name`, `email`) — opcionais: preenchidos automaticamente se ausentes. - `generateCheckout` — opcional (padrão `true` = página hospedada). Use `false` para receber o voucher cru em `payee_data` e exibir na sua tela. - `splits` — opcional: OXXO aceita repasse (split) igual ao SPEI/PIX, aplicado no crédito quando o pagamento confirma. Veja Splits (repasse). **Resposta (`generateCheckout: false`)** ``` { "ok": true, "currency": "MXN", "amount": 250.00, "fee": 5.00, "method": "OXXO", "request_number": "oxxo_...", "transaction_id": "oxxo_...", "status": "pending", "awaiting_instruction": false, "transfer_type": "REFERENCE", "payee_data": { "transferType": "REFERENCE", "reference": "8204240000119882", "barcode": "https://static.muwe.mx/.../abc.png" } } ``` - `reference` — número da referência OXXO que o pagador informa no caixa. - `barcode` — URL de imagem (PNG) do código de barras, exiba direto na sua tela. O voucher fica `pending` até o pagador ir à loja (pode levar horas ou dias, normal para dinheiro). A confirmação chega no seu webhook quando o pagamento é feito e credita seu saldo MXN. Reconcilie por `request_number`/`external_id`. **Webhook, quando o pagador paga na loja** ``` { "type": "cashin", "origin": "dynamic", "status": "confirmed", "amount": 250.00, "fee": 5.00, "request_number": "oxxo_...", "transaction_id": "oxxo_...", "external_id": "PEDIDO-123", "e2e": "E...", "provider": "Zypher Global" } ``` - Só dispara quando o pagamento é confirmado (o voucher pode ficar `pending` por horas/dias). Case pelo `transaction_id`/`request_number`/`external_id`, o mesmo que você guardou ao gerar. - Se você mandou `splits`, o repasse já foi aplicado no crédito antes deste webhook. Detalhes em Webhooks. # Cobrança USDT POST `/cashin` · `"currency": "USDT"` O pagador envia USDT (rede TRON/TRC20) para o `deposit_address`. A resposta traz o `amount` EXATO a enviar (a base + um ajuste de centavos que identifica a cobrança). O pagador deve enviar exatamente esse `amount`, senão o pagamento não concilia com este pedido. `splits` aplicam-se sobre a entrada (USDT creditado). **Body** ``` { "currency": "USDT", "amount": 100.00, "external_id": "PEDIDO-123", "webhook_url": "https://seusistema.com/webhooks/zypherglobal", "splits": [ { "username": "usuario1", "percentage": 10 } ] } ``` **Resposta** ``` { "ok": true, "currency": "USDT", "network": "TRON", "deposit_address": "T...", "amount": 100.001234, "transaction_id": "PEDIDO-123", "status": "pending" } ``` O pagador envia exatamente o `amount` em USDT (TRC20) para o `deposit_address`. A confirmação chega pelo seu `webhook_url` quando o pagamento é confirmado on-chain (`status`=confirmed). Consulte também pelo `transaction_id`. Consulte pelo `transaction_id` em Consultar Transações. Splits e webhook: Splits (repasse) · Webhooks. # Saque PIX (chave) POST `/cashout` · `"currency": "BRL"` Requisitos: a requisição deve vir de um endereço IPv4 e o IP precisa estar configurado em `IP` da chave. Caso contrário, a resposta será IP não permitido. **PIX_TYPE aceitos** ``` EMAIL CPF CNPJ PHONE EVP ``` **Body** ``` { "currency": "BRL", "amount": 250.00, "name": "Fulano de Tal", "document": "00000000000", "pix_key": "fulano@banco.com", "pix_type": "EMAIL", "webhook_url": "https://seusistema.com/webhooks/zypherglobal" } ``` Formatação: para CPF/CNPJ envie apenas dígitos (sem pontos/traços); para PHONE use E.164 (+5511999999999) ou apenas dígitos com DDI+DDD; EMAIL deve ser um email válido; EVP pode ser chave aleatória (32 hex) ou UUID. **Resposta** ``` { "ok": true, "e2e": "WD_...", "amount": 250, "fee": 2.5, "status": "pending" } ``` # Saque BR Code POST `/cashout` · `pix_copia_cola` Mesmo endpoint do cash-out por chave (`POST /cashout`), em vez de informar `pix_key`/`pix_type`, envie o `pix_copia_cola` (BR Code / EMV) que será PAGO. Não exige PIN (a API é autenticada pela chave). Sempre em BRL. Elegibilidade: disponível apenas para contas habilitadas para BR Code (permissão `pix_qr_enabled`). Caso contrário a API retorna `Saque por BR Code não disponível para esta conta.` (`403`). Também exige KYC verificado, permissão `cashout`, IP autorizado e cash-out ativo. **Body** ``` { "currency": "BRL", "pix_copia_cola": "00020126580014br.gov.bcb.pix0136...5204000053039865802BR6304ABCD", "amount": 250.00, "description": "Pagamento fornecedor", "webhook_url": "https://seusistema.com/webhooks/zypherglobal" } ``` Campo `pix_copia_cola`, aceita os apelidos `brcode`, `emv`, `qrcode`. Valor: se o BR Code for ESTÁTICO (valor fixo embutido), esse valor é usado; se você também enviar `amount` e ele divergir do valor fixo, a requisição é RECUSADA (`422 — O valor informado difere do valor fixo do BR Code.`). Se o BR Code for DINÂMICO (sem valor embutido), envie `amount`. **Resposta** ``` { "ok": true, "e2e": "E20251124...", "transaction_id": "...", "fee": 2.5, "amount": 250, "status": "pending", "currency": "BRL" } ``` Erros: provider inelegível (`403`); KYC/permissão/IP ausentes (`403`); valor divergente do BR Code estático (`422`); saldo insuficiente; BR Code inválido/expirado. # Saque SPEI (MXN) POST `/cashout` · `"currency": "MXN"` Para `MXN` use a `clabe` (18 dígitos) no lugar de `pix_key`/`pix_type`. **Body** ``` { "currency": "MXN", "amount": 300.00, "name": "Juan Pérez", "document": "PEPJ800101HDFRRL09", "clabe": "012345678901234567", "webhook_url": "https://seusistema.com/webhooks/zypherglobal" } ``` **Resposta** ``` { "ok": true, "e2e": "WL_...", "amount": 300, "fee": 3.0, "currency": "MXN", "status": "pending" } ``` # Saque USDT POST `/cashout` · `"currency": "USDT"` Saque USDT on-chain na rede TRON (TRC20). O `amount` é em USDT, é o valor enviado ao `address`. A taxa é cobrada POR CIMA, em USDT (debita do seu saldo USDT). O envio é processado e confirmado pelo seu `webhook_url`. Cashout não aceita splits. **Body** ``` { "currency": "USDT", "amount": 100.00, "address": "T...", "network": "TRON", "external_id": "SAQUE-123", "webhook_url": "https://seusistema.com/webhooks/zypherglobal" } ``` **Resposta** ``` { "ok": true, "transaction_id": "...", "amount": 100.00, "fee": 1.00, "currency": "USDT", "network": "TRON", "status": "pending" } ``` Apenas TRC20: o `address` deve ser um endereço TRON válido (começa com "T"). Rede errada = perda irreversível. O saque fica `status`=pending até o envio on-chain ser confirmado no seu `webhook_url`. # Splits (repasse) Splits repassam automaticamente uma parte do valor líquido de um cash-in para outras contas da plataforma. Envie o campo `splits` no corpo do cash-in, funciona em todas as moedas habilitadas, e nos DOIS modos (dinâmico por valor E chave/CLABE fixa de valor aberto). **Como enviar** ``` "splits": [ { "username": "usuario1", "percentage": 10 }, { "username": "usuario2", "percentage": 5 } ] ``` ### Regras - Cada item tem `username` (usuário de destino na plataforma) e `percentage` (percentual sobre o líquido). - A soma total dos percentuais, considerando Dashboard + payload, não pode ultrapassar 90%. Se ultrapassar, TODOS os splits são ignorados e 100% do líquido fica na conta de origem. - São aplicados sobre o valor LÍQUIDO (após a taxa), no mesmo instante da confirmação do cash-in, cada destino no seu próprio saldo. - Cash-out NÃO aceita splits (é só na entrada). ### Por moeda - BRL — repasse em reais, no saldo BRL do destino (líquido após a taxa PIX). - MXN — repasse em pesos, no saldo MXN do destino (líquido após a taxa SPEI). - USDT — o repasse é sobre o USDT creditado, no saldo USDT do destino (aplicado quando a entrada on-chain confirma). ### Nos dois modos - Cobrança dinâmica (com `amount`): os splits valem só para AQUELE pagamento. - Chave PIX / CLABE fixa (sem `amount`): os splits ficam AMARRADOS à chave e são aplicados em cada pagamento que ela receber. Reenviar `splits` numa nova chamada atualiza a regra da chave. Regra de cascata e teto (90%) valem igual nos dois modos. Detalhes de cada fluxo em Cash In · Cobranças. # Consultar Saldo GET `/balance` · permissão `balance` Devolve o saldo por moeda da conta autenticada. Use antes de disparar um saque pra confirmar que há liquidez. Só retornam as moedas habilitadas no sistema. **Resposta** ``` { "ok": true, "balances": { "BRL": { "available": 1530.75, "blocked": 200.00 }, "MXN": { "available": 0.00, "blocked": 0.00 }, "USDT": { "available": 42.500000, "blocked": 0.000000 } }, "blocked_currencies": ["BRL"], "updated_at": "2026-07-24 12:00:00" } ``` ### Campos - `available` — valor GASTÁVEL. É o único número que você deve usar pra decidir um pagamento. - `blocked` — valor RETIDO (ex.: por MED em aberto). Informativo, explica por que o disponível caiu; NÃO some com o available. - `blocked_currencies` — moedas onde a retenção é monitorada. Hoje só `BRL`: em MXN/USDT o `blocked` vem 0 por estrutura, não por "nada retido". Nunca trate available + blocked como saldo total pra pagamento, o retido não é sacável. # Converter Moeda Converta o saldo entre `BRL`, `MXN`, `USDT` pela cotação atual. Fluxo em dois passos: `/conversion/simulate` devolve a cotação sem mexer no saldo; `/conversion` executa (debita a origem, credita o destino). ### 1. Simular POST `/conversion/simulate` · permissão `cashin` **Body** ``` { "amount": 1000.00, "from": "BRL", "to": "MXN" } ``` **Resposta** ``` { "ok": true, "rate": 3.42, "estimated_amount": 3402.90, "fee": 5.00, "from": "BRL", "to": "MXN" } ``` ### 2. Executar POST `/conversion` · permissão `cashin` Mesmo corpo da simulação. Exige documentos verificados na conta. O valor final usa a cotação do momento da execução (pode diferir da simulação). **Resposta** ``` { "ok": true, "converted_amount": 3402.90, "rate": 3.42, "fee": 5.00, "from": "BRL", "to": "MXN", "status": "confirmed" } ``` A taxa de conversão (`fee`) é descontada da origem antes de aplicar a cotação. Saldo insuficiente na moeda de origem retorna erro sem debitar nada. # Consultar Transações Consulte o estado REAL de qualquer transação, cash-in ou cash-out, em qualquer moeda habilitada, na cobrança dinâmica (por valor) ou na chave/CLABE fixa (valor aberto). Todas as rotas exigem a permissão `balance` e são escopadas à conta autenticada. Prefira receber as confirmações pelo `webhook_url` (webhook), é instantâneo. Use a consulta para RECONCILIAR (webhook perdido, estado indeterminado). O `/consult-transaction` é UNIVERSAL: aceita qualquer identificador (`request_number`, `transaction_id`, `e2e`, `external_id`) seja de cash-in ou de cash-out e devolve o registro certo no campo `type`. O `/consult-cashout` continua funcionando (compatível com quem já usa). ### Cash In por identificador (dinâmico) Cobrança dinâmica (com `amount`) = um pagamento só. Consulte pelo `request_number` ou `transaction_id` devolvidos na criação. **Consulta** ``` GET /consult-transaction?request_number=pr_... GET /consult-transaction?transaction_id=pr_... ``` **Resposta** ``` { "ok": true, "type": "cashin", "status": "pending|confirmed|expired", "amount": 123.45, "fee": 1.23, "request_number": "pr_...", "transaction_id": "pr_...", "e2e": "E...", "provider": "Zypher Global", "updated_at": "2026-07-24 12:00:00" } ``` Vale para qualquer moeda habilitada, a moeda vem no próprio registro. ### Cash In por chave/CLABE fixa (estático · LISTA) A chave PIX fixa (BRL) e a CLABE fixa (MXN) recebem VÁRIOS pagamentos e você não tem o `transaction_id` de cada um. Consulte por `clabe` OU por `external_id` → devolve a LISTA dos recebimentos daquela chave. **Consulta** ``` GET /consult-transaction?clabe=012345678901234567 GET /consult-transaction?external_id=PEDIDO-123 GET /consult-transaction?external_id=PEDIDO-123&limit=20 ``` **Resposta** ``` { "ok": true, "type": "cashin", "count": 2, "payments": [ { "id": "pr_...", "status": "confirmed", "amount": 100.00, "fee": 1.00, "currency": "MXN", "e2e": "E...", "transaction_id": "...", "external_id": "PEDIDO-123", "clabe": "0123...", "payer_name": "...", "created_at": "...", "updated_at": "..." } ] } ``` Para a chave PIX fixa (BRL), consulte por `external_id` (você não tem a CLABE). Para a CLABE fixa (MXN), consulte por `clabe` ou `external_id`. O `limit` limita a lista (padrão 20, máx 100). ### Cash Out (saque) Consulta o estado real de um saque, essencial pra não ficar com o cash-out indeterminado se um webhook falhar. Busque por um destes: **Parâmetros (um deles)** ``` GET /consult-cashout?e2e=E... GET /consult-cashout?transaction_id=pr_... GET /consult-cashout?external_id=SAQUE-123 ``` **Resposta** ``` { "ok": true, "type": "cashout", "status": "pending|completed|failed", "amount": 250.00, "fee": 2.50, "currency": "BRL", "e2e": "E...", "transaction_id": "pr_...", "external_id": "SAQUE-123", "provider": "Zypher Global", "beneficiary": { "name": "Fulano de Tal", "document": "00000000000", "bank": "NUBANK" }, "pix_key": "fulano@banco.com", "pix_type": "EMAIL", "created_at": "2026-07-24 12:00:00", "updated_at": "2026-07-24 12:00:05", "paid_at": "2026-07-24 12:00:05" } ``` Vale para saque em qualquer moeda habilitada, a moeda vem em `currency`. Os identificadores vêm mascarados como referências públicas (`pr_...`) e o `provider` aparece com o nome da plataforma, os IDs internos nunca são expostos. # Webhooks Enviaremos um POST para o seu `webhook_url` sempre que uma transação mudar de status. Aqui está TUDO sobre webhooks, cobrança dinâmica, chave/CLABE fixa (valor aberto), cash-out e disputas (MED), nas moedas habilitadas. ### Campos comuns - `type` — `cashin` | `cashout`. - `status` — `confirmed` · `completed` · `med` · `failed`. - `currency` — moeda da transação (`BRL` · `MXN` · `USDT`). - `transaction_id`, `e2e` — identificadores; reconcilie pelo `e2e` (único por pagamento). - `external_id` — o seu identificador, quando você o enviou na criação. - `origin` — presente e igual a `"static"` quando o pagamento caiu numa CHAVE/CLABE FIXA (valor aberto). Ausente = cobrança dinâmica (por valor). O `provider` vem sempre com o nome da plataforma. Sempre trate o resultado por `status`, não pela mensagem. ### Cash In (confirmado) ### BRL · PIX **Cash In · BRL** ``` { "type":"cashin", "status":"confirmed", "amount":123.45, "fee":1.23, "currency":"BRL", "request_number":"pr_...", "transaction_id":"pr_...", "e2e":"E...", "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" } ``` ### MXN · SPEI **Cash In · MXN** ``` { "type":"cashin", "status":"confirmed", "amount":500.00, "fee":5.00, "currency":"MXN", "request_number":"pr_...", "transaction_id":"pr_...", "e2e":"E...", "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" } ``` ### USDT **Cash In · USDT (BRL→USDT)** ``` { "type":"cashin", "status":"confirmed", "amount":19.23, "fee":0.58, "currency":"USDT", "transaction_id":"CRYPTO-...", "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" } ``` No BRL→USDT o webhook dispara assim que o PIX confirma, o `amount` é o USDT ESTIMADO (o valor real é creditado ao final da conversão). ### Chave fixa / valor aberto (estática) Pagamentos numa CHAVE FIXA (o QR PIX fixo em BRL ou a CLABE fixa em MXN) chegam com `"origin":"static"` e o seu `external_id` de volta. Como a mesma chave recebe vários pagamentos, cada evento é individual, reconcilie pelo `e2e`. **Cash In · chave/CLABE fixa** ``` { "type":"cashin", "origin":"static", "status":"confirmed", "amount":100.00, "currency":"MXN", "external_id":"PEDIDO-123", "transaction_id":"PEDIDO-123", "e2e":"E...", "provider":"Zypher Global" } ``` Mesmo formato para BRL (QR PIX fixo) e MXN (CLABE fixa), só muda o `currency`. ### Cash Out (concluído) **Cash Out · BRL** ``` { "type":"cashout", "status":"completed", "amount":250.00, "fee":2.50, "currency":"BRL", "transaction_id":"pr_...", "external_id":"SEU-ID-123", "e2e":"E...", "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" } ``` **Cash Out · MXN** ``` { "type":"cashout", "status":"completed", "amount":300.00, "fee":3.00, "currency":"MXN", "transaction_id":"pr_...", "e2e":"E...", "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" } ``` **Cash Out · USDT (USDT→BRL)** ``` { "type":"cashout", "status":"completed", "usdt":20.00, "brl":100.00, "fee":0.50, "currency":"USDT", "transaction_id":"pr_...", "e2e":"E...", "provider":"Zypher Global", "updated_at":"2025-11-24 12:00:00" } ``` ### Cash Out (falhou / cancelado) Se um saque já aceito (respondido como `pending`) NÃO for concluído, recusado pelo banco do recebedor, chave inválida, QR vencido ou sem liquidez a tempo, enviamos um POST com `"status":"failed"` e o `fail_reason`. O valor (`amount` + `fee`) é estornado ao seu saldo automaticamente; use o webhook para re-creditar ou reprocessar o seu usuário final. **Cash Out · falha** ``` { "type":"cashout", "status":"failed", "amount":250.00, "fee":2.50, "transaction_id":"pr_...", "external_id":"SEU-ID-123", "e2e":"E...", "fail_reason":"Chave PIX inválida.", "updated_at":"2025-11-24 12:00:00" } ``` O `external_id` (o mesmo que você enviou no cash-out) vem em TODOS os callbacks de saque, `completed` e `failed`, para casar a transação no seu lado. A recusa síncrona (dados inválidos no momento do POST) já volta no HTTP da própria chamada; este webhook `failed` cobre a falha assíncrona (depois do `pending`). ### MED / Disputa (BRL) MED (disputa PIX): quando uma entrada já confirmada é contestada pelo pagador, enviamos um POST com `"status":"med"` no MESMO `transaction_id` da cobrança original. Use-o para travar ou estornar o pedido do seu lado, o valor líquido volta a ser debitado/retido no seu saldo. **MED · BRL** ``` { "type": "cashin", "status": "med", "amount": 123.45, "fee": 1.23, "currency": "BRL", "request_number": "pr_...", "transaction_id": "pr_...", "e2e": "E...", "provider": "Zypher Global", "updated_at": "2025-11-24 12:00:00", "med": { "payer_name": "João da Silva", "payer_document": "***.456.789-**", "payer_institution": "ISPB 12345678", "infraction_type": "FRAUD", "infraction_status": "OPEN", "infraction_reason": "Pagador alega não reconhecer a transação", "reference_id": "PROTOCOL-123", "reported_by": "PAGADOR" } } ``` O objeto `med` traz os detalhes da infração (quando a adquirente os fornece); campos podem vir vazios/ausentes, trate como opcionais. Idempotência: reconcilie sempre pelo `e2e` (único por pagamento), a mesma chave/CLABE fixa reenvia o mesmo `external_id` em pagamentos diferentes. Se o webhook falhar, consulte o estado real em Consultar Transações. # Tratamento de erros Toda falha na API retorna um corpo JSON padronizado com uma mensagem legível e um `error_code` estável. O status HTTP também reflete o erro (não é sempre 200). Trate os erros pelo `error_code`, ele NÃO muda entre idiomas nem entre versões. **Formato do erro** ``` HTTP 422 { "ok": false, "error": "Chave PIX não encontrada.", "error_code": "pix_key_not_found" } ``` - `ok` — sempre `false` em caso de erro. - `error` — mensagem legível (localizada por idioma). Boa para exibir/logar; NÃO use para lógica. - `error_code` — slug estável para você tratar por código (switch/if). É a fonte da verdade. ### Status HTTP - 400 — requisição inválida (dados/campos). - 401 — credenciais ausentes ou inválidas. - 403 — sem permissão, IP não autorizado ou recurso desativado. - 422 — dado recusado no processamento (ex.: chave não encontrada, saldo insuficiente). - 429 — muitas tentativas. Aguarde e tente de novo. - 500 / 503 — erro interno ou serviço temporariamente indisponível. O status também vem no header `X-App-Error-Code` (compatibilidade com clientes que só leem cabeçalhos). ### Códigos de erro O mesmo `error_code` pode aparecer em rotas diferentes. Trate sempre pelo código. ### Autenticação e acesso - `missing_credentials`401— Client ID/Secret ausentes no cabeçalho. - `invalid_credentials`401— Client ID/Secret inválidos. - `key_inactive`403— Chave de API inativa. - `permission_denied`403— A chave não tem a permissão exigida (cashin/cashout/balance). - `ip_not_configured`403— Cash-out sem IP na allowlist da chave. - `ip_not_allowed`403— IP de origem fora da allowlist (ou não IPv4). - `invalid_account`403— Conta associada à chave inválida. - `rate_limited`429— Muitas solicitações em pouco tempo. ### Chave / recebedor (PIX) - `pix_key_not_found`422— Chave PIX informada não existe no DICT. - `pix_key_invalid`422— Chave PIX com formato inválido. - `pix_key_type_invalid`422— Tipo de chave não aceito para este destino. - `invalid_document`422— CPF/CNPJ inválido ou não confere com a chave. - `name_mismatch`422— Nome/documento do recebedor não confere com a chave. - `recipient_cannot_receive`422— Conta de destino não pode receber (encerrada/inativa). - `destination_blocked`422— Chave ou conta de destino bloqueada/suspensa. ### Valor e saldo - `insufficient_balance`400— Saldo insuficiente para o valor + a taxa. - `amount_below_min`422— Valor abaixo do mínimo permitido. - `amount_above_limit`422— Valor acima do limite permitido. - `amount_unavailable`422— Nenhum trilho cobre esse valor agora, tente um valor menor. - `invalid_amount`422— Valor inválido ou zero. ### Processamento / provedor - `duplicate`422— Operação duplicada (verifique se já foi processada). - `unauthorized`403— Operação não autorizada pelo provedor. - `maintenance`503— Serviço em manutenção. - `provider_unavailable`422— Serviço indisponível ou timeout, pode retentar. - `cashin_failed`422— Falha genérica ao gerar a cobrança. - `cashout_failed`422— Falha genérica no saque (consulte o suporte). ### Cadastro / permissão da conta - `kyc_required`403— Conta sem verificação de documentos (KYC). - `currency_not_supported`400— Moeda não suportada (use BRL, MXN, ARS, COP ou USDT). - `currency_not_enabled`403— Moeda não habilitada para esta conta, fale com o suporte. - `phone_required`422— Telefone do comprador obrigatório no cash-in COP (NEQUI/BRE-B). ### Conversão - `invalid_request`400— Campos amount/from/to ausentes ou inválidos. - `conversion_failed`500— Falha ao executar a conversão (nada foi debitado). - `conversion_in_progress`409— Conversão com esse external_id já está em andamento, aguarde, não reenvie. ### Consulta / status - `missing_reference`400— Faltou o identificador (request_number / e2e / transaction_id). - `deposit_not_found`404— Nenhum depósito para o identificador informado. - `withdrawal_not_found`404— Nenhum saque para o identificador informado. Retentáveis: `provider_unavailable` e `rate_limited` são transitórios, pode repetir a mesma requisição (com o mesmo `external_id` para não duplicar). Os demais são definitivos: corrija o dado antes de reenviar. ### Exemplo de tratamento **JavaScript** ``` const r = await fetch(url, opts); const data = await r.json(); if (!data.ok) { switch (data.error_code) { case 'pix_key_not_found': case 'pix_key_invalid': // pedir outra chave ao usuário break; case 'insufficient_balance': // avisar saldo break; case 'provider_unavailable': case 'rate_limited': // retentar com backoff (mesmo external_id) break; default: // mostrar data.error e logar } } ```