Skip to main content
Este guia mostra como enviar transferências PIX de forma detalhada, com exemplos práticos e todas as opções disponíveis para transferências instantâneas.

Endpoint

POST /api/v1/pix/out/pixkey

Realiza transferência PIX instantânea para uma chave PIX específica. URL Completa:
  • Produção: https://api.vexybank.com/api/v1/pix/out/pixkey

Headers obrigatórios

Content-Type: application/json
Authorization: Bearer <seu_token_bearer>
x-idempotency-key: <chave_unica_para_evitar_duplicacao>
Crítico: Sempre use o header x-idempotency-key com um identificador único para evitar transferências duplicadas.

Parâmetros do corpo

Campos Obrigatórios

Tipo: string
Descrição: Chave PIX de destino
Formatos aceitos: CPF, CNPJ, email, telefone ou chave aleatória
// Exemplos válidos
{
  "pixKey": "11122233344"                           // CPF
  "pixKey": "11222333000144"                        // CNPJ
  "pixKey": "[email protected]"                   // Email
  "pixKey": "+5511912345678"                        // Telefone
  "pixKey": "abc12345-de67-89fg-hijk-lmnopqrstuv0" // Chave aleatória
}
Tipo: number
Descrição: Valor em centavos
Mínimo: 1 centavo (R0,01)Maˊximo:1.000.000.000centavos(R 0,01) **Máximo**: 1.000.000.000 centavos (R 10.000.000,00)
{
  "amount": 150000  // R$ 1.500,00
}

Campos opcionais

Tipo: string
Descrição: Moeda da transferência
Padrão: "BRL"
Valores aceitos: Apenas "BRL"
{
  "currency": "BRL"
}
Tipo: string
Descrição: Descrição da transferência
Limite: 140 caracteres
Padrão: Vazio
{
  "description": "Pagamento de fornecedor - NF 12345"
}
Tipo: string
Descrição: URL para receber notificações de status da transferência
Formato: URL válida com protocolo HTTPS
Padrão: Vazio
{
  "postbackUrl": "https://exemplo.com.br/webhook/pix-out"
}

Exemplos práticos

Exemplo Básico - CPF

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": "11122233344",
    "amount": 10000,
    "currency": "BRL",
    "description": "Transfer PIX Exemplo",
    "postbackUrl": "https://exemplo.com.br/webhook/pix-out"
  }'

Exemplo Email - Fornecedor

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_002' \
  --data '{
    "pixKey": "[email protected]",
    "amount": 20000,
    "currency": "BRL",
    "description": "Pagamento fornecedor - NF Exemplo",
    "postbackUrl": "https://exemplo.com.br/webhook/pix-out"
  }'

Exemplo CNPJ - B2B

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_003' \
  --data '{
    "pixKey": "11222333000144",
    "amount": 50000,
    "currency": "BRL",
    "description": "Pagamento parcela contrato Exemplo",
    "postbackUrl": "https://exemplo.com.br/webhook/pix-out"
  }'

Exemplo Telefone - Afiliado

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_004' \
  --data '{
    "pixKey": "+5511912345678",
    "amount": 15000,
    "currency": "BRL",
    "description": "Comissão Exemplo - Afiliado",
    "postbackUrl": "https://exemplo.com.br/webhook/pix-out"
  }'

Resposta de sucesso (200)

{
  "success": true,
  "message": "Transferência criada com sucesso",
  "data": {
    "id": "transfer_abc123def456",
    "status": "queued",
    "amount": 10000,
    "netAmount": 10000,
    "fees": 0,
    "pixKey": "11122233344"
  }
}

Campos da Resposta

CampoDescrição
idID único da transferência
statusStatus atual da transferência (queued, processing, completed, canceled)
amountValor total da transferência
netAmountValor líquido após dedução de taxas
feesTaxa cobrada na transferência
pixKeyChave PIX de destino

Acompanhamento de status

Via Webhook (Recomendado)

Configure um webhook via postbackUrl para receber atualizações automáticas:
{
  "id": "transfer_abc123def456",
  "type": "transfer",
  "event": "transfer_completed",
  "scope": "postback",
  "transfer": {
    "id": "transfer_abc123def456",
    "amount": 10000,
    "status": "completed",
    "pix": {
      "endToEndId": "E00000000202401011200000000000000",
      "creditorAccount": null
    }
  }
}

Eventos Webhook Disponíveis

Os seguintes eventos serão enviados para o postbackUrl configurado:
EventoDescrição
transfer_createdTransferência criada e validada
transfer_updatedStatus da transferência foi atualizado
transfer_completedTransferência concluída com sucesso
transfer_canceledTransferência foi cancelada

Possíveis Status da Transferência

StatusDescrição
queuedTransferência em fila para processamento
processingTransferência sendo processada
completedTransferência concluída com sucesso
canceledTransferência foi cancelada

Via Polling (Alternativo)

# Consultar status da transferência
curl -X GET 'https://api.vexybank.com/api/v1/transfers/transfer_abc123def456' \
  -H 'Authorization: Bearer SEU_TOKEN_AQUI'

Implementações por linguagem

JavaScript/Node.js

class PixTransfer {
  constructor(auth) {
    this.auth = auth
  }

  async sendToPixKey(pixKey, amount, description = '', postbackUrl = '', idempotencyKey = null) {
    if (!idempotencyKey) {
      idempotencyKey = this.generateIdempotencyKey(pixKey, amount)
    }

    const transferData = {
      pixKey,
      amount,
      currency: 'BRL',
      description,
      postbackUrl
    }

    try {
      const response = await this.auth.makeAuthenticatedRequest(
        'POST',
        '/api/v1/pix/out/pixkey',
        transferData,
        { 'x-idempotency-key': idempotencyKey }
      )

      return response.data.data
    } catch (error) {
      throw new Error(`Erro na transferência: ${error.message}`)
    }
  }

  generateIdempotencyKey(pixKey, amount) {
    const timestamp = Date.now()
    const hash = require('crypto')
      .createHash('md5')
      .update(`${pixKey}_${amount}_${timestamp}`)
      .digest('hex')
    return `transfer_${hash.substring(0, 16)}`
  }

  async waitForCompletion(transferId, timeoutMs = 60000) {
    const startTime = Date.now()
    const checkInterval = 3000 // 3 segundos

    while (Date.now() - startTime < timeoutMs) {
      const transfer = await this.getTransfer(transferId)
      
      if (transfer.status === 'completed') {
        return transfer
      } else if (transfer.status === 'refused' || transfer.status === 'canceled') {
        throw new Error(`Transferência ${transfer.status}: ${transfer.reason}`)
      }

      await new Promise(resolve => setTimeout(resolve, checkInterval))
    }

    throw new Error('Timeout: transferência não foi concluída no tempo esperado')
  }

  async getTransfer(transferId) {
    const response = await this.auth.makeAuthenticatedRequest(
      'GET',
      `/api/v1/transfers/${transferId}`
    )
    return response.data.data
  }
}

// Uso
const pixTransfer = new PixTransfer(authInstance)

try {
  const transfer = await pixTransfer.sendToPixKey(
    '[email protected]',
    20000,
    'Pagamento NF Exemplo',
    'https://exemplo.com.br/webhook/pix-out'
  )

  console.log('Transferência iniciada:', transfer.id)

  // Aguardar conclusão via webhook ou polling
  const completedTransfer = await pixTransfer.waitForCompletion(transfer.id)
  console.log('Transferência concluída:', completedTransfer.status)
} catch (error) {
  console.error('Erro:', error.message)
}

Python

import time
import hashlib
from datetime import datetime

class PixTransfer:
    def __init__(self, auth_instance):
        self.auth = auth_instance

    def send_to_pix_key(self, pix_key, amount, description='', postback_url='', idempotency_key=None):
        if not idempotency_key:
            idempotency_key = self.generate_idempotency_key(pix_key, amount)

        transfer_data = {
            'pixKey': pix_key,
            'amount': amount,
            'currency': 'BRL',
            'description': description,
            'postbackUrl': postback_url
        }

        headers = {'x-idempotency-key': idempotency_key}

        response = self.auth.make_authenticated_request(
            'POST',
            '/api/v1/pix/out/pixkey',
            transfer_data,
            additional_headers=headers
        )

        if response.status_code == 200:
            return response.json()['data']
        else:
            raise Exception(f"Erro na transferência: {response.text}")

    def generate_idempotency_key(self, pix_key, amount):
        timestamp = str(int(time.time() * 1000))
        data = f"{pix_key}_{amount}_{timestamp}"
        hash_obj = hashlib.md5(data.encode())
        return f"transfer_{hash_obj.hexdigest()[:16]}"

    def wait_for_completion(self, transfer_id, timeout_seconds=60):
        start_time = time.time()
        check_interval = 3  # 3 segundos

        while time.time() - start_time < timeout_seconds:
            transfer = self.get_transfer(transfer_id)
            
            if transfer['status'] == 'completed':
                return transfer
            elif transfer['status'] in ['refused', 'canceled']:
                raise Exception(f"Transferência {transfer['status']}: {transfer.get('reason', 'Motivo não informado')}")

            time.sleep(check_interval)

        raise Exception('Timeout: transferência não foi concluída no tempo esperado')

    def get_transfer(self, transfer_id):
        response = self.auth.make_authenticated_request(
            'GET',
            f'/api/v1/transfers/{transfer_id}'
        )
        return response.json()['data']

# Uso
pix_transfer = PixTransfer(auth_instance)

try:
    transfer = pix_transfer.send_to_pix_key(
        '[email protected]',
        20000,
        'Pagamento NF Exemplo',
        'https://exemplo.com.br/webhook/pix-out'
    )

    print(f'Transferência iniciada: {transfer["id"]}')

    # Aguardar conclusão via webhook ou polling
    completed_transfer = pix_transfer.wait_for_completion(transfer['id'])
    print(f'Transferência concluída: {completed_transfer["status"]}')
except Exception as error:
    print(f'Erro: {error}')

PHP

<?php

class PixTransfer {
    private $auth;

    public function __construct($authInstance) {
        $this->auth = $authInstance;
    }

    public function sendToPixKey($pixKey, $amount, $description = '', $postbackUrl = '', $idempotencyKey = null) {
        if (!$idempotencyKey) {
            $idempotencyKey = $this->generateIdempotencyKey($pixKey, $amount);
        }

        $transferData = [
            'pixKey' => $pixKey,
            'amount' => $amount,
            'currency' => 'BRL',
            'description' => $description,
            'postbackUrl' => $postbackUrl
        ];

        $headers = ['x-idempotency-key' => $idempotencyKey];

        $response = $this->auth->makeAuthenticatedRequest(
            'POST',
            '/api/v1/pix/out/pixkey',
            $transferData,
            $headers
        );

        $data = json_decode($response, true);

        if ($data['success']) {
            return $data['data'];
        } else {
            throw new Exception("Erro na transferência: " . $response);
        }
    }

    private function generateIdempotencyKey($pixKey, $amount) {
        $timestamp = round(microtime(true) * 1000);
        $data = "{$pixKey}_{$amount}_{$timestamp}";
        $hash = md5($data);
        return "transfer_" . substr($hash, 0, 16);
    }

    public function waitForCompletion($transferId, $timeoutSeconds = 60) {
        $startTime = time();
        $checkInterval = 3; // 3 segundos

        while (time() - $startTime < $timeoutSeconds) {
            $transfer = $this->getTransfer($transferId);
            
            if ($transfer['status'] === 'completed') {
                return $transfer;
            } elseif (in_array($transfer['status'], ['refused', 'canceled'])) {
                $reason = $transfer['reason'] ?? 'Motivo não informado';
                throw new Exception("Transferência {$transfer['status']}: {$reason}");
            }

            sleep($checkInterval);
        }

        throw new Exception('Timeout: transferência não foi concluída no tempo esperado');
    }

    public function getTransfer($transferId) {
        $response = $this->auth->makeAuthenticatedRequest(
            'GET',
            "/api/v1/transfers/{$transferId}"
        );

        $data = json_decode($response, true);
        return $data['data'];
    }
}

// Uso
$pixTransfer = new PixTransfer($authInstance);

try {
    $transfer = $pixTransfer->sendToPixKey(
        '[email protected]',
        20000,
        'Pagamento NF Exemplo',
        'https://exemplo.com.br/webhook/pix-out'
    );

    echo "Transferência iniciada: " . $transfer['id'] . "\n";

    // Aguardar conclusão via webhook ou polling
    $completedTransfer = $pixTransfer->waitForCompletion($transfer['id']);
    echo "Transferência concluída: " . $completedTransfer['status'] . "\n";
} catch (Exception $error) {
    echo "Erro: " . $error->getMessage() . "\n";
}
?>

Chaves de idempotência

Estratégias Recomendadas

Ideal para pagamentos programados ou recorrentes:
function gerarPorReferencia(referencia, data = null) {
  const dataFormatada = data || new Date().toISOString().split('T')[0]
  return `transfer_${referencia}_${dataFormatada}`
}

// Exemplo: transfer_supplier_123_2024-01-20
Para transferências únicas sem referência específica:
const { v4: uuidv4 } = require('uuid')

function gerarUUID() {
  return `transfer_${uuidv4()}`
}

// Exemplo: transfer_123e4567-e89b-12d3-a456-426614174000
Baseado nos dados da transferência:
const crypto = require('crypto')

function gerarPorDados(pixKey, amount, description) {
  const data = `${pixKey}_${amount}_${description}_${Date.now()}`
  const hash = crypto.createHash('sha256').update(data).digest('hex')
  return `transfer_${hash.substring(0, 16)}`
}

❌ Tratamento de Erros

Códigos de Erro Específicos

CódigoErroDescriçãoSolução
400INVALID_PIX_KEYChave PIX inválidaValidar formato da chave
400INVALID_AMOUNTValor inválidoVerificar limites min/max
402INSUFFICIENT_BALANCESaldo insuficienteVerificar saldo disponível
409DUPLICATE_IDEMPOTENCY_KEYChave duplicadaUsar nova chave única
422DAILY_LIMIT_EXCEEDEDLimite diário excedidoAguardar próximo dia
422RECIPIENT_UNAVAILABLEDestinatário indisponívelVerificar chave PIX

Exemplo de Tratamento Completo

async function enviarComTratamentoCompleto(dados) {
  const maxRetries = 3
  let attempt = 0

  while (attempt < maxRetries) {
    try {
      return await pixTransfer.sendToPixKey(
        dados.pixKey,
        dados.amount,
        dados.description
      )
    } catch (error) {
      attempt++
      
      switch (error.code) {
        case 'INSUFFICIENT_BALANCE':
          throw new Error('Saldo insuficiente. Verifique seu saldo e tente novamente.')
        
        case 'INVALID_PIX_KEY':
          throw new Error('Chave PIX inválida. Verifique o formato da chave.')
        
        case 'DAILY_LIMIT_EXCEEDED':
          throw new Error('Limite diário de transferências excedido.')
        
        case 'RECIPIENT_UNAVAILABLE':
          throw new Error('Destinatário temporariamente indisponível. Tente novamente mais tarde.')
        
        case 'RATE_LIMIT':
          if (attempt < maxRetries) {
            // Aguardar antes de tentar novamente
            await new Promise(resolve => setTimeout(resolve, 1000 * attempt))
            continue
          }
          throw new Error('Muitas tentativas. Aguarde alguns minutos e tente novamente.')
        
        default:
          if (attempt < maxRetries) {
            await new Promise(resolve => setTimeout(resolve, 1000 * attempt))
            continue
          }
          throw new Error('Erro interno. Entre em contato com o suporte.')
      }
    }
  }
}

Validações recomendadas

Validação Completa Antes do Envio

function validarDadosTransferencia(dadosTransferencia) {
  const { pixKey, amount, description } = dadosTransferencia
  const erros = []

  // Validar chave PIX
  if (!pixKey || !validarChavePIX(pixKey)) {
    erros.push('Chave PIX inválida')
  }

  // Validar valor
  if (!amount || amount < 1) {
    erros.push('Valor deve ser maior que R$ 0,01')
  }

  if (amount > 1000000000) {
    erros.push('Valor máximo é R$ 10.000.000,00')
  }

  // Validar descrição
  if (description && description.length > 140) {
    erros.push('Descrição deve ter no máximo 140 caracteres')
  }

  if (erros.length > 0) {
    throw new Error('Dados inválidos: ' + erros.join(', '))
  }

  return true
}

Simulação Antes do Envio

async function simularTransferencia(dados) {
  // Verificar se seria bem-sucedida sem executar
  return {
    pixKey: dados.pixKey,
    amount: dados.amount,
    fees: 300, // R$ 3,00
    netAmount: dados.amount - 300,
    estimatedTime: '10 segundos',
    valid: true
  }
}

Próximos passos