Skip to main content
O PIX OUT permite enviar transferências instantâneas para qualquer chave PIX de forma programática. Ideal para pagamentos a fornecedores, transferências para afiliados, estornos e qualquer situação onde você precisa enviar dinheiro.

Como funciona

O PIX OUT funciona através do envio direto para chaves PIX, sem necessidade de dados bancários complexos:

Chave PIX

Use qualquer tipo de chave: CPF, CNPJ, email, telefone ou aleatória

Transferência instantânea

Dinheiro é transferido instantaneamente, 24/7

Segurança

Validação automática de chaves e dados

Confirmação por webhook

Receba confirmação via webhook em tempo real

Implementação rápida

1. Enviar Transferência PIX

curl --location 'https://api.vexybank.com/api/v1/pix/out/pixkey' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer SEU_TOKEN_AQUI' \
  --header 'x-idempotency-key: idempotency_exemplo_001' \
  --data '{
    "pixKey": "[email protected]",
    "amount": 20000,
    "currency": "BRL",
    "description": "Transfer PIX Exemplo",
    "postbackUrl": "https://exemplo.com.br/webhook/pix-out"
  }'

2. Resposta de sucesso

{
  "success": true,
  "message": "Transferência criada com sucesso",
  "data": {
    "id": "transfer_abc123def456",
    "status": "queued",
    "amount": 20000,
    "netAmount": 20000,
    "fees": 0,
    "pixKey": "[email protected]"
  }
}

3. Confirmação via Webhook

{
  "id": "transfer_abc123def456",
  "type": "transfer",
  "event": "transfer_completed",
  "scope": "postback",
  "transfer": {
    "id": "transfer_abc123def456",
    "amount": 20000,
    "status": "completed",
    "pix": {
      "endToEndId": "E00000000202401011200000000000000",
      "creditorAccount": null
    }
  }
}

Tipos de chave PIX suportados

CPF e CNPJ

# CPF (apenas números)
"pixKey": "11122233344"

# CNPJ (apenas números)  
"pixKey": "11222333000144"

Email

# Email válido
"pixKey": "[email protected]"
"pixKey": "[email protected]"

Telefone

# Formato: +5511999999999
"pixKey": "+5511987654321"
"pixKey": "+5521988888888"

Chave Aleatória (EVP)

# UUID gerado pelo banco
"pixKey": "123e4567-e89b-12d3-a456-426614174000"
"pixKey": "a1b2c3d4-e5f6-7890-1234-567890abcdef"

Parâmetros detalhados

Campos Obrigatórios

CampoTipoDescrição
pixKeystringChave PIX de destino
amountnumberValor em centavos

Campos Opcionais

CampoTipoDescriçãoPadrão
currencystringMoeda da transferência"BRL"
descriptionstringDescrição da transferência (máx. 140 chars)""
postbackUrlstringURL para receber notificações de status""

Headers Importantes

HeaderObrigatórioDescrição
AuthorizationSimToken Bearer
Content-TypeSimapplication/json
x-idempotency-keyRecomendadoChave única para evitar duplicação
Importante: Use sempre o header x-idempotency-key para evitar transferências duplicadas acidentais.

Taxas e limites

Estrutura de Taxas

  • Taxa por transferência: R$ 3,00
  • Sem taxa mínima: Transferências a partir de R$ 0,01
  • Sem mensalidade: Pague apenas pelo que usar

Limites Operacionais

PeríodoLimite IndividualLimite Diário
Por transferênciaR$ 10.000.000,00-
Por dia-R$ 50.000.000,00
Quantidade/dia-1.000 transferências
Limites podem ser aumentados mediante análise e aprovação. Entre em contato com nosso suporte comercial.

Status das transferências

StatusDescriçãoTempo EsperadoPróximo Passo
queuedTransferência em fila para processamentoImediatoAguardar processamento
processingEm processamento pelo sistema1-3 segundosAguardar conclusão
completedTransferência concluída3-10 segundosTransferência realizada
canceledCancelada pelo sistemaImediatoVerificar motivo

Fluxo de implementação

1. Validar Dados

function validarTransferencia(pixKey, amount, description) {
  // Validar valor mínimo
  if (amount < 1) {
    throw new Error('Valor mínimo é R$ 0,01')
  }
  
  // Validar valor máximo
  if (amount > 1000000000) {
    throw new Error('Valor máximo é R$ 10.000.000,00')
  }
  
  // Validar chave PIX
  if (!pixKey || pixKey.trim().length === 0) {
    throw new Error('Chave PIX é obrigatória')
  }
  
  // Validar descrição
  if (description && description.length > 140) {
    throw new Error('Descrição deve ter no máximo 140 caracteres')
  }
  
  return true
}

2. Gerar Chave de Idempotência

function gerarIdempotencyKey(dados) {
  // Usar dados únicos da transferência
  const uniqueString = `${dados.pixKey}_${dados.amount}_${Date.now()}`
  
  // Ou usar UUID
  const uuid = require('uuid')
  return `transfer_${uuid.v4()}`
  
  // Ou usar referência interna
  return `transfer_${dados.referenceId}_${new Date().toISOString().split('T')[0]}`
}

3. Enviar Transferência

async function enviarTransferencia(dadosTransferencia) {
  const idempotencyKey = gerarIdempotencyKey(dadosTransferencia)
  
  try {
    const response = await fetch('/api/v1/pix/out/pixkey', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
        'X-Idempotency-Key': idempotencyKey
      },
      body: JSON.stringify(dadosTransferencia)
    })

    const result = await response.json()
    
    if (result.success) {
      return result.data
    } else {
      throw new Error(result.error.message)
    }
  } catch (error) {
    console.error('Erro na transferência:', error)
    throw error
  }
}

4. Acompanhar Status

async function acompanharTransferencia(transferId) {
  const maxTentativas = 20 // 1 minuto total
  let tentativas = 0
  
  return new Promise((resolve, reject) => {
    const interval = setInterval(async () => {
      try {
        const response = await fetch(`/api/v1/transfers/${transferId}`, {
          headers: { 'Authorization': `Bearer ${token}` }
        })
        
        const data = await response.json()
        
        if (data.status === 'completed') {
          clearInterval(interval)
          resolve(data)
        } else if (data.status === 'refused' || data.status === 'canceled') {
          clearInterval(interval)
          reject(new Error(`Transferência ${data.status}: ${data.reason}`))
        } else if (tentativas >= maxTentativas) {
          clearInterval(interval)
          reject(new Error('Timeout: transferência não foi concluída'))
        }
        
        tentativas++
      } catch (error) {
        clearInterval(interval)
        reject(error)
      }
    }, 3000) // Verifica a cada 3 segundos
  })
}

Segurança e boas práticas

Validação de Chaves PIX

function validarChavePIX(chave) {
  // Remover espaços e caracteres especiais
  chave = chave.trim()
  
  // CPF (11 dígitos)
  if (/^\d{11}$/.test(chave)) {
    return validarCPF(chave)
  }
  
  // CNPJ (14 dígitos)
  if (/^\d{14}$/.test(chave)) {
    return validarCNPJ(chave)
  }
  
  // Email
  if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(chave)) {
    return true
  }
  
  // Telefone (+5511999999999)
  if (/^\+55\d{10,11}$/.test(chave)) {
    return true
  }
  
  // Chave aleatória (UUID)
  if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(chave)) {
    return true
  }
  
  return false
}

Controle de Concorrência

// Cache para evitar transferências duplicadas
const transferenciasEmAndamento = new Map()

async function enviarComControle(dados) {
  const chaveControle = `${dados.pixKey}_${dados.amount}`
  
  if (transferenciasEmAndamento.has(chaveControle)) {
    throw new Error('Transferência similar já em andamento')
  }
  
  try {
    transferenciasEmAndamento.set(chaveControle, true)
    const resultado = await enviarTransferencia(dados)
    return resultado
  } finally {
    transferenciasEmAndamento.delete(chaveControle)
  }
}

Auditoria e Logs

function logarTransferencia(transferencia, status, detalhes = {}) {
  const logEntry = {
    timestamp: new Date().toISOString(),
    transferId: transferencia.id,
    pixKey: transferencia.pixKey,
    amount: transferencia.amount,
    status: status,
    userId: transferencia.userId,
    ...detalhes
  }
  
  // Salvar no sistema de auditoria
  console.log('Transfer Log:', JSON.stringify(logEntry))
  
  // Enviar para sistema de monitoramento
  if (status === 'refused' || status === 'canceled') {
    alertSystem.notify('transfer_failed', logEntry)
  }
}

Tratamento de erros

Códigos de Erro Comuns

CódigoErroCausaSolução
400Chave PIX inválidaFormato incorretoValidar formato da chave
402Saldo insuficienteNão há saldo na contaVerificar saldo disponível
422Valor inválidoFora dos limitesVerificar limites operacionais
429Rate limitMuitas requisiçõesImplementar retry com delay

Exemplo de Tratamento

async function enviarComTratamento(dados) {
  try {
    return await enviarTransferencia(dados)
  } catch (error) {
    switch (error.status) {
      case 400:
        throw new Error('Verifique os dados da transferência')
      case 402:
        throw new Error('Saldo insuficiente para realizar a transferência')
      case 422:
        throw new Error('Valor fora dos limites permitidos')
      case 429:
        // Aguardar e tentar novamente
        await new Promise(resolve => setTimeout(resolve, 1000))
        return await enviarTransferencia(dados)
      default:
        throw new Error('Erro interno. Tente novamente em alguns minutos.')
    }
  }
}

Casos de uso comuns

Pagamento a Fornecedores

async function pagarFornecedor(fornecedor, valorNota, numeroNF) {
  const transferencia = {
    pixKey: fornecedor.chavePix,
    amount: valorNota,
    description: `Pagamento NF ${numeroNF} - ${fornecedor.nome}`
  }
  
  return await enviarTransferencia(transferencia)
}

Transferência para Afiliados

async function pagarAfiliado(afiliado, comissao, periodo) {
  const transferencia = {
    pixKey: afiliado.chavePix,
    amount: comissao,
    description: `Comissão ${periodo} - Afiliado ${afiliado.id}`
  }
  
  return await enviarTransferencia(transferencia)
}

Estorno para Cliente

async function estornarCliente(cliente, valorEstorno, motivo) {
  const transferencia = {
    pixKey: cliente.chavePix,
    amount: valorEstorno,
    description: `Estorno: ${motivo}`
  }
  
  return await enviarTransferencia(transferencia)
}

Próximos passos