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.quantumpay.com.br/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
pixKey
pixKey
Tipo:
Descrição: Chave PIX de destino
Formatos aceitos: CPF, CNPJ, email, telefone ou chave aleatória
stringDescriçã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": "contato@exemplo.com" // Email
"pixKey": "+5511912345678" // Telefone
"pixKey": "abc12345-de67-89fg-hijk-lmnopqrstuv0" // Chave aleatória
}
amount
amount
Tipo:
Descrição: Valor em centavos
Mínimo: 1 centavo (R0,01)∗∗Maˊximo∗∗:1.000.000.000centavos(R 10.000.000,00)
numberDescrição: Valor em centavos
Mínimo: 1 centavo (R0,01)∗∗Maˊximo∗∗:1.000.000.000centavos(R 10.000.000,00)
{
"amount": 150000 // R$ 1.500,00
}
Campos opcionais
currency
currency
Tipo:
Descrição: Moeda da transferência
Padrão:
Valores aceitos: Apenas
stringDescrição: Moeda da transferência
Padrão:
"BRL"Valores aceitos: Apenas
"BRL"{
"currency": "BRL"
}
description
description
Tipo:
Descrição: Descrição da transferência
Limite: 140 caracteres
Padrão: Vazio
stringDescrição: Descrição da transferência
Limite: 140 caracteres
Padrão: Vazio
{
"description": "Pagamento de fornecedor - NF 12345"
}
postbackUrl
postbackUrl
Tipo:
Descrição: URL para receber notificações de status da transferência
Formato: URL válida com protocolo HTTPS
Padrão: Vazio
stringDescriçã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.quantumpay.com.br/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.quantumpay.com.br/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": "fornecedor@exemplo.com",
"amount": 20000,
"currency": "BRL",
"description": "Pagamento fornecedor - NF Exemplo",
"postbackUrl": "https://exemplo.com.br/webhook/pix-out"
}'
Exemplo CNPJ - B2B
curl --location 'https://api.quantumpay.com.br/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.quantumpay.com.br/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
| Campo | Descrição |
|---|---|
id | ID único da transferência |
status | Status atual da transferência (queued, processing, completed, canceled) |
amount | Valor total da transferência |
netAmount | Valor líquido após dedução de taxas |
fees | Taxa cobrada na transferência |
pixKey | Chave PIX de destino |
Acompanhamento de status
Via Webhook (Recomendado)
Configure um webhook viapostbackUrl 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 opostbackUrl configurado:
| Evento | Descrição |
|---|---|
transfer_created | Transferência criada e validada |
transfer_updated | Status da transferência foi atualizado |
transfer_completed | Transferência concluída com sucesso |
transfer_canceled | Transferência foi cancelada |
Possíveis Status da Transferência
| Status | Descrição |
|---|---|
queued | Transferência em fila para processamento |
processing | Transferência sendo processada |
completed | Transferência concluída com sucesso |
canceled | Transferência foi cancelada |
Via Polling (Alternativo)
# Consultar status da transferência
curl -X GET 'https://api.quantumpay.com.br/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(
'fornecedor@exemplo.com',
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(
'fornecedor@exemplo.com',
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(
'fornecedor@exemplo.com',
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
Por data + referência
Por data + referência
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
UUID + timestamp
UUID + timestamp
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
Hash dos dados
Hash dos dados
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ódigo | Erro | Descrição | Solução |
|---|---|---|---|
400 | INVALID_PIX_KEY | Chave PIX inválida | Validar formato da chave |
400 | INVALID_AMOUNT | Valor inválido | Verificar limites min/max |
402 | INSUFFICIENT_BALANCE | Saldo insuficiente | Verificar saldo disponível |
409 | DUPLICATE_IDEMPOTENCY_KEY | Chave duplicada | Usar nova chave única |
422 | DAILY_LIMIT_EXCEEDED | Limite diário excedido | Aguardar próximo dia |
422 | RECIPIENT_UNAVAILABLE | Destinatário indisponível | Verificar 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
Configurar webhooks
Configure notificações automáticas de transferências
PIX IN
Aprenda a receber pagamentos PIX
Status e códigos
Entenda os status e códigos de erro
Segurança
Boas práticas de segurança para transferências