Pular para o conteúdo
Documentação da API
Entrar Criar conta

API da Cherryfy

Documentação da API

Tudo o que você precisa para ligar o seu sistema à Cherryfy: criar cobranças Pix, receber o aviso de pagamento, consultar a carteira e enviar Pix direto do seu servidor.

Como funciona, em três passos

  1. 1
    Crie a credencial

    No painel, em Configurações › Chave API, gere o Token e o Secret da sua integração.

  2. 2
    Gere a cobrança

    Envie o valor e os dados do pagador e mostre o QR code para o seu cliente.

  3. 3
    Confirme o pagamento

    Receba o aviso na sua URL de postback e confirme com a consulta da transação antes de liberar o pedido.

Convenções

  • Requisições e respostas em JSON (UTF-8). Envie Content-Type: application/json nas chamadas com corpo e Accept: application/json em todas.
  • Valores em reais, com ponto decimal: 25.00.
  • Identificadores como idTransaction são texto. Guarde como string, sem presumir formato ou tamanho.
  • A API fala com o seu servidor. Nunca chame as rotas autenticadas a partir de um navegador ou aplicativo.
  • Não existe ambiente de testes separado: toda chamada vale de verdade. Teste com valores pequenos.

Os exemplos em PHP usam a extensão cURL; os de Node.js, o fetch nativo (Node 18 ou mais novo, em módulo ES); os de Python, a biblioteca requests. Em todos, as credenciais vêm de variáveis de ambiente.

Início rápido #

Da credencial ao primeiro Pix confirmado, em quatro passos.

  1. 1
    Pegue o Token e o Secret

    Painel › Configurações › Chave API › Nova credencial. O Secret aparece uma única vez: guarde na hora, em um cofre de senhas ou variável de ambiente.

  2. 2
    Crie a cobrança

    Chame POST /wallet/deposit/payment. A resposta traz o idTransaction e o qrcode (copia e cola).

  3. 3
    Mostre o QR code

    Exiba o copia e cola e a imagem do QR code para o seu cliente pagar no aplicativo do banco.

  4. 4
    Confirme

    Quando o aviso chegar na sua URL de postback, consulte POST /wallet/transaction. Só libere o pedido com status igual a PAID_OUT.

Sem ambiente de testes

As chamadas acontecem na sua conta de verdade. Para validar a integração, gere uma cobrança de valor baixo e pague você mesmo.

1. Criar a cobrança
curl -X POST \
  "https://app.cherryfy.co/api/wallet/deposit/payment" \
  -H "X-Api-Token: seu_token" \
  -H "X-Api-Secret: seu_secret" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "amount": 25.00,
    "debtor_name": "Marina Souza",
    "email": "[email protected]",
    "debtor_document_number": "12345678909",
    "phone": "11999999999",
    "method_pay": "pix",
    "postback": "https://seusite.com.br/webhooks/pix"
  }'
<?php
$base = 'https://app.cherryfy.co/api';

$ch = curl_init($base . '/wallet/deposit/payment');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Token: ' . getenv('CHERRYFY_TOKEN'),
        'X-Api-Secret: ' . getenv('CHERRYFY_SECRET'),
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount' => 25.00,
        'debtor_name' => 'Marina Souza',
        'email' => '[email protected]',
        'debtor_document_number' => '12345678909',
        'phone' => '11999999999',
        'method_pay' => 'pix',
        'postback' => 'https://seusite.com.br/webhooks/pix',
    ]),
]);

$resposta = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);

print_r([$http, $resposta]);
const BASE = 'https://app.cherryfy.co/api';

const resposta = await fetch(`${BASE}/wallet/deposit/payment`, {
  method: 'POST',
  headers: {
    'X-Api-Token': process.env.CHERRYFY_TOKEN,
    'X-Api-Secret': process.env.CHERRYFY_SECRET,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({
    amount: 25.00,
    debtor_name: 'Marina Souza',
    email: '[email protected]',
    debtor_document_number: '12345678909',
    phone: '11999999999',
    method_pay: 'pix',
    postback: 'https://seusite.com.br/webhooks/pix',
  }),
});

const dados = await resposta.json();
console.log(resposta.status, dados);
import os

import requests

BASE = "https://app.cherryfy.co/api"

resposta = requests.post(
    f"{BASE}/wallet/deposit/payment",
    headers={
        "X-Api-Token": os.environ["CHERRYFY_TOKEN"],
        "X-Api-Secret": os.environ["CHERRYFY_SECRET"],
        "Accept": "application/json",
    },
    json={
        "amount": 25.00,
        "debtor_name": "Marina Souza",
        "email": "[email protected]",
        "debtor_document_number": "12345678909",
        "phone": "11999999999",
        "method_pay": "pix",
        "postback": "https://seusite.com.br/webhooks/pix",
    },
    timeout=30,
)

print(resposta.status_code, resposta.json())
Resposta
{
  "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17",
  "qrcode": "00020126580014br.gov.bcb.pix...6304A1B2",
  "qr_code_image_url": "https://exemplo.com/qr/7c1f2b9e.png",
  "qr_code_base64": "iVBORw0KGgoAAAANSUhEUgAA..."
}
2. Confirmar o pagamento
curl -X POST \
  "https://app.cherryfy.co/api/wallet/transaction" \
  -H "X-Api-Token: seu_token" \
  -H "X-Api-Secret: seu_secret" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17"
  }'
<?php
$base = 'https://app.cherryfy.co/api';

$ch = curl_init($base . '/wallet/transaction');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Token: ' . getenv('CHERRYFY_TOKEN'),
        'X-Api-Secret: ' . getenv('CHERRYFY_SECRET'),
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'idTransaction' => '7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17',
    ]),
]);

$resposta = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);

print_r([$http, $resposta]);
const BASE = 'https://app.cherryfy.co/api';

const resposta = await fetch(`${BASE}/wallet/transaction`, {
  method: 'POST',
  headers: {
    'X-Api-Token': process.env.CHERRYFY_TOKEN,
    'X-Api-Secret': process.env.CHERRYFY_SECRET,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({
    idTransaction: '7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17',
  }),
});

const dados = await resposta.json();
console.log(resposta.status, dados);
import os

import requests

BASE = "https://app.cherryfy.co/api"

resposta = requests.post(
    f"{BASE}/wallet/transaction",
    headers={
        "X-Api-Token": os.environ["CHERRYFY_TOKEN"],
        "X-Api-Secret": os.environ["CHERRYFY_SECRET"],
        "Accept": "application/json",
    },
    json={
        "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17",
    },
    timeout=30,
)

print(resposta.status_code, resposta.json())

Autenticação #

Cada integração se identifica com um par de credenciais da conta: o Token, que diz quem está chamando, e o Secret, que prova que é você.

Onde gerar

No painel, em Configurações › Chave API, clique em Nova credencial. O Secret é mostrado uma única vez, logo depois de criar. Se perder, use Gerar novo na mesma tela: o Secret antigo para de funcionar na hora.

Como enviar

Envie as credenciais nos cabeçalhos da requisição:

CabeçalhoValor
X-Api-TokenstringobrigatórioO Token da credencial.
X-Api-SecretstringobrigatórioO Secret da credencial.
Content-Typestringapplication/json, nas chamadas com corpo.
Acceptstringapplication/json, para receber os erros sempre em JSON.
Integrações antigas

Quem já envia token e secret como campos do corpo JSON continua funcionando. Em integrações novas, use os cabeçalhos. Se os dois chegarem, vale o cabeçalho.

Nunca na URL

Uma chamada com secret na query string é recusada com 400. Endereços ficam gravados em histórico, proxies e logs.

Quando a autenticação falha

HTTPMotivo
400Token ou Secret não foram enviados.
400O Secret veio na URL.
401Token ou Secret inválidos.
401A conta ainda não foi aprovada, ou está inativa.
Requisição autenticada
curl "https://app.cherryfy.co/api/wallet/balance" \
  -H "X-Api-Token: seu_token" \
  -H "X-Api-Secret: seu_secret" \
  -H "Accept: application/json"
<?php
$base = 'https://app.cherryfy.co/api';

$ch = curl_init($base . '/wallet/balance');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Token: ' . getenv('CHERRYFY_TOKEN'),
        'X-Api-Secret: ' . getenv('CHERRYFY_SECRET'),
        'Accept: application/json',
    ],
]);

$resposta = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);

print_r([$http, $resposta]);
const BASE = 'https://app.cherryfy.co/api';

const resposta = await fetch(`${BASE}/wallet/balance`, {
  headers: {
    'X-Api-Token': process.env.CHERRYFY_TOKEN,
    'X-Api-Secret': process.env.CHERRYFY_SECRET,
    Accept: 'application/json',
  },
});

const dados = await resposta.json();
console.log(resposta.status, dados);
import os

import requests

BASE = "https://app.cherryfy.co/api"

resposta = requests.get(
    f"{BASE}/wallet/balance",
    headers={
        "X-Api-Token": os.environ["CHERRYFY_TOKEN"],
        "X-Api-Secret": os.environ["CHERRYFY_SECRET"],
        "Accept": "application/json",
    },
    timeout=30,
)

print(resposta.status_code, resposta.json())
Respostas de erro
{
  "error": "Token ou Secret ausentes",
  "message": "Você precisa fornecer tanto o token quanto o secret."
}
{
  "status": "error",
  "message": "Não envie o secret na URL. Use os cabeçalhos X-Api-Token e X-Api-Secret."
}
{
  "status": "error",
  "message": "Token ou Secret inválidos"
}
{
  "status": "error",
  "message": "Usuário com conta pendente de aprovação."
}

Ambiente #

Existe um único ambiente, o de produção. Todas as rotas desta documentação partem do endereço base:

Endereço base https://app.cherryfy.co/api
RotaO que fazCredencialLimite
POST/wallet/deposit/paymentGera uma cobrança Pix.Sim60 por minuto
POST/statusDevolve o status de uma cobrança.Não20 por minuto
POST/pixoutEnvia um Pix a partir do saldo.Sim, e IP permitido30 por minuto
GET/wallet/balanceConsulta o saldo da carteira.Sim60 por minuto
POST/wallet/transactionConsulta uma transação da conta.Sim60 por minuto

Use sempre HTTPS. Os limites são explicados em Limites de requisição.

Erros e códigos de retorno #

A API responde com os códigos HTTP abaixo. Decida o que fazer pelo código; use o campo message para registrar e mostrar o motivo.

HTTPQuando acontece
200Deu certo.
400Credenciais ausentes, ou Secret enviado na URL.
401Credenciais inválidas ou conta pendente de aprovação. Também é o código das regras de valor: abaixo do mínimo e saldo insuficiente.
403Saque vindo de um IP fora da lista de permitidos, ou com baasPostbackUrl igual a web.
404A transação consultada não existe nesta conta.
422Campo obrigatório faltando ou com formato inválido. O corpo traz errors, com as mensagens de cada campo.
429Limite de requisições atingido. Espere o tempo do cabeçalho Retry-After.
500Falha interna ou no processador do pagamento. Tente de novo mais tarde; em saque, repita com o mesmo external_id para não pagar duas vezes.

Formato do corpo de erro

O formato padrão tem status igual a error, uma message legível e, nas validações, o objeto errors.

Nem todo erro segue o padrão

Três respostas têm outro formato: credenciais ausentes usam a chave error; o bloqueio por IP devolve success: false e o client_ip que o servidor enxergou; e o limite de requisições traz só message. Por isso, confie primeiro no código HTTP.

Exemplos de erro
{
  "status": "error",
  "message": "Erro de validação",
  "errors": {
    "email": [
      "O campo email é obrigatório."
    ],
    "phone": [
      "O campo phone é obrigatório."
    ]
  }
}
{
  "status": "error",
  "message": "Token ou Secret inválidos"
}
{
  "success": false,
  "message": "IP não autorizado para realizar saques",
  "client_ip": "203.0.113.10"
}
{
  "status": "error",
  "message": "Transação não encontrada nesta conta."
}
{
  "message": "Too Many Attempts."
}
{
  "status": "error",
  "message": "Nenhum adquirente configurado."
}

Limites de requisição #

Cada rota aceita um número máximo de chamadas por minuto. Passando do teto, a resposta é 429 até a janela de um minuto virar.

RotaTeto por minuto
POST /wallet/deposit/payment60
GET /wallet/balance60
POST /wallet/transaction60
POST /pixout30
POST /status20

Como a contagem funciona

  • A contagem é por IP de origem, em janelas de um minuto.
  • Cada rota tem a sua própria contagem. A exceção são as duas consultas da carteira, GET /wallet/balance e POST /wallet/transaction, que somam juntas no mesmo teto de 60.
  • Toda resposta traz X-RateLimit-Limit e X-RateLimit-Remaining. No 429 vêm também Retry-After, em segundos, e X-RateLimit-Reset, em tempo Unix.
Prefira o aviso à consulta em laço

Em vez de perguntar o status a cada segundo, espere o webhook e faça uma única consulta para confirmar. Em caso de 429, aguarde o Retry-After antes de tentar outra vez.

Cabeçalhos de uma resposta normal
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
Resposta ao passar do limite
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 20
X-RateLimit-Remaining: 0
Retry-After: 60
X-RateLimit-Reset: 1790770129

{
  "message": "Too Many Attempts."
}

Segurança #

A API movimenta dinheiro. Estas são as proteções que existem e o que a sua integração precisa fazer para que elas funcionem.

HTTPS, sempre

Chame a API somente por https:// e publique as suas URLs de aviso também em HTTPS.

Credenciais só no servidor

Token e Secret vão nos cabeçalhos e ficam em variável de ambiente ou cofre de segredos. Nunca em código de front-end, aplicativo, repositório ou URL.

Secret aparece uma vez só

O Secret aparece uma vez, na criação. Se suspeitar de vazamento, gere um novo no painel: o anterior deixa de valer na hora.

Saque só de IPs permitidos

O saque pela API exige que o IP de origem esteja em Perfil › Segurança › IPs permitidos. Sem IP cadastrado, todo saque pela API é recusado.

Limite por rota

Cada rota tem um teto de chamadas por minuto. Quem passa do teto recebe 429. Veja Limites de requisição.

Confirme todo aviso

Os webhooks não são assinados. Antes de liberar produto ou saldo, confirme a transação com POST /wallet/transaction.

Trate avisos repetidos

Responda 200 rápido e processe cada idTransaction uma única vez. O mesmo aviso pode chegar mais de uma vez.

Painel com 2 etapas e PIN

No painel, o login pode exigir o código de um app autenticador, e o saque pede um PIN de 6 dígitos, com bloqueio de até 15 minutos depois de 5 erros.

O que esta API não tem

Não há assinatura nos webhooks nem ambiente de testes. Por isso três cuidados são obrigatórios: confirmar todo aviso pela API, deduplicar por idTransaction e mandar um external_id seu em todo saque. Se a chamada de saque cair no meio, repetir com o mesmo external_id devolve o saque já criado em vez de pagar de novo.

Webhooks #

Um webhook é um POST em JSON que a Cherryfy faz no seu servidor quando algo muda. Existem dois tipos:

TipoPara onde vaiQuando dispara
Por transaçãoPara a URL que você manda em cada chamada: postback na cobrança, baasPostbackUrl no saque.Quando a cobrança é paga ou estornada, e quando o saque é concluído ou falha.
Da contaPara a URL cadastrada em Configurações › Webhooks.Nas vendas do checkout de produtos: pedido gerado e pedido pago.

O que o seu endpoint precisa fazer

  • Aceitar POST com Content-Type: application/json em uma URL pública, em HTTPS.
  • Responder com um código 2xx em poucos segundos. O processamento pesado fica para depois da resposta.
  • Ser idempotente: o mesmo aviso pode chegar mais de uma vez.
  • Não confiar no corpo recebido. Use o idTransaction para confirmar pela API.
Se o aviso não chegar

Cada aviso é enviado uma única vez, no momento do evento, com 15 segundos de espera pela sua resposta. Não há reenvio: se o seu servidor estiver fora do ar, o aviso se perde. Tenha uma rotina que consulta as transações que continuam em aberto.

Pix recebido #

Enviado para a URL de postback da cobrança quando o pagamento é confirmado. Nesse momento a cobrança passa para PAID_OUT e o valor líquido entra no saldo. O aviso sai depois de o saldo ser gravado: a consulta da transação feita na hora já mostra PAID_OUT.

CampoDescrição
statusstringpaid quando a cobrança foi paga. refunded quando um Pix já pago foi devolvido ao pagador (estorno ou MED): nesse caso o líquido sai do saldo e a cobrança passa para REFUNDED.
idTransactionstringO mesmo identificador devolvido na criação da cobrança.
typeTransactionstringPIX no pagamento; PIX_REFUND no estorno.
methodstringMeio da cobrança: pix ou crypto. Só no pagamento.
amountnumberValor bruto da cobrança.
deposito_liquidonumberValor que entrou no saldo (bruto menos a taxa da sua conta).
debtor_namestringNome do pagador informado na cobrança.
emailstringE-mail do pagador.
debtor_document_numberstringCPF ou CNPJ do pagador.
phonestringTelefone do pagador.
endToEndstring ou nullIdentificador da transação no Pix, quando o processador informa.
created_atstringCriação da cobrança, em ISO 8601 (UTC).
paid_atstringConfirmação do pagamento, em ISO 8601 (UTC).
split_processedbooleantrue quando a cobrança foi criada com split.
split_amountnumberValor bruto multiplicado pelo percentual do split. É uma referência: o repasse efetivo é calculado sobre o valor líquido.
split_recipientstring ou nullE-mail da conta que recebe o split.
refunded_atstringSó no estorno: momento da devolução, em ISO 8601 (UTC).
O aviso é só o gatilho

Use status, idTransaction e typeTransaction para saber o que aconteceu. Valor, líquido, taxa e pagador: confirme na consulta da transação, nunca pelo corpo do aviso. No estorno, os dados do pagador não vêm.

Corpo do aviso
{
  "status": "paid",
  "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17",
  "typeTransaction": "PIX",
  "method": "pix",
  "amount": 25,
  "deposito_liquido": 23.75,
  "debtor_name": "Marina Souza",
  "email": "[email protected]",
  "debtor_document_number": "12345678909",
  "phone": "11999999999",
  "endToEnd": "E12345678202609301500410000000001",
  "created_at": "2026-09-30T14:58:03.000000Z",
  "paid_at": "2026-09-30T15:00:41.000000Z",
  "split_processed": false,
  "split_amount": 0,
  "split_recipient": null
}
{
  "status": "refunded",
  "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17",
  "typeTransaction": "PIX_REFUND",
  "amount": 25,
  "deposito_liquido": 23.75,
  "endToEnd": "E12345678202609301500410000000001",
  "refunded_at": "2026-10-01T12:10:05.000000Z"
}

Saque #

Enviado para a baasPostbackUrl do saque quando o processador confirma a transferência ou a recusa. Uma única tentativa, com 15 segundos de espera pela sua resposta.

CampoDescrição
statusstringpaid quando o Pix foi enviado. failed quando o processador recusou a transferência: o saque passa para REJECTED e o valor volta para o saldo.
idstringO id devolvido por POST /pixout.
idTransactionstringO mesmo valor de id.
typeTransactionstringSempre PIX.
amountnumberValor do saque.
cash_out_liquidonumberValor enviado ao beneficiário.
pix_keystringChave Pix de destino.
pix_key_typestringTipo da chave.
beneficiary_namestringNome registrado no saque.
beneficiary_documentstringDocumento registrado no saque.
endToEndstring ou nullIdentificador da transferência no Pix, quando o processador informa. null na falha.
messagestring ou nullMotivo informado pelo processador quando o saque falha. null quando pago.
datestringData do pedido, no formato AAAA-MM-DD HH:MM:SS.
timestampstringMomento do envio do aviso, em ISO 8601 (UTC).
Se o aviso não chegar

O processador pode demorar para confirmar. Se nada chegar em alguns minutos, consulte a transação pelo id: os status finais são PAID_OUT (enviado) e REJECTED (não enviado, valor devolvido ao saldo). Enquanto estiver PENDING, aguarde.

Corpo do aviso
{
  "status": "paid",
  "id": "b4d2a6f0-1c7e-4f3a-8a55-9e0c2d7b6f41",
  "idTransaction": "b4d2a6f0-1c7e-4f3a-8a55-9e0c2d7b6f41",
  "typeTransaction": "PIX",
  "amount": 50,
  "cash_out_liquido": 50,
  "pix_key": "12345678909",
  "pix_key_type": "CPF",
  "beneficiary_name": "Sua Empresa LTDA",
  "beneficiary_document": "12345678909",
  "endToEnd": "E12345678202609301500120000000002",
  "message": null,
  "date": "2026-09-30 12:00:00",
  "timestamp": "2026-09-30T15:00:12.000000Z"
}
{
  "status": "failed",
  "message": "Invalid Pix Entry",
  "endToEnd": null,
  "id": "b4d2a6f0-1c7e-4f3a-8a55-9e0c2d7b6f41",
  "idTransaction": "b4d2a6f0-1c7e-4f3a-8a55-9e0c2d7b6f41",
  "typeTransaction": "PIX",
  "amount": 50,
  "cash_out_liquido": 50,
  "pix_key": "12345678909",
  "pix_key_type": "CPF",
  "beneficiary_name": "Sua Empresa LTDA",
  "beneficiary_document": "12345678909",
  "date": "2026-09-30 12:00:00",
  "timestamp": "2026-09-30T15:00:12.000000Z"
}

Webhooks da conta #

Em Configurações › Webhooks você cadastra uma URL e escolhe os eventos que ela recebe. Eles acompanham as vendas feitas pelo checkout de produtos da Cherryfy e levam os dados do comprador, úteis para CRM, e-mail e recuperação de carrinho.

EventoQuando disparastatus no corpo
Pix gerado (gerado)O comprador gera o pedido no checkout.pendente
Pix pago (pago)O pagamento do pedido é confirmado.pago
CampoDescrição
nomestringNome do comprador.
cpfstringCPF do comprador, só dígitos.
telefonestringTelefone do comprador, só dígitos.
emailstringE-mail do comprador.
statusstringpendente ou pago.
Cobranças criadas pela API não passam por aqui

Uma cobrança gerada com POST /wallet/deposit/payment avisa somente a URL de postback daquela cobrança. Os webhooks da conta não trazem idTransaction nem valor.

Corpo do aviso
{
  "nome": "Marina Souza",
  "cpf": "12345678909",
  "telefone": "11999999999",
  "email": "[email protected]",
  "status": "pendente"
}
{
  "nome": "Marina Souza",
  "cpf": "12345678909",
  "telefone": "11999999999",
  "email": "[email protected]",
  "status": "pago"
}

Como confirmar um aviso #

Os avisos não são assinados: nada no corpo prova que ele veio da Cherryfy. Qualquer pessoa que descubra a sua URL consegue mandar um POST igual. Por isso o aviso serve só como gatilho, e a verdade vem da API.

  1. 1
    Responda 200

    Devolva a resposta na hora. Não segure a conexão enquanto processa.

  2. 2
    Pegue só o idTransaction

    Ignore o resto do corpo para decidir qualquer coisa.

  3. 3
    Consulte a transação

    Chame POST /wallet/transaction com as suas credenciais. A consulta só enxerga transações da sua conta.

  4. 4
    Confira status e valor

    Para Pix recebido, exija type igual a cash_in, status igual a PAID_OUT e o amount que você esperava para aquele pedido.

  5. 5
    Libere uma única vez

    Marque o idTransaction como processado no seu banco. Se o aviso chegar de novo, não faça nada.

Dica: use um caminho difícil de adivinhar na URL de aviso (por exemplo, com um identificador aleatório). Isso reduz ruído, mas não substitui a confirmação.

Receptor que confirma pela API
# Simule o aviso no SEU endpoint de testes.
# Repare: qualquer um consegue mandar este POST, por isso o
# receptor confirma pela API antes de liberar qualquer coisa.
curl -X POST "https://seusite.com.br/webhooks/pix" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "paid",
    "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17",
    "typeTransaction": "PIX"
  }'
<?php
// webhook-pix.php: recebe o aviso e confirma pela API antes de liberar
$aviso = json_decode(file_get_contents('php://input'), true) ?: [];
$id = $aviso['idTransaction'] ?? null;

http_response_code(200); // responda rápido, com 2xx
if (function_exists('fastcgi_finish_request')) {
    fastcgi_finish_request(); // no PHP-FPM, devolve a resposta antes da consulta
}
if (!is_string($id) || $id === '') {
    exit;
}

$base = 'https://app.cherryfy.co/api';
$ch = curl_init($base . '/wallet/transaction');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Token: ' . getenv('CHERRYFY_TOKEN'),
        'X-Api-Secret: ' . getenv('CHERRYFY_SECRET'),
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['idTransaction' => $id]),
]);
$consulta = json_decode(curl_exec($ch), true);

$t = $consulta['transaction'] ?? null;
if ($t && $t['type'] === 'cash_in' && $t['status'] === 'PAID_OUT') {
    // Aqui: libere o pedido UMA vez por idTransaction,
    // conferindo se $t['amount'] é o valor que você esperava.
    error_log('Pix confirmado: ' . $t['idTransaction'] . ' R$ ' . $t['amount']);
}
import http from 'node:http';

const BASE = 'https://app.cherryfy.co/api';

async function confirmar(idTransaction) {
  const resposta = await fetch(`${BASE}/wallet/transaction`, {
    method: 'POST',
    headers: {
      'X-Api-Token': process.env.CHERRYFY_TOKEN,
      'X-Api-Secret': process.env.CHERRYFY_SECRET,
      'Content-Type': 'application/json',
      Accept: 'application/json',
    },
    body: JSON.stringify({ idTransaction }),
  });
  const dados = await resposta.json();
  return dados.transaction ?? null;
}

http.createServer((req, res) => {
  let corpo = '';
  req.on('data', (parte) => { corpo += parte; });
  req.on('end', async () => {
    res.writeHead(200).end(); // responda rápido, com 2xx

    let aviso = {};
    try { aviso = JSON.parse(corpo); } catch { return; }
    if (typeof aviso.idTransaction !== 'string') return;

    const t = await confirmar(aviso.idTransaction);
    if (t && t.type === 'cash_in' && t.status === 'PAID_OUT') {
      // Aqui: libere o pedido UMA vez por idTransaction,
      // conferindo se t.amount é o valor que você esperava.
      console.log('Pix confirmado:', t.idTransaction, t.amount);
    }
  });
}).listen(3000);
import json
import os
from http.server import BaseHTTPRequestHandler, HTTPServer

import requests

BASE = "https://app.cherryfy.co/api"


def confirmar(id_transacao):
    resposta = requests.post(
        f"{BASE}/wallet/transaction",
        headers={
            "X-Api-Token": os.environ["CHERRYFY_TOKEN"],
            "X-Api-Secret": os.environ["CHERRYFY_SECRET"],
            "Accept": "application/json",
        },
        json={"idTransaction": id_transacao},
        timeout=30,
    )
    return resposta.json().get("transaction")


class Aviso(BaseHTTPRequestHandler):
    def do_POST(self):
        tamanho = int(self.headers.get("Content-Length", 0))
        corpo = self.rfile.read(tamanho)
        self.send_response(200)  # responda rápido, com 2xx
        self.send_header("Content-Length", "0")
        self.end_headers()

        try:
            aviso = json.loads(corpo or b"{}")
        except ValueError:
            return
        id_transacao = aviso.get("idTransaction") if isinstance(aviso, dict) else None
        if not isinstance(id_transacao, str):
            return

        t = confirmar(id_transacao)
        if t and t["type"] == "cash_in" and t["status"] == "PAID_OUT":
            # Aqui: libere o pedido UMA vez por idTransaction,
            # conferindo se t["amount"] é o valor que você esperava.
            print("Pix confirmado:", t["idTransaction"], t["amount"])


HTTPServer(("0.0.0.0", 3000), Aviso).serve_forever()

API de Pix

Cobranças (Pix recebido) e transferências (Pix enviado). Toda cobrança nasce com o status WAITING_FOR_APPROVAL e passa para PAID_OUT quando é paga.

Gerar cobrança #

POST/wallet/deposit/payment

Cria uma cobrança Pix e devolve o código copia e cola e a imagem do QR code. Quando o pagamento é confirmado, o valor líquido entra no saldo da carteira e a URL de postback é avisada.

Corpo da requisição

CampoDescrição
amountnumberobrigatórioValor da cobrança em reais, por exemplo 25.00, com até duas casas decimais e no máximo 999999.99. Precisa respeitar o valor mínimo da plataforma.
debtor_namestringobrigatórioNome do pagador.
emailstringobrigatórioE-mail do pagador, em formato válido.
debtor_document_numberstringopcionalCPF ou CNPJ do pagador. Envie sempre que tiver.
phonestringobrigatórioTelefone do pagador, com DDD.
method_paystringobrigatórioMeio de pagamento. Use pix. Para receber em BTC ou USDT, veja Receber em cripto.
postbackstringobrigatórioURL pública (http ou https) do seu sistema que recebe o aviso de Pix recebido. Endereços internos, localhost e domínios que não resolvem são recusados.
split_emailstringopcionalE-mail da conta que recebe parte do valor. Veja Cobrança com split.
split_percentagenumberopcionalPercentual do split, de 0 a 100.

Resposta 200

CampoDescrição
idTransactionstringIdentificador da cobrança. Guarde: é com ele que você reconhece o aviso e faz as consultas.
qrcodestringCódigo Pix copia e cola.
qr_code_image_urlstring ou nullImagem do QR code pronta para exibir. Se não vier, gere o QR code a partir do campo qrcode.
qr_code_base64string ou nullA mesma imagem em PNG, codificada em base64 e sem o prefixo data:. Para exibir sem depender de outro servidor: <img src="data:image/png;base64,...">.

A resposta traz também um objeto charge com os mesmos dados. Use os campos acima e ignore o que não conhecer.

Erros desta rota

HTTPMotivo
422Campo obrigatório faltando, e-mail inválido, valor com mais de duas casas decimais, percentual de split fora de 0 a 100, ou postback que não é uma URL pública. O corpo traz errors com o campo e o motivo.
401Valor abaixo do mínimo de depósito da plataforma. A mensagem informa o mínimo.
500O processador do pagamento não gerou o QR code, ou a plataforma está sem processador ativo. Tente de novo mais tarde.

Mais os erros de autenticação e de limite, comuns a todas as rotas.

Requisição
curl -X POST \
  "https://app.cherryfy.co/api/wallet/deposit/payment" \
  -H "X-Api-Token: seu_token" \
  -H "X-Api-Secret: seu_secret" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "amount": 25.00,
    "debtor_name": "Marina Souza",
    "email": "[email protected]",
    "debtor_document_number": "12345678909",
    "phone": "11999999999",
    "method_pay": "pix",
    "postback": "https://seusite.com.br/webhooks/pix"
  }'
<?php
$base = 'https://app.cherryfy.co/api';

$ch = curl_init($base . '/wallet/deposit/payment');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Token: ' . getenv('CHERRYFY_TOKEN'),
        'X-Api-Secret: ' . getenv('CHERRYFY_SECRET'),
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount' => 25.00,
        'debtor_name' => 'Marina Souza',
        'email' => '[email protected]',
        'debtor_document_number' => '12345678909',
        'phone' => '11999999999',
        'method_pay' => 'pix',
        'postback' => 'https://seusite.com.br/webhooks/pix',
    ]),
]);

$resposta = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);

print_r([$http, $resposta]);
const BASE = 'https://app.cherryfy.co/api';

const resposta = await fetch(`${BASE}/wallet/deposit/payment`, {
  method: 'POST',
  headers: {
    'X-Api-Token': process.env.CHERRYFY_TOKEN,
    'X-Api-Secret': process.env.CHERRYFY_SECRET,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({
    amount: 25.00,
    debtor_name: 'Marina Souza',
    email: '[email protected]',
    debtor_document_number: '12345678909',
    phone: '11999999999',
    method_pay: 'pix',
    postback: 'https://seusite.com.br/webhooks/pix',
  }),
});

const dados = await resposta.json();
console.log(resposta.status, dados);
import os

import requests

BASE = "https://app.cherryfy.co/api"

resposta = requests.post(
    f"{BASE}/wallet/deposit/payment",
    headers={
        "X-Api-Token": os.environ["CHERRYFY_TOKEN"],
        "X-Api-Secret": os.environ["CHERRYFY_SECRET"],
        "Accept": "application/json",
    },
    json={
        "amount": 25.00,
        "debtor_name": "Marina Souza",
        "email": "[email protected]",
        "debtor_document_number": "12345678909",
        "phone": "11999999999",
        "method_pay": "pix",
        "postback": "https://seusite.com.br/webhooks/pix",
    },
    timeout=30,
)

print(resposta.status_code, resposta.json())
Resposta
{
  "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17",
  "qrcode": "00020126580014br.gov.bcb.pix...6304A1B2",
  "qr_code_image_url": "https://exemplo.com/qr/7c1f2b9e.png",
  "qr_code_base64": "iVBORw0KGgoAAAANSUhEUgAA..."
}
{
  "status": "error",
  "message": "Erro de validação",
  "errors": {
    "email": [
      "O campo email é obrigatório."
    ],
    "phone": [
      "O campo phone é obrigatório."
    ]
  }
}
{
  "status": "error",
  "message": "Nenhum adquirente configurado."
}

Cobrança com split #

POST/wallet/deposit/payment

É a mesma rota da cobrança, com dois campos a mais. Quando o Pix é pago, uma parte do valor é repassada automaticamente para outra conta da Cherryfy.

CampoDescrição
split_emailstringopcionalE-mail de cadastro da conta que recebe o repasse.
split_percentagenumberopcionalPercentual repassado, de 0 a 100.

Regras

  • O split só acontece quando os dois campos são enviados.
  • O percentual é aplicado sobre o valor líquido da cobrança, ou seja, depois da taxa da sua conta.
  • O repasse é feito na confirmação do pagamento: o valor líquido entra no seu saldo e a parte do split sai para a conta de destino em seguida.
  • O e-mail precisa ser de uma conta existente na Cherryfy. Se não for, o repasse não acontece e o valor fica inteiro com você.
  • O aviso de Pix recebido traz split_processed, split_amount e split_recipient.

A vitrine de afiliação do painel usa este mesmo mecanismo para pagar a comissão do afiliado.

Requisição com split de 10%
curl -X POST \
  "https://app.cherryfy.co/api/wallet/deposit/payment" \
  -H "X-Api-Token: seu_token" \
  -H "X-Api-Secret: seu_secret" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "amount": 100.00,
    "debtor_name": "Marina Souza",
    "email": "[email protected]",
    "debtor_document_number": "12345678909",
    "phone": "11999999999",
    "method_pay": "pix",
    "postback": "https://seusite.com.br/webhooks/pix",
    "split_email": "[email protected]",
    "split_percentage": 10
  }'
<?php
$base = 'https://app.cherryfy.co/api';

$ch = curl_init($base . '/wallet/deposit/payment');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Token: ' . getenv('CHERRYFY_TOKEN'),
        'X-Api-Secret: ' . getenv('CHERRYFY_SECRET'),
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount' => 100.00,
        'debtor_name' => 'Marina Souza',
        'email' => '[email protected]',
        'debtor_document_number' => '12345678909',
        'phone' => '11999999999',
        'method_pay' => 'pix',
        'postback' => 'https://seusite.com.br/webhooks/pix',
        'split_email' => '[email protected]',
        'split_percentage' => 10,
    ]),
]);

$resposta = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);

print_r([$http, $resposta]);
const BASE = 'https://app.cherryfy.co/api';

const resposta = await fetch(`${BASE}/wallet/deposit/payment`, {
  method: 'POST',
  headers: {
    'X-Api-Token': process.env.CHERRYFY_TOKEN,
    'X-Api-Secret': process.env.CHERRYFY_SECRET,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({
    amount: 100.00,
    debtor_name: 'Marina Souza',
    email: '[email protected]',
    debtor_document_number: '12345678909',
    phone: '11999999999',
    method_pay: 'pix',
    postback: 'https://seusite.com.br/webhooks/pix',
    split_email: '[email protected]',
    split_percentage: 10,
  }),
});

const dados = await resposta.json();
console.log(resposta.status, dados);
import os

import requests

BASE = "https://app.cherryfy.co/api"

resposta = requests.post(
    f"{BASE}/wallet/deposit/payment",
    headers={
        "X-Api-Token": os.environ["CHERRYFY_TOKEN"],
        "X-Api-Secret": os.environ["CHERRYFY_SECRET"],
        "Accept": "application/json",
    },
    json={
        "amount": 100.00,
        "debtor_name": "Marina Souza",
        "email": "[email protected]",
        "debtor_document_number": "12345678909",
        "phone": "11999999999",
        "method_pay": "pix",
        "postback": "https://seusite.com.br/webhooks/pix",
        "split_email": "[email protected]",
        "split_percentage": 10,
    },
    timeout=30,
)

print(resposta.status_code, resposta.json())

Consultar status #

POST/status

Consulta simples, que devolve só o status de uma cobrança. Não pede credencial: foi pensada para a tela de pagamento acompanhar o QR code.

Corpo da requisição

CampoDescrição
idTransactionstringobrigatórioO identificador devolvido na criação da cobrança.

Resposta 200

CampoDescrição
statusstringUm dos status de cobrança, ou NOT_FOUND quando o identificador não existe. Atenção: NOT_FOUND também volta com HTTP 200.
  • Só enxerga cobranças. Para saques, use a consulta da transação.
  • O teto é de 20 chamadas por minuto, por IP. Em uma tela de pagamento, consulte a cada 5 segundos ou mais. Se a consulta sair do seu servidor, o teto vale para todos os seus clientes juntos: nesse caso, espere o aviso.
  • Para decidir a liberação de um pedido no servidor, prefira a consulta autenticada, que também devolve o valor.
Requisição
curl -X POST \
  "https://app.cherryfy.co/api/status" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17"
  }'
<?php
$base = 'https://app.cherryfy.co/api';

$ch = curl_init($base . '/status');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'idTransaction' => '7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17',
    ]),
]);

$resposta = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);

print_r([$http, $resposta]);
const BASE = 'https://app.cherryfy.co/api';

const resposta = await fetch(`${BASE}/status`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({
    idTransaction: '7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17',
  }),
});

const dados = await resposta.json();
console.log(resposta.status, dados);
import requests

BASE = "https://app.cherryfy.co/api"

resposta = requests.post(
    f"{BASE}/status",
    headers={
        "Accept": "application/json",
    },
    json={
        "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17",
    },
    timeout=30,
)

print(resposta.status_code, resposta.json())
Resposta
{
  "status": "WAITING_FOR_APPROVAL"
}
{
  "status": "PAID_OUT"
}
{
  "status": "NOT_FOUND"
}

Transferir / sacar #

POST/pixout

Envia um Pix do saldo da carteira para uma chave Pix. Pela API o pedido vai para o processador na hora, sem passar pela fila de aprovação do painel. É a rota mais sensível da API e tem três exigências:

  • Credencial válida de uma conta ativa.
  • IP de origem permitido, cadastrado em Perfil › Segurança › IPs permitidos. Aceita IP único (203.0.113.10), faixa CIDR (203.0.113.0/24) ou curinga (203.0.113.*).
  • Saldo para cobrir o valor mais a taxa de saque da sua conta.

Corpo da requisição

CampoDescrição
amountnumberobrigatórioValor a enviar, em reais. O beneficiário recebe esse valor; a taxa de saque é debitada do saldo à parte.
pixKeystringobrigatórioChave Pix de destino.
pixKeyTypestringobrigatórioTipo da chave: cpf, cnpj, email, telefone (ou phone) ou aleatoria.
baasPostbackUrlstringobrigatórioURL pública (http ou https) do seu sistema que recebe o aviso do saque. O valor web não é aceito.
external_idstringopcionalIdentificador do pedido no seu sistema, até 64 caracteres (letras, números, ponto, traço, sublinhado e dois-pontos). Repetir a chamada com o mesmo external_id devolve o saque já criado em vez de pagar de novo. Envie sempre.

Resposta 200

CampoDescrição
idstringIdentificador do saque. É o idTransaction usado no aviso e na consulta da transação.
external_idstring ou nullO external_id que você enviou.
amountnumberValor solicitado.
pixKeystringChave de destino. CPF, CNPJ e telefone podem voltar só com os dígitos.
pixKeyTypestringTipo da chave, em maiúsculas e no padrão do processador: CPF, CNPJ, EMAIL, PHONE ou EVP (aleatória).
withdrawStatusIdstringPendingProcessing: enviado ao processador, aguardando a confirmação. Paid: concluído. Failed: recusado, valor devolvido. PendingApproval: só em saques pedidos pelo painel. Trate qualquer outro valor como pedido em andamento.
createdAtstringCriação do pedido, em ISO 8601 (UTC).
updatedAtstringÚltima atualização, em ISO 8601 (UTC).
idempotentebooleanSó aparece, com true, quando a resposta é de um saque já existente, devolvido porque o external_id se repetiu.

Um 200 significa que o pedido foi registrado e enviado, não que o dinheiro chegou. Considere o saque aceito somente quando a resposta trouxer id, e espere o status final pelo aviso ou pela consulta da transação.

Erros desta rota

HTTPMotivo
403IP de origem fora da lista de permitidos. O corpo traz client_ip, o IP que o servidor enxergou: é esse que deve ser cadastrado.
403baasPostbackUrl com o valor web, reservado ao painel.
401Saldo insuficiente para o valor mais a taxa, ou valor abaixo do saque mínimo da plataforma.
422Campo obrigatório faltando, pixKeyType fora da lista, external_id fora do formato ou baasPostbackUrl que não é uma URL pública.
500O processador recusou ou não respondeu. Repita com o mesmo external_id: se o saque chegou a ser registrado, ele volta em vez de ser pago de novo.
Repita só com o mesmo external_id

Se a sua chamada terminar em erro de rede ou tempo esgotado, o saque pode ter sido registrado mesmo assim. Repita com o mesmo external_id: a resposta devolve o saque existente (com idempotente igual a true) ou cria um novo se nada tinha sido registrado. Sem external_id, confira o saldo em GET /wallet/balance e o relatório de saídas do painel antes de enviar de novo.

Requisição
curl -X POST \
  "https://app.cherryfy.co/api/pixout" \
  -H "X-Api-Token: seu_token" \
  -H "X-Api-Secret: seu_secret" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "amount": 50.00,
    "pixKey": "12345678909",
    "pixKeyType": "cpf",
    "baasPostbackUrl": "https://seusite.com.br/webhooks/saque",
    "external_id": "pedido-8841"
  }'
<?php
$base = 'https://app.cherryfy.co/api';

$ch = curl_init($base . '/pixout');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Token: ' . getenv('CHERRYFY_TOKEN'),
        'X-Api-Secret: ' . getenv('CHERRYFY_SECRET'),
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount' => 50.00,
        'pixKey' => '12345678909',
        'pixKeyType' => 'cpf',
        'baasPostbackUrl' => 'https://seusite.com.br/webhooks/saque',
        'external_id' => 'pedido-8841',
    ]),
]);

$resposta = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);

print_r([$http, $resposta]);
const BASE = 'https://app.cherryfy.co/api';

const resposta = await fetch(`${BASE}/pixout`, {
  method: 'POST',
  headers: {
    'X-Api-Token': process.env.CHERRYFY_TOKEN,
    'X-Api-Secret': process.env.CHERRYFY_SECRET,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({
    amount: 50.00,
    pixKey: '12345678909',
    pixKeyType: 'cpf',
    baasPostbackUrl: 'https://seusite.com.br/webhooks/saque',
    external_id: 'pedido-8841',
  }),
});

const dados = await resposta.json();
console.log(resposta.status, dados);
import os

import requests

BASE = "https://app.cherryfy.co/api"

resposta = requests.post(
    f"{BASE}/pixout",
    headers={
        "X-Api-Token": os.environ["CHERRYFY_TOKEN"],
        "X-Api-Secret": os.environ["CHERRYFY_SECRET"],
        "Accept": "application/json",
    },
    json={
        "amount": 50.00,
        "pixKey": "12345678909",
        "pixKeyType": "cpf",
        "baasPostbackUrl": "https://seusite.com.br/webhooks/saque",
        "external_id": "pedido-8841",
    },
    timeout=30,
)

print(resposta.status_code, resposta.json())
Resposta
{
  "id": "b4d2a6f0-1c7e-4f3a-8a55-9e0c2d7b6f41",
  "external_id": "pedido-8841",
  "amount": 50,
  "pixKey": "12345678909",
  "pixKeyType": "CPF",
  "withdrawStatusId": "PendingProcessing",
  "createdAt": "2026-09-30T15:00:00.000000Z",
  "updatedAt": "2026-09-30T15:00:00.000000Z"
}
{
  "id": "b4d2a6f0-1c7e-4f3a-8a55-9e0c2d7b6f41",
  "external_id": "pedido-8841",
  "amount": 50,
  "pixKey": "12345678909",
  "pixKeyType": "CPF",
  "withdrawStatusId": "PendingProcessing",
  "createdAt": "2026-09-30T15:00:00.000000Z",
  "updatedAt": "2026-09-30T15:00:00.000000Z",
  "idempotente": true
}
{
  "success": false,
  "message": "IP não autorizado para realizar saques",
  "client_ip": "203.0.113.10"
}
{
  "status": "error",
  "message": "Saque pelo painel exige sessão e PIN. Pela API, informe a URL de postback do seu sistema."
}
{
  "status": "error",
  "message": "Saldo Insuficiente."
}

API de Cripto

Receber em cripto #

POST/wallet/deposit/payment

Mesma rota da cobrança Pix, com method_pay igual a crypto. Você informa o valor em reais e a moeda; a resposta traz o endereço de carteira e a quantia exata em cripto que o pagador precisa enviar. Quando a rede confirma, o processador converte para reais e o valor líquido entra na carteira, com o mesmo aviso de pagamento recebido.

Disponível para contas cujo processador tem cripto habilitado. Nas demais, a rota responde 422 sem criar nada.

Corpo da requisição

CampoDescrição
amountnumberobrigatórioValor em reais que você quer receber, por exemplo 100.00. A quantia em cripto é calculada pelo processador na cotação do momento.
method_paystringobrigatórioUse crypto.
currencystringobrigatórioMoeda que o pagador vai enviar: BTC ou USDT.
debtor_namestringobrigatórioNome do pagador.
emailstringobrigatórioE-mail do pagador.
phonestringobrigatórioTelefone do pagador, com DDD.
debtor_document_numberstringopcionalCPF ou CNPJ do pagador, se tiver.
postbackstringobrigatórioURL pública (http ou https) do seu sistema que recebe o aviso quando o valor cair.

Resposta 200

CampoDescrição
idTransactionstringIdentificador da cobrança, o mesmo que volta no aviso e nas consultas.
walletAddressstringEndereço de carteira que o pagador deve usar. Vale só para esta cobrança.
cryptoAmountstringQuantia exata em cripto a enviar. Mostre ao pagador junto com o endereço; valores diferentes podem não ser reconhecidos.
currencystringMoeda, como enviada: BTC ou USDT.
networkstringRede informada pelo processador para o envio.
amountnumberValor em reais pedido na cobrança.
qr_code_image_urlstringQR code do endereço, pronto para exibir.

A cotação muda até a confirmação na rede: o valor creditado é o que o processador converteu de fato, e é ele que vem no campo amount do aviso. Não trate a cobrança como paga antes do aviso.

Erros desta rota

HTTPMotivo
422Moeda fora de BTC/USDT, campo obrigatório faltando, ou conta sem processador com cripto.
401Valor abaixo do mínimo de depósito da plataforma.
500O processador não gerou o endereço. Tente de novo mais tarde.
Requisição
curl -X POST \
  "https://app.cherryfy.co/api/wallet/deposit/payment" \
  -H "X-Api-Token: seu_token" \
  -H "X-Api-Secret: seu_secret" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "amount": 100.00,
    "debtor_name": "Marina Souza",
    "email": "[email protected]",
    "debtor_document_number": "12345678909",
    "phone": "11999999999",
    "method_pay": "crypto",
    "currency": "USDT",
    "postback": "https://seusite.com.br/webhooks/pix"
  }'
<?php
$base = 'https://app.cherryfy.co/api';

$ch = curl_init($base . '/wallet/deposit/payment');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Token: ' . getenv('CHERRYFY_TOKEN'),
        'X-Api-Secret: ' . getenv('CHERRYFY_SECRET'),
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount' => 100.00,
        'debtor_name' => 'Marina Souza',
        'email' => '[email protected]',
        'debtor_document_number' => '12345678909',
        'phone' => '11999999999',
        'method_pay' => 'crypto',
        'currency' => 'USDT',
        'postback' => 'https://seusite.com.br/webhooks/pix',
    ]),
]);

$resposta = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);

print_r([$http, $resposta]);
const BASE = 'https://app.cherryfy.co/api';

const resposta = await fetch(`${BASE}/wallet/deposit/payment`, {
  method: 'POST',
  headers: {
    'X-Api-Token': process.env.CHERRYFY_TOKEN,
    'X-Api-Secret': process.env.CHERRYFY_SECRET,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({
    amount: 100.00,
    debtor_name: 'Marina Souza',
    email: '[email protected]',
    debtor_document_number: '12345678909',
    phone: '11999999999',
    method_pay: 'crypto',
    currency: 'USDT',
    postback: 'https://seusite.com.br/webhooks/pix',
  }),
});

const dados = await resposta.json();
console.log(resposta.status, dados);
import os

import requests

BASE = "https://app.cherryfy.co/api"

resposta = requests.post(
    f"{BASE}/wallet/deposit/payment",
    headers={
        "X-Api-Token": os.environ["CHERRYFY_TOKEN"],
        "X-Api-Secret": os.environ["CHERRYFY_SECRET"],
        "Accept": "application/json",
    },
    json={
        "amount": 100.00,
        "debtor_name": "Marina Souza",
        "email": "[email protected]",
        "debtor_document_number": "12345678909",
        "phone": "11999999999",
        "method_pay": "crypto",
        "currency": "USDT",
        "postback": "https://seusite.com.br/webhooks/pix",
    },
    timeout=30,
)

print(resposta.status_code, resposta.json())
Resposta
{
  "idTransaction": "8d2f0c1e-4b7a-4e0d-9c3a-2f6b1d5e7a90",
  "method": "crypto",
  "currency": "USDT",
  "network": "USDT",
  "walletAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
  "cryptoAmount": "18.42000000",
  "amount": 100,
  "qrcode": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
  "qr_code_image_url": "https://exemplo.com/qr/8d2f0c1e.png",
  "message": "Envie exatamente 18.42000000 USDT (rede USDT) para o endereço. O crédito em reais cai após a confirmação na rede."
}
{
  "status": "error",
  "message": "Depósito em cripto não está disponível na adquirente desta conta."
}
{
  "status": "error",
  "message": "Nenhum adquirente configurado."
}

Saque em cripto #

O saque em cripto existe para contas em plataformas com um processador de saída em cripto habilitado. Quando está disponível, a opção aparece no painel, em Carteira › Saque.

Pelo painel

Você escolhe a rede e a moeda entre as opções oferecidas pelo processador naquele momento, informa o endereço da carteira de destino e confirma com o PIN de 6 dígitos. O pedido fica pendente até a aprovação da plataforma.

Pela API

Não disponível. POST /pixout trabalha somente com chaves Pix: um pedido com pixKeyType igual a crypto é recusado com 422 e nada é debitado. Para receber em cripto, use Receber em cripto.

As redes e moedas não são fixas: a lista vem do processador e só aparece para contas habilitadas. Por isso esta documentação não promete nenhuma rede ou moeda específica.

API de Carteira

Consultas somente de leitura. Tudo é filtrado pela conta dona da credencial: uma credencial nunca enxerga saldo nem transação de outra conta.

Consultar saldo #

GET/wallet/balance

Devolve o saldo da carteira da conta. Não tem parâmetros.

Resposta 200

CampoDescrição
statusstringSempre success.
currencystringMoeda do saldo. Sempre BRL.
balance.availablenumberSaldo disponível para saque.
balance.pending_withdrawalsnumberSoma dos saques que ainda estão pendentes.
balance.blockednumberValor bloqueado pela plataforma. Normalmente 0.
updated_atstringMomento da consulta, em ISO 8601 com fuso horário.
Requisição
curl "https://app.cherryfy.co/api/wallet/balance" \
  -H "X-Api-Token: seu_token" \
  -H "X-Api-Secret: seu_secret" \
  -H "Accept: application/json"
<?php
$base = 'https://app.cherryfy.co/api';

$ch = curl_init($base . '/wallet/balance');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Token: ' . getenv('CHERRYFY_TOKEN'),
        'X-Api-Secret: ' . getenv('CHERRYFY_SECRET'),
        'Accept: application/json',
    ],
]);

$resposta = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);

print_r([$http, $resposta]);
const BASE = 'https://app.cherryfy.co/api';

const resposta = await fetch(`${BASE}/wallet/balance`, {
  headers: {
    'X-Api-Token': process.env.CHERRYFY_TOKEN,
    'X-Api-Secret': process.env.CHERRYFY_SECRET,
    Accept: 'application/json',
  },
});

const dados = await resposta.json();
console.log(resposta.status, dados);
import os

import requests

BASE = "https://app.cherryfy.co/api"

resposta = requests.get(
    f"{BASE}/wallet/balance",
    headers={
        "X-Api-Token": os.environ["CHERRYFY_TOKEN"],
        "X-Api-Secret": os.environ["CHERRYFY_SECRET"],
        "Accept": "application/json",
    },
    timeout=30,
)

print(resposta.status_code, resposta.json())
Resposta
{
  "status": "success",
  "currency": "BRL",
  "balance": {
    "available": 1520.75,
    "pending_withdrawals": 50,
    "blocked": 0
  },
  "updated_at": "2026-09-30T12:00:00-03:00"
}
{
  "status": "error",
  "message": "Token ou Secret inválidos"
}

Consultar transação #

POST/wallet/transaction

Devolve os dados de uma transação da sua conta, seja uma cobrança (cash_in) ou um saque (cash_out). É a fonte de verdade para confirmar um aviso.

Corpo da requisição

CampoDescrição
idTransactionstringobrigatórioO idTransaction da cobrança ou o id do saque. Também aceita o valor de externalreference.

Resposta 200

CampoDescrição
transaction.typestringcash_in para cobrança, cash_out para saque.
transaction.idTransactionstringIdentificador da transação.
transaction.externalreferencestringReferência da transação no processador.
transaction.methodstringMeio usado. Normalmente pix.
transaction.statusstringSituação atual. Veja Status das transações.
transaction.amountnumberValor bruto.
transaction.net_amountnumberNa cobrança, o valor que entra no saldo. No saque, o valor enviado ao beneficiário.
transaction.feenumberTaxa cobrada da conta nesta transação.
transaction.payerobjectSó em cash_in: name e document do pagador.
transaction.beneficiaryobjectSó em cash_out: name, document, pix_key e pix_key_type.
transaction.end_to_endstring ou nullSó em cash_out: identificador da transferência, quando informado.
transaction.created_atstringCriação, em ISO 8601 com fuso horário.
transaction.updated_atstringÚltima atualização, em ISO 8601 com fuso horário.

Erros desta rota

HTTPMotivo
422O idTransaction não foi enviado.
404Não existe transação com esse identificador nesta conta. É a mesma resposta para um identificador de outra conta.
Requisição
curl -X POST \
  "https://app.cherryfy.co/api/wallet/transaction" \
  -H "X-Api-Token: seu_token" \
  -H "X-Api-Secret: seu_secret" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17"
  }'
<?php
$base = 'https://app.cherryfy.co/api';

$ch = curl_init($base . '/wallet/transaction');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Token: ' . getenv('CHERRYFY_TOKEN'),
        'X-Api-Secret: ' . getenv('CHERRYFY_SECRET'),
        'Content-Type: application/json',
        'Accept: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'idTransaction' => '7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17',
    ]),
]);

$resposta = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);

print_r([$http, $resposta]);
const BASE = 'https://app.cherryfy.co/api';

const resposta = await fetch(`${BASE}/wallet/transaction`, {
  method: 'POST',
  headers: {
    'X-Api-Token': process.env.CHERRYFY_TOKEN,
    'X-Api-Secret': process.env.CHERRYFY_SECRET,
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({
    idTransaction: '7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17',
  }),
});

const dados = await resposta.json();
console.log(resposta.status, dados);
import os

import requests

BASE = "https://app.cherryfy.co/api"

resposta = requests.post(
    f"{BASE}/wallet/transaction",
    headers={
        "X-Api-Token": os.environ["CHERRYFY_TOKEN"],
        "X-Api-Secret": os.environ["CHERRYFY_SECRET"],
        "Accept": "application/json",
    },
    json={
        "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17",
    },
    timeout=30,
)

print(resposta.status_code, resposta.json())
Resposta
{
  "status": "success",
  "transaction": {
    "type": "cash_in",
    "idTransaction": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17",
    "externalreference": "7c1f2b9e-5a3d-4e8f-9b21-3f6a0d4c8e17",
    "method": "pix",
    "status": "PAID_OUT",
    "amount": 25,
    "net_amount": 23.75,
    "fee": 1.25,
    "payer": {
      "name": "Marina Souza",
      "document": "12345678909"
    },
    "created_at": "2026-09-30T11:58:03-03:00",
    "updated_at": "2026-09-30T12:00:41-03:00"
  }
}
{
  "status": "success",
  "transaction": {
    "type": "cash_out",
    "idTransaction": "b4d2a6f0-1c7e-4f3a-8a55-9e0c2d7b6f41",
    "externalreference": "b4d2a6f0-1c7e-4f3a-8a55-9e0c2d7b6f41",
    "method": "pix",
    "status": "PENDING",
    "amount": 50,
    "net_amount": 50,
    "fee": 1.5,
    "beneficiary": {
      "name": "Sua Empresa LTDA",
      "document": "12345678909",
      "pix_key": "12345678909",
      "pix_key_type": "CPF"
    },
    "end_to_end": null,
    "created_at": "2026-09-30T12:00:00-03:00",
    "updated_at": "2026-09-30T12:00:00-03:00"
  }
}
{
  "status": "error",
  "message": "Informe o idTransaction da transação."
}
{
  "status": "error",
  "message": "Transação não encontrada nesta conta."
}

Status das transações #

Os valores abaixo aparecem no campo status da consulta de status e da consulta da transação.

Cobranças (cash_in)

StatusSignificadoO que fazer
WAITING_FOR_APPROVALCobrança criada, aguardando o pagamento.Aguardar.
PAID_OUTPaga. O valor líquido entrou no saldo.Liberar o pedido.
CANCELLEDCancelada ou não paga.Não liberar. Gere outra cobrança, se for o caso.
REFUNDEDEstornada depois de paga. O valor saiu do saldo.Desfazer a liberação.
MEDIATIONEm mediação. O valor sai do saldo disponível até a decisão.Acompanhar pelo painel.

Saques (cash_out)

StatusSignificadoO que fazer
PENDINGPedido registrado: enviado ao processador (API) ou aguardando aprovação (painel).Aguardar o aviso ou consultar de novo.
PAID_OUTPix enviado. Registros antigos podem aparecer como COMPLETED.Dar o saque como concluído.
REJECTEDNão enviado. O valor voltou para o saldo. Registros antigos podem aparecer como CANCELLED.Conferir a chave Pix e pedir de novo.
PENDING_APPROVALSó em registros antigos. Hoje o saque do painel que espera aprovação aparece como PENDING.Aguardar.

Trate qualquer valor que você não conheça como "ainda não concluído" e não libere nada com base nele.

Checklist de produção #

Antes de colocar a integração no ar, confira cada item. As marcações ficam salvas só neste navegador.