(
'https://api.public.firebanking.com.br/api/pix/cash-in',
payload,
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
console.log('Cobrança PIX gerada com sucesso!');
console.log(`ID da Transação: ${response.data.transactionId}`);
console.log(`Código PIX: ${response.data.pixCode}`);
console.log(`Expira em: ${new Date(response.data.expirationDate).toLocaleString('pt-BR')}`);
if (response.data.qrCodeImage) {
console.log('QR Code Image disponível para exibição');
}
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
console.error('Erro ao gerar cobrança:', error.response?.data);
throw new Error(error.response?.data?.message || 'Erro ao gerar cobrança PIX');
}
throw error;
}
}
// Uso
const token = 'seu_token_aqui';
createPixCharge(token, '12345', 150.00, 'Carlos Oliveira', '12345678901');
```
### Python
```python theme={null}
import requests
from datetime import datetime, timedelta
from typing import Dict, Optional
def create_pix_charge(
token: str,
order_id: str,
amount: float,
customer_name: str,
customer_document: str,
expiration_hours: int = 1,
additional_info: Optional[Dict[str, str]] = None
) -> Dict:
"""
Gera uma cobrança PIX
Args:
token: Token Bearer válido
order_id: ID do pedido
amount: Valor em reais
customer_name: Nome do cliente
customer_document: CPF ou CNPJ (apenas números)
expiration_hours: Horas até expiração (padrão: 1)
additional_info: Informações adicionais
Returns:
Dados da cobrança gerada
"""
url = 'https://api.public.firebanking.com.br/api/pix/cash-in'
payload = {
'transaction': {
'value': round(amount, 2),
'description': f'Pagamento do pedido {order_id}',
'expirationTime': expiration_hours * 3600,
'externalId': f'ORDER-{order_id}-{int(datetime.now().timestamp())}',
'generateQrCode': True
},
'payer': {
'fullName': customer_name,
'document': customer_document
},
'additionalInfo': additional_info or {}
}
headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
try:
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
data = response.json()
print('Cobrança PIX gerada com sucesso!')
print(f"ID da Transação: {data['transactionId']}")
print(f"Código PIX: {data['pixCode']}")
print(f"Status: {data['status']}")
expiration = datetime.fromisoformat(data['expirationDate'].replace('Z', '+00:00'))
print(f"Expira em: {expiration.strftime('%d/%m/%Y %H:%M:%S')}")
if 'qrCodeImage' in data:
print('QR Code Image disponível para exibição')
return data
except requests.exceptions.RequestException as e:
print(f'Erro ao gerar cobrança: {e}')
if hasattr(e.response, 'json'):
print(f'Detalhes: {e.response.json()}')
raise
# Uso
token = 'seu_token_aqui'
charge = create_pix_charge(
token=token,
order_id='12345',
amount=150.00,
customer_name='Carlos Oliveira',
customer_document='12345678901',
expiration_hours=24,
additional_info={
'storeName': 'Tech Solutions',
'productCategory': 'Eletrônicos'
}
)
```
### PHP
```php theme={null}
[
'value' => round($amount, 2),
'description' => "Pagamento do pedido $orderId",
'expirationTime' => $expirationHours * 3600,
'externalId' => "ORDER-$orderId-" . time(),
'generateQrCode' => true
],
'payer' => [
'fullName' => $customerName,
'document' => $customerDocument
],
'additionalInfo' => [
'orderId' => $orderId
]
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $token,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 201) {
throw new Exception("Erro ao gerar cobrança: HTTP $httpCode - $response");
}
$data = json_decode($response, true);
echo "Cobrança PIX gerada com sucesso!" . PHP_EOL;
echo "ID da Transação: {$data['transactionId']}" . PHP_EOL;
echo "Código PIX: {$data['pixCode']}" . PHP_EOL;
echo "Status: {$data['status']}" . PHP_EOL;
if (isset($data['qrCodeImage'])) {
echo "QR Code Image disponível para exibição" . PHP_EOL;
}
return $data;
}
// Uso
$token = 'seu_token_aqui';
$charge = createPixCharge(
$token,
'12345',
150.00,
'Carlos Oliveira',
'12345678901',
24
);
```
## Casos de Uso
### 1. E-commerce - Checkout com PIX
```javascript theme={null}
// Integração em checkout de e-commerce
class PixCheckout {
constructor(token) {
this.token = token;
}
async generatePayment(order) {
const charge = await createPixCharge(
this.token,
order.id,
order.total,
order.customer.name,
order.customer.document
);
// Exibir QR Code na página (usando imagem da API ou gerando localmente)
this.displayQrCode(charge);
// Iniciar polling para verificar pagamento
this.startPaymentPolling(charge.transactionId);
return charge;
}
displayQrCode(charge) {
const qrCanvas = document.getElementById('qr-canvas');
const qrImage = document.getElementById('qr-image');
// Usar imagem Base64 da API (preferível - evita processamento no cliente)
if (charge.qrCodeImage) {
qrImage.src = charge.qrCodeImage;
qrImage.style.display = 'block';
qrCanvas.style.display = 'none';
} else {
// Fallback: gerar QR Code localmente usando biblioteca (ex: qrcode.js)
QRCode.toCanvas(qrCanvas, charge.pixCode, {
width: 300,
margin: 2
});
qrCanvas.style.display = 'block';
qrImage.style.display = 'none';
}
// Mostrar também o código Pix Copia e Cola
document.getElementById('pix-code').textContent = charge.pixCode;
}
startPaymentPolling(transactionId) {
// Verificar status a cada 3 segundos
const interval = setInterval(async () => {
const status = await this.checkPaymentStatus(transactionId);
if (status === 'CONFIRMED') {
clearInterval(interval);
this.onPaymentConfirmed();
}
}, 3000);
// Parar após 10 minutos
setTimeout(() => clearInterval(interval), 10 * 60 * 1000);
}
onPaymentConfirmed() {
// Redirecionar para página de sucesso
window.location.href = '/payment/success';
}
}
```
### 2. PDV (Ponto de Venda)
```python theme={null}
class PixPDV:
"""Sistema de PDV com cobrança PIX"""
def __init__(self, token: str):
self.token = token
def process_sale(self, items: list, customer: dict) -> dict:
"""Processar venda e gerar cobrança PIX"""
# Calcular total
total = sum(item['price'] * item['quantity'] for item in items)
# Gerar descrição
description = self.generate_sale_description(items)
# Criar cobrança PIX (expira em 15 minutos)
charge = create_pix_charge(
token=self.token,
order_id=self.generate_sale_id(),
amount=total,
customer_name=customer['name'],
customer_document=customer['document'],
expiration_hours=0.25, # 15 minutos
additional_info={
'items_count': str(len(items)),
'cashier_id': self.get_cashier_id()
}
)
# Imprimir comprovante com QR Code
self.print_receipt(charge, items, total)
return charge
def generate_sale_description(self, items: list) -> str:
"""Gerar descrição resumida da venda"""
if len(items) == 1:
return f"{items[0]['name']}"
else:
return f"{len(items)} itens - {items[0]['name']} e mais"
def print_receipt(self, charge: dict, items: list, total: float):
"""Imprimir comprovante com QR Code"""
# Implementar impressão térmica ou gerar PDF
print("\n" + "="*50)
print("COMPROVANTE DE COBRANÇA PIX")
print("="*50)
for item in items:
print(f"{item['name']}: R$ {item['price']:.2f}")
print("-"*50)
print(f"TOTAL: R$ {total:.2f}")
print(f"\nID da Transação: {charge['transactionId']}")
print(f"Código PIX:\n{charge['pixCode']}")
print("="*50 + "\n")
```
### 3. SaaS - Cobrança de Assinatura
```typescript theme={null}
class SubscriptionBilling {
constructor(private token: string) {}
async chargeMonthlySubscription(
subscriptionId: string,
userId: string,
planValue: number
) {
// Buscar dados do usuário
const user = await this.getUserData(userId);
// Gerar cobrança com expiração de 3 dias
const charge = await createPixCharge(
this.token,
`SUB-${subscriptionId}-${new Date().getMonth() + 1}`,
planValue,
user.name,
user.document
);
// Enviar email com link de pagamento
await this.sendPaymentEmail(user.email, charge);
// Agendar lembrete 1 dia antes de expirar
await this.scheduleReminder(user, charge, 24);
return charge;
}
async sendPaymentEmail(email: string, charge: CashInResponse) {
// Implementar envio de email
const paymentLink = `https://app.exemplo.com/payment/${charge.transactionId}`;
await sendEmail({
to: email,
subject: 'Fatura disponível - Pague com PIX',
html: `
Sua fatura está disponível
Valor: R$ ${charge.value}
Vencimento: ${new Date(charge.expirationDate).toLocaleDateString('pt-BR')}
Clique aqui para pagar com PIX
`
});
}
}
```
## Monitoramento de Pagamentos
Para ser notificado quando um pagamento for confirmado, você pode:
Configure webhooks para receber notificações automáticas quando o status mudar.
```javascript theme={null}
// Endpoint webhook em seu servidor
app.post('/webhooks/pix', (req, res) => {
const { transactionId, status, externalId } = req.body;
if (status === 'CONFIRMED') {
// Processar pagamento confirmado
processPaymentConfirmation(externalId);
}
res.sendStatus(200);
});
```
Consulte periodicamente o status da transação.
```javascript theme={null}
async function monitorPayment(transactionId, maxAttempts = 200) {
for (let i = 0; i < maxAttempts; i++) {
const status = await checkTransactionStatus(transactionId);
if (status === 'CONFIRMED') {
return true;
}
// Aguardar 3 segundos antes de tentar novamente
await new Promise(resolve => setTimeout(resolve, 3000));
}
return false; // Timeout
}
```
## Códigos de Resposta
| Código | Descrição | Significado |
| ------ | --------------- | ------------------------------------------- |
| `201` | Cobrança Criada | Cobrança PIX gerada com sucesso |
| `400` | Dados Inválidos | Verifique os campos obrigatórios e formatos |
| `401` | Token Inválido | Token não fornecido, expirado ou inválido |
Consulte a [Referência da API](/api-reference/endpoints/pix-cash-in) para detalhes completos dos campos de resposta.
## Boas Práticas
Inclua informações que facilitem a identificação: `ORDER-{orderId}-{timestamp}` ou `INV-{invoiceId}-{date}`
* **E-commerce:** 15-30 minutos
* **Boletos/Faturas:** 3-7 dias
* **PDV:** 5-15 minutos
Implemente validação local para evitar erros 400.
```javascript theme={null}
function isValidCPF(cpf: string): boolean {
cpf = cpf.replace(/\D/g, '');
if (cpf.length !== 11) return false;
// Adicionar lógica de validação de CPF
return true;
}
```
Use bibliotecas de precisão decimal para evitar erros de arredondamento.
```javascript theme={null}
import Decimal from 'decimal.js';
const total = new Decimal(price).times(quantity).toNumber();
```
## Observações Importantes
Cobranças expiradas não podem ser reativadas. Gere uma nova cobrança se necessário.
* **Valor mínimo:** R\$ 0,01
* **Expiração mínima:** 5 minutos (300 segundos)
* **Expiração máxima:** 7 dias (604800 segundos)
## Próximos Passos
Aprenda a estornar pagamentos recebidos
Envie pagamentos PIX
Realize pagamentos via QR Code PIX
# PIX Cash-Out (Pagamento)
Source: https://docs.firebanking.dev/api-reference/guides/pix-cash-out
Como realizar pagamentos PIX para qualquer chave
## Visão Geral
O endpoint **PIX Cash-Out** permite que você realize pagamentos PIX instantâneos para qualquer chave PIX válida (CPF, CNPJ, telefone, email ou chave aleatória). O pagamento é processado em tempo real e o valor é debitado da sua conta imediatamente.
Para pagamentos via QR Code PIX (escaneamento ou copia-e-cola), utilize o endpoint dedicado [Cash-Out via QR Code](/api-reference/guides/pix-cash-out-qrcode). Este endpoint é exclusivo para pagamentos por chave PIX.
Este endpoint requer um token Bearer válido. Verifique a [documentação de autenticação](/api-reference/guides/authentication) para mais detalhes.
## Características
* Pagamentos instantâneos 24/7
* Suporte a todos os tipos de chave PIX
* Validação automática de dados do destinatário
* Identificação única por `externalId`
* Descrição personalizável para o destinatário
* Verificação de saldo automática
## Endpoint
### POST /api/pix/cash-out
Realiza um pagamento PIX.
#### Headers Obrigatórios
```
Authorization: Bearer {token}
Content-Type: application/json
```
#### Request Body
```json theme={null}
{
"value": 250.50,
"details": {
"key": "12345678901",
"keyType": "DOCUMENT",
"name": "Ana Costa",
"document": "12345678901"
},
"externalId": "PAYMENT-987654-20240119",
"description": "Pagamento de fornecedor"
}
```
#### Request
```bash theme={null}
curl -X POST https://api.public.firebanking.com.br/api/pix/cash-out \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"value": 250.50,
"details": {
"key": "12345678901",
"keyType": "DOCUMENT",
"name": "Ana Costa",
"document": "12345678901"
},
"externalId": "PAYMENT-987654-20240119",
"description": "Pagamento de fornecedor"
}'
```
#### Response (201 Created)
```json theme={null}
{
"transactionId": "9876",
"externalId": "PAYMENT-987654-20240119",
"status": "PENDING",
"generateTime": "2024-01-19T15:45:00.000Z"
}
```
## Parâmetros da Requisição
Valor do pagamento em reais (BRL). Deve ter no máximo 2 casas decimais.
**Mínimo:** `0.01`
**Exemplo:** `250.50`
Informações da chave PIX de destino.
Chave PIX de destino.
**Formatos aceitos:**
* CPF: `12345678901` (11 dígitos)
* CNPJ: `12345678000199` (14 dígitos)
* Email: `usuario@exemplo.com`
* Telefone: `5511999999999` (com DDI e DDD)
* Chave aleatória: UUID formato `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
Tipo da chave PIX.
**Valores aceitos:**
* `DOCUMENT` - CPF ou CNPJ
* `EMAIL` - Endereço de email
* `PHONE` - Número de telefone
* `RANDOM` - Chave aleatória (UUID)
**Exemplo:** `"DOCUMENT"`
Nome completo do titular da chave PIX de destino.
**Validação:** O nome deve corresponder ao cadastrado na chave PIX
**Exemplo:** `"Ana Costa"`
CPF ou CNPJ do titular (apenas números).
**CPF:** 11 dígitos
**CNPJ:** 14 dígitos
**Validação:** O documento deve corresponder ao cadastrado na chave PIX
**Exemplo:** `"12345678901"`
Identificador único externo da transação.
**Máximo:** 255 caracteres
**Recomendação:** Use um formato que garanta unicidade
**Exemplo:** `"PAYMENT-987654-20240119-154500"`
Descrição do pagamento que aparecerá no extrato do destinatário.
**Máximo:** 140 caracteres
**Padrão:** Vazio
**Exemplo:** `"Pagamento de fornecedor - Nota Fiscal 12345"`
## Estrutura da Resposta
ID interno da transação gerada pela Fire Banking.
**Exemplo:** `"9876"`
ID externo fornecido na requisição (mesmo valor do input).
**Exemplo:** `"PAYMENT-987654-20240119"`
Status atual da transação.
**Valores possíveis:**
* `PENDING`: Pagamento em processamento
* `CONFIRMED`: Pagamento confirmado e finalizado
* `ERROR`: Erro no processamento
**Exemplo:** `"PENDING"`
**Nota:** A maioria dos pagamentos PIX é confirmada em poucos segundos
Data e hora de criação do pagamento (ISO 8601 UTC).
**Exemplo:** `"2024-01-19T15:45:00.000Z"`
## Exemplos de Implementação
### Node.js / TypeScript
```typescript theme={null}
import axios from 'axios';
interface CashOutRequest {
value: number;
details: {
key: string;
keyType: 'DOCUMENT' | 'EMAIL' | 'PHONE' | 'RANDOM';
name: string;
document: string;
};
externalId: string;
description?: string;
}
interface CashOutResponse {
transactionId: string;
externalId: string;
status: 'PENDING' | 'CONFIRMED' | 'ERROR';
generateTime: string;
}
async function sendPixPayment(
token: string,
recipientKey: string,
recipientKeyType: 'DOCUMENT' | 'EMAIL' | 'PHONE' | 'RANDOM',
recipientName: string,
recipientDocument: string,
amount: number,
description?: string
): Promise {
const payload: CashOutRequest = {
value: amount,
details: {
key: recipientKey,
keyType: recipientKeyType,
name: recipientName,
document: recipientDocument
},
externalId: `PAY-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
description: description || `Pagamento PIX de R$ ${amount.toFixed(2)}`
};
try {
const response = await axios.post(
'https://api.public.firebanking.com.br/api/pix/cash-out',
payload,
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
console.log('Pagamento PIX iniciado com sucesso!');
console.log(`ID da Transação: ${response.data.transactionId}`);
console.log(`Status: ${response.data.status}`);
console.log(`Valor: R$ ${amount.toFixed(2)}`);
console.log(`Destinatário: ${recipientName}`);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
const errorData = error.response?.data;
console.error('Erro ao realizar pagamento:', errorData);
// Tratar erros específicos
if (error.response?.status === 400) {
if (errorData?.message?.includes('saldo insuficiente')) {
throw new Error('Saldo insuficiente para realizar o pagamento');
}
throw new Error('Dados inválidos: ' + errorData?.message);
}
throw new Error(errorData?.message || 'Erro ao realizar pagamento PIX');
}
throw error;
}
}
// Uso - Pagamento por CPF
sendPixPayment(
'seu_token_aqui',
'12345678901',
'DOCUMENT',
'Ana Costa',
'12345678901',
250.50,
'Pagamento de fornecedor'
);
// Uso - Pagamento por Email
sendPixPayment(
'seu_token_aqui',
'ana.costa@email.com',
'EMAIL',
'Ana Costa',
'12345678901',
100.00,
'Reembolso'
);
// Uso - Pagamento por Telefone
sendPixPayment(
'seu_token_aqui',
'5511999999999',
'PHONE',
'Ana Costa',
'12345678901',
50.00
);
```
### Python
```python theme={null}
import requests
from datetime import datetime
from typing import Dict, Optional
import uuid
def send_pix_payment(
token: str,
recipient_key: str,
recipient_key_type: str,
recipient_name: str,
recipient_document: str,
amount: float,
description: Optional[str] = None
) -> Dict:
"""
Envia um pagamento PIX
Args:
token: Token Bearer válido
recipient_key: Chave PIX do destinatário
recipient_key_type: Tipo da chave (DOCUMENT, EMAIL, PHONE, RANDOM)
recipient_name: Nome do destinatário
recipient_document: CPF ou CNPJ do destinatário
amount: Valor em reais
description: Descrição do pagamento (opcional)
Returns:
Dados do pagamento iniciado
"""
url = 'https://api.public.firebanking.com.br/api/pix/cash-out'
payload = {
'value': round(amount, 2),
'details': {
'key': recipient_key,
'keyType': recipient_key_type,
'name': recipient_name,
'document': recipient_document
},
'externalId': f'PAY-{int(datetime.now().timestamp())}-{uuid.uuid4().hex[:8]}',
'description': description or f'Pagamento PIX de R$ {amount:.2f}'
}
headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
try:
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
data = response.json()
print('Pagamento PIX iniciado com sucesso!')
print(f"ID da Transação: {data['transactionId']}")
print(f"Status: {data['status']}")
print(f"Valor: R$ {amount:.2f}")
print(f"Destinatário: {recipient_name}")
return data
except requests.exceptions.HTTPError as e:
error_data = e.response.json() if e.response else {}
# Tratar erros específicos
if e.response.status_code == 400:
if 'saldo insuficiente' in error_data.get('message', '').lower():
raise Exception('Saldo insuficiente para realizar o pagamento')
raise Exception(f"Dados inválidos: {error_data.get('message')}")
raise Exception(f"Erro ao realizar pagamento: {error_data.get('message', str(e))}")
# Uso
token = 'seu_token_aqui'
# Pagamento por CPF
payment = send_pix_payment(
token=token,
recipient_key='12345678901',
recipient_key_type='DOCUMENT',
recipient_name='Ana Costa',
recipient_document='12345678901',
amount=250.50,
description='Pagamento de fornecedor'
)
```
### PHP
```php theme={null}
round($amount, 2),
'details' => [
'key' => $recipientKey,
'keyType' => $recipientKeyType,
'name' => $recipientName,
'document' => $recipientDocument
],
'externalId' => 'PAY-' . time() . '-' . bin2hex(random_bytes(4)),
'description' => $description ?? "Pagamento PIX de R$ " . number_format($amount, 2, ',', '.')
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $token,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 201) {
$errorData = json_decode($response, true);
$errorMessage = $errorData['message'] ?? "HTTP $httpCode";
if ($httpCode === 400 && stripos($errorMessage, 'saldo insuficiente') !== false) {
throw new Exception('Saldo insuficiente para realizar o pagamento');
}
throw new Exception("Erro ao realizar pagamento: $errorMessage");
}
$data = json_decode($response, true);
echo "Pagamento PIX iniciado com sucesso!" . PHP_EOL;
echo "ID da Transação: {$data['transactionId']}" . PHP_EOL;
echo "Status: {$data['status']}" . PHP_EOL;
echo "Valor: R$ " . number_format($amount, 2, ',', '.') . PHP_EOL;
echo "Destinatário: $recipientName" . PHP_EOL;
return $data;
}
// Uso
$token = 'seu_token_aqui';
$payment = sendPixPayment(
$token,
'12345678901',
'DOCUMENT',
'Ana Costa',
'12345678901',
250.50,
'Pagamento de fornecedor'
);
```
## Casos de Uso
### 1. Folha de Pagamento
```typescript theme={null}
class PayrollProcessor {
constructor(private token: string) {}
async processPayroll(employees: Employee[]) {
const results = {
successful: [],
failed: []
};
for (const employee of employees) {
try {
// Verificar saldo antes de cada pagamento
const balance = await getBalance(this.token);
if (balance.netBalance < employee.salary) {
throw new Error('Saldo insuficiente');
}
// Realizar pagamento
const payment = await sendPixPayment(
this.token,
employee.pixKey,
employee.pixKeyType,
employee.fullName,
employee.document,
employee.salary,
`Salário ${new Date().toLocaleDateString('pt-BR', { month: 'long', year: 'numeric' })}`
);
results.successful.push({
employee: employee.fullName,
amount: employee.salary,
transactionId: payment.transactionId
});
// Aguardar 1 segundo entre pagamentos
await this.sleep(1000);
} catch (error) {
results.failed.push({
employee: employee.fullName,
error: error.message
});
}
}
return results;
}
private sleep(ms: number): Promise {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// Uso
interface Employee {
fullName: string;
document: string;
pixKey: string;
pixKeyType: 'DOCUMENT' | 'EMAIL' | 'PHONE' | 'RANDOM';
salary: number;
}
const payroll = new PayrollProcessor('seu_token_aqui');
const employees: Employee[] = [
{
fullName: 'Pedro Santos',
document: '12345678901',
pixKey: '12345678901',
pixKeyType: 'DOCUMENT',
salary: 3500.00
},
// ... mais funcionários
];
const results = await payroll.processPayroll(employees);
console.log(`Pagamentos bem-sucedidos: ${results.successful.length}`);
console.log(`Pagamentos com erro: ${results.failed.length}`);
```
### 2. Marketplace - Repasse para Vendedores
```python theme={null}
class MarketplacePayouts:
"""Processa repasses para vendedores de marketplace"""
def __init__(self, token: str):
self.token = token
def process_seller_payouts(self, sales_data: list) -> dict:
"""Processa repasses baseados em vendas"""
results = {'successful': [], 'failed': []}
# Agrupar vendas por vendedor
seller_totals = self.group_sales_by_seller(sales_data)
for seller_id, total_amount in seller_totals.items():
try:
# Buscar dados do vendedor
seller = self.get_seller_data(seller_id)
# Calcular valor após comissão
commission = total_amount * 0.10 # 10% de comissão
payout_amount = total_amount - commission
# Realizar pagamento
payment = send_pix_payment(
token=self.token,
recipient_key=seller['pix_key'],
recipient_key_type=seller['pix_key_type'],
recipient_name=seller['name'],
recipient_document=seller['document'],
amount=payout_amount,
description=f'Repasse de vendas - {len(sales_data)} transações'
)
results['successful'].append({
'seller': seller['name'],
'gross_amount': total_amount,
'commission': commission,
'net_amount': payout_amount,
'transaction_id': payment['transactionId']
})
# Registrar repasse no banco de dados
self.record_payout(seller_id, payment)
except Exception as e:
results['failed'].append({
'seller_id': seller_id,
'error': str(e)
})
return results
def group_sales_by_seller(self, sales_data: list) -> dict:
"""Agrupa vendas por vendedor"""
totals = {}
for sale in sales_data:
seller_id = sale['seller_id']
totals[seller_id] = totals.get(seller_id, 0) + sale['amount']
return totals
```
### 3. Sistema de Reembolso
```javascript theme={null}
class RefundSystem {
constructor(token) {
this.token = token;
}
async processRefund(orderId, refundReason) {
// Buscar dados do pedido
const order = await this.getOrderData(orderId);
// Validar se reembolso é permitido
if (!this.canRefund(order)) {
throw new Error('Reembolso não permitido para este pedido');
}
// Realizar pagamento de volta ao cliente
const refund = await sendPixPayment(
this.token,
order.customer.pixKey,
order.customer.pixKeyType,
order.customer.name,
order.customer.document,
order.amount,
`Reembolso - Pedido ${orderId} - ${refundReason}`
);
// Atualizar status do pedido
await this.updateOrderStatus(orderId, 'REFUNDED', refund.transactionId);
// Enviar notificação ao cliente
await this.notifyCustomer(order.customer.email, refund);
return refund;
}
canRefund(order) {
// Verificar se pedido foi pago e ainda está dentro do prazo
const daysSincePurchase = (Date.now() - new Date(order.paidAt)) / (1000 * 60 * 60 * 24);
return order.status === 'PAID' && daysSincePurchase <= 7;
}
}
```
## Validação de Chave PIX
Antes de enviar um pagamento, valide o formato da chave PIX:
```typescript theme={null}
function validatePixKey(key: string, keyType: string): boolean {
switch (keyType) {
case 'DOCUMENT':
// CPF: 11 dígitos ou CNPJ: 14 dígitos
return /^\d{11}$|^\d{14}$/.test(key);
case 'EMAIL':
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(key);
case 'PHONE':
// Formato: +5511999999999 (DDI + DDD + número)
return /^55\d{10,11}$/.test(key);
case 'RANDOM':
// UUID formato: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(key);
default:
return false;
}
}
```
## Verificação de Saldo
Sempre verifique o saldo antes de realizar pagamentos para evitar erros 400.
```typescript theme={null}
async function safePayment(
token: string,
amount: number,
recipient: RecipientData
) {
// Consultar saldo
const balance = await getBalance(token);
// Verificar se há saldo suficiente
if (balance.netBalance < amount) {
throw new Error(
`Saldo insuficiente. Disponível: R$ ${balance.netBalance.toFixed(2)} | ` +
`Necessário: R$ ${amount.toFixed(2)}`
);
}
// Prosseguir com pagamento
return await sendPixPayment(token, ...recipient, amount);
}
```
## Monitoramento de Status
Para acompanhar a confirmação do pagamento:
```javascript theme={null}
async function monitorPaymentStatus(transactionId, timeout = 60000) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const status = await checkTransactionStatus(transactionId);
if (status === 'CONFIRMED') {
console.log('Pagamento confirmado!');
return true;
}
if (status === 'ERROR') {
throw new Error('Pagamento falhou');
}
// Aguardar 2 segundos antes de verificar novamente
await new Promise(resolve => setTimeout(resolve, 2000));
}
throw new Error('Timeout: Pagamento não confirmado no tempo esperado');
}
```
## Códigos de Resposta
| Código | Descrição | Significado |
| ------ | ------------------ | -------------------------------------------- |
| `201` | Pagamento Iniciado | Transferência PIX iniciada com sucesso |
| `400` | Saldo Insuficiente | Saldo insuficiente para realizar a transação |
| `400` | Dados Inválidos | Verifique os campos obrigatórios e formatos |
| `401` | Token Inválido | Token não fornecido, expirado ou inválido |
Consulte a [Referência da API](/api-reference/endpoints/pix-cash-out) para detalhes completos dos campos de resposta.
## Boas Práticas
Consulte o saldo disponível antes de realizar pagamentos para evitar erros.
Facilita a conciliação e o rastreamento de pagamentos: `PAY-{timestamp}-{uuid}`
Implemente validação local de chaves PIX e documentos antes de enviar a requisição.
Em caso de falhas temporárias, implemente lógica de retry com backoff exponencial.
```javascript theme={null}
async function retryPayment(paymentFn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await paymentFn();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
}
```
Mantenha um log completo de todas as tentativas de pagamento para auditoria.
## Observações Importantes
* **Valor mínimo:** R\$ 0,01
## Próximos Passos
Realize pagamentos escaneando QR Codes PIX
Verifique o saldo antes de realizar pagamentos
Receba pagamentos via PIX
# PIX Cash-Out via QR Code
Source: https://docs.firebanking.dev/api-reference/guides/pix-cash-out-qrcode
Como realizar pagamentos PIX escaneando QR Codes
## Visão Geral
O endpoint **PIX Cash-Out via QR Code** permite que você realize pagamentos PIX a partir de um QR Code escaneado ou copiado (copia-e-cola). O QR Code deve seguir o padrão EMV do Banco Central do Brasil. Os dados do destinatário são extraídos automaticamente do QR Code, simplificando o processo de pagamento.
Este endpoint requer um token Bearer válido. Verifique a [documentação de autenticação](/api-reference/guides/authentication) para mais detalhes.
## Chave PIX vs QR Code: Qual endpoint usar?
A API Fire Banking oferece dois endpoints para enviar pagamentos PIX. Escolha o mais adequado para o seu caso de uso:
| Critério | Cash-Out por Chave PIX | Cash-Out via QR Code |
| -------------------------- | ------------------------------------------- | ---------------------------------------------------- |
| **Endpoint** | `POST /api/pix/cash-out` | `POST /api/pix/cash-out-qrcode` |
| **Quando usar** | Você conhece a chave PIX do destinatário | Você tem o QR Code gerado pelo recebedor |
| **Dados do destinatário** | Obrigatórios (chave, tipo, nome, documento) | Embutidos no QR Code (opcionais no request) |
| **Validação de valor** | Apenas saldo e limites | Saldo, limites + valor do QR Code vs valor informado |
| **Tipos de chave** | CPF, CNPJ, email, telefone, aleatória | N/A (informação dentro do QR Code) |
| **Webhook de confirmação** | Evento `CashOut` | Mesmo evento `CashOut` |
| **Resposta** | Mesma estrutura | Mesma estrutura |
* **Use Cash-Out por Chave** quando sua aplicação já tem os dados do destinatário (ex: folha de pagamento, repasses programáticos)
* **Use Cash-Out via QR Code** quando o pagamento é iniciado a partir de um QR Code escaneado (ex: PDV, pagamento de conta, copia-e-cola)
Ambos os endpoints retornam a mesma estrutura de resposta e disparam o mesmo webhook `CashOut` quando confirmados.
## Características
* Pagamento via QR Code estático ou dinâmico
* Validação automática de valor embutido no QR Code
* Verificação de saldo automática antes do envio
* Identificação única por `externalId` (idempotência)
* Cálculo automático de taxas
## Endpoint
### POST /api/pix/cash-out-qrcode
Realiza um pagamento PIX a partir de um QR Code.
#### Headers Obrigatórios
```
Authorization: Bearer {token}
Content-Type: application/json
```
#### Request Body
```json theme={null}
{
"value": 15.50,
"qrCode": "00020126580014br.gov.bcb.pix0136a1b2c3d4-e5f6-7890-abcd-ef1234567890520400005303986540515.505802BR5925DESTINATARIO LTDA6009SAO PAULO62070503***6304ABCD",
"externalId": "QRPAY-987654-20240119",
"description": "Pagamento fornecedor XYZ via QR Code",
"name": "Destinatario Ltda",
"document": "12345678000190"
}
```
#### Request
```bash theme={null}
curl -X POST https://api.public.firebanking.com.br/api/pix/cash-out-qrcode \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"value": 15.50,
"qrCode": "00020126580014br.gov.bcb.pix0136a1b2c3d4-e5f6-7890-abcd-ef1234567890520400005303986540515.505802BR5925DESTINATARIO LTDA6009SAO PAULO62070503***6304ABCD",
"externalId": "QRPAY-987654-20240119",
"description": "Pagamento fornecedor XYZ via QR Code",
"name": "Destinatario Ltda",
"document": "12345678000190"
}'
```
#### Response (201 Created)
```json theme={null}
{
"transactionId": "456",
"externalId": "QRPAY-987654-20240119",
"status": "PENDING",
"generateTime": "2024-01-19T14:30:00.000Z"
}
```
## Parâmetros da Requisição
Valor do pagamento em reais (BRL). Deve ter no máximo 2 casas decimais. Se o QR Code contiver um valor embutido, o valor informado deve corresponder (tolerância de 1 centavo).
**Mínimo:** `0.01`
**Exemplo:** `15.50`
Conteúdo do QR Code PIX (string EMV). Pode ser obtido via escaneamento de câmera ou pelo campo copia-e-cola.
**Mínimo:** 50 caracteres
**Máximo:** 500 caracteres
**Formato:** Deve iniciar com `000201` (padrão EMV PIX do Banco Central)
**Exemplo:** `"00020126580014br.gov.bcb.pix0136a1b2c3d4-e5f6-7890-abcd-ef1234567890520400005303986540515.505802BR5925DESTINATARIO LTDA6009SAO PAULO62070503***6304ABCD"`
Identificador externo único da transação. Garante idempotência — enviar o mesmo `externalId` duas vezes resulta em erro 409.
**Máximo:** 255 caracteres
**Recomendação:** Use um formato que garanta unicidade
**Exemplo:** `"QRPAY-987654-20240119"`
Descrição do pagamento que aparecerá no extrato do destinatário.
**Máximo:** 140 caracteres
**Padrão:** Vazio
**Exemplo:** `"Pagamento fornecedor XYZ via QR Code"`
Nome do destinatário. Opcional — quando omitido, os dados do QR Code são utilizados.
**Exemplo:** `"Destinatario Ltda"`
CPF ou CNPJ do destinatário (apenas números). Opcional — quando omitido, os dados do QR Code são utilizados.
**CPF:** 11 dígitos
**CNPJ:** 14 dígitos
**Exemplo:** `"12345678000190"`
## Estrutura da Resposta
ID interno da transação gerada pela Fire Banking.
**Exemplo:** `"456"`
ID externo fornecido na requisição (mesmo valor do input).
**Exemplo:** `"QRPAY-987654-20240119"`
Status atual da transação.
**Valores possíveis:**
* `PENDING`: Pagamento em processamento
* `CONFIRMED`: Pagamento confirmado e finalizado
* `ERROR`: Erro no processamento
**Exemplo:** `"PENDING"`
**Nota:** A maioria dos pagamentos PIX é confirmada em poucos segundos
Data e hora de criação do pagamento (ISO 8601 UTC).
**Exemplo:** `"2024-01-19T14:30:00.000Z"`
## Exemplos de Implementação
### Node.js / TypeScript
```typescript theme={null}
import axios from 'axios';
interface CashOutQrCodeRequest {
value: number;
qrCode: string;
externalId: string;
description?: string;
name?: string;
document?: string;
}
interface CashOutQrCodeResponse {
transactionId: string;
externalId: string;
status: 'PENDING' | 'CONFIRMED' | 'ERROR';
generateTime: string;
}
async function payWithQrCode(
token: string,
qrCode: string,
amount: number,
description?: string
): Promise {
const payload: CashOutQrCodeRequest = {
value: amount,
qrCode,
externalId: `QRPAY-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
description: description || 'Pagamento via QR Code PIX'
};
try {
const response = await axios.post(
'https://api.public.firebanking.com.br/api/pix/cash-out-qrcode',
payload,
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
console.log('Pagamento via QR Code iniciado!');
console.log(`ID da Transação: ${response.data.transactionId}`);
console.log(`Status: ${response.data.status}`);
console.log(`Valor: R$ ${amount.toFixed(2)}`);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
const errorData = error.response?.data;
console.error('Erro ao realizar pagamento:', errorData);
if (error.response?.status === 400) {
if (errorData?.code === 'INVALID_QR_CODE') {
throw new Error('QR Code inválido ou malformado');
}
if (errorData?.code === 'QR_CODE_VALUE_MISMATCH') {
throw new Error('Valor informado diverge do valor no QR Code');
}
if (errorData?.code === 'INSUFFICIENT_BALANCE') {
throw new Error('Saldo insuficiente para realizar o pagamento');
}
throw new Error('Dados inválidos: ' + errorData?.message);
}
if (error.response?.status === 409) {
throw new Error('externalId já utilizado em outra transação');
}
throw new Error(errorData?.message || 'Erro ao realizar pagamento via QR Code');
}
throw error;
}
}
// Uso - Pagamento via QR Code escaneado
const qrCodeContent = '00020126580014br.gov.bcb.pix0136a1b2c3d4...6304ABCD';
payWithQrCode('seu_token_aqui', qrCodeContent, 15.50, 'Pagamento fornecedor');
```
### Python
```python theme={null}
import requests
from datetime import datetime
from typing import Dict, Optional
import uuid
def pay_with_qr_code(
token: str,
qr_code: str,
amount: float,
description: Optional[str] = None
) -> Dict:
"""
Envia um pagamento PIX via QR Code
Args:
token: Token Bearer válido
qr_code: Conteúdo do QR Code PIX (string EMV)
amount: Valor em reais
description: Descrição do pagamento (opcional)
Returns:
Dados do pagamento iniciado
"""
url = 'https://api.public.firebanking.com.br/api/pix/cash-out-qrcode'
payload = {
'value': round(amount, 2),
'qrCode': qr_code,
'externalId': f'QRPAY-{int(datetime.now().timestamp())}-{uuid.uuid4().hex[:8]}',
'description': description or 'Pagamento via QR Code PIX'
}
headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
try:
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
data = response.json()
print('Pagamento via QR Code iniciado!')
print(f"ID da Transação: {data['transactionId']}")
print(f"Status: {data['status']}")
print(f"Valor: R$ {amount:.2f}")
return data
except requests.exceptions.HTTPError as e:
error_data = e.response.json() if e.response else {}
if e.response.status_code == 400:
code = error_data.get('code', '')
if code == 'INVALID_QR_CODE':
raise Exception('QR Code inválido ou malformado')
if code == 'QR_CODE_VALUE_MISMATCH':
raise Exception('Valor informado diverge do valor no QR Code')
if code == 'INSUFFICIENT_BALANCE':
raise Exception('Saldo insuficiente para realizar o pagamento')
raise Exception(f"Dados inválidos: {error_data.get('message')}")
if e.response.status_code == 409:
raise Exception('externalId já utilizado em outra transação')
raise Exception(f"Erro ao realizar pagamento: {error_data.get('message', str(e))}")
# Uso
token = 'seu_token_aqui'
qr_code = '00020126580014br.gov.bcb.pix0136a1b2c3d4...6304ABCD'
payment = pay_with_qr_code(
token=token,
qr_code=qr_code,
amount=15.50,
description='Pagamento fornecedor XYZ via QR Code'
)
```
### PHP
```php theme={null}
round($amount, 2),
'qrCode' => $qrCode,
'externalId' => 'QRPAY-' . time() . '-' . bin2hex(random_bytes(4)),
'description' => $description ?? 'Pagamento via QR Code PIX'
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $token,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 201) {
$errorData = json_decode($response, true);
$errorCode = $errorData['code'] ?? '';
$errorMessage = $errorData['message'] ?? "HTTP $httpCode";
if ($httpCode === 400) {
if ($errorCode === 'INVALID_QR_CODE') {
throw new Exception('QR Code inválido ou malformado');
}
if ($errorCode === 'QR_CODE_VALUE_MISMATCH') {
throw new Exception('Valor informado diverge do valor no QR Code');
}
if ($errorCode === 'INSUFFICIENT_BALANCE') {
throw new Exception('Saldo insuficiente para realizar o pagamento');
}
}
if ($httpCode === 409) {
throw new Exception('externalId já utilizado em outra transação');
}
throw new Exception("Erro ao realizar pagamento: $errorMessage");
}
$data = json_decode($response, true);
echo "Pagamento via QR Code iniciado!" . PHP_EOL;
echo "ID da Transação: {$data['transactionId']}" . PHP_EOL;
echo "Status: {$data['status']}" . PHP_EOL;
echo "Valor: R$ " . number_format($amount, 2, ',', '.') . PHP_EOL;
return $data;
}
// Uso
$token = 'seu_token_aqui';
$qrCode = '00020126580014br.gov.bcb.pix0136a1b2c3d4...6304ABCD';
$payment = payWithQrCode($token, $qrCode, 15.50, 'Pagamento fornecedor XYZ');
```
## Casos de Uso
### 1. PDV — Pagar via QR Code no Caixa
```typescript theme={null}
class PointOfSalePayment {
constructor(private token: string) {}
async payFromScannedQrCode(qrCodeContent: string, amount: number) {
// Validar QR Code localmente antes de enviar
if (!this.isValidPixQrCode(qrCodeContent)) {
throw new Error('QR Code inválido. Verifique e tente novamente.');
}
const payment = await payWithQrCode(
this.token,
qrCodeContent,
amount,
`Pagamento PDV - ${new Date().toLocaleDateString('pt-BR')}`
);
console.log(`Pagamento iniciado: ${payment.transactionId}`);
return payment;
}
private isValidPixQrCode(qrCode: string): boolean {
return qrCode.length >= 50 && qrCode.startsWith('000201');
}
}
```
### 2. Pagamento de Fornecedor via QR Code
```python theme={null}
class SupplierPayment:
"""Processa pagamentos a fornecedores via QR Code"""
def __init__(self, token: str):
self.token = token
def pay_supplier_invoice(self, qr_code: str, invoice_amount: float, invoice_id: str):
"""Paga fatura de fornecedor via QR Code"""
# Verificar saldo antes
balance = get_balance(self.token)
if balance['netBalance'] < invoice_amount:
raise Exception(f"Saldo insuficiente. Disponível: R$ {balance['netBalance']:.2f}")
payment = pay_with_qr_code(
token=self.token,
qr_code=qr_code,
amount=invoice_amount,
description=f'Pagamento fatura #{invoice_id}'
)
return {
'invoice_id': invoice_id,
'transaction_id': payment['transactionId'],
'status': payment['status'],
'amount': invoice_amount
}
```
### 3. Automação de Pagamentos Recorrentes
```javascript theme={null}
class RecurringQrCodePayment {
constructor(token) {
this.token = token;
}
async processPaymentBatch(payments) {
const results = { successful: [], failed: [] };
for (const payment of payments) {
try {
const result = await payWithQrCode(
this.token,
payment.qrCode,
payment.amount,
payment.description
);
results.successful.push({
reference: payment.reference,
transactionId: result.transactionId,
amount: payment.amount
});
// Aguardar entre pagamentos
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
results.failed.push({
reference: payment.reference,
error: error.message
});
}
}
return results;
}
}
```
## Validação do QR Code
O QR Code PIX segue o padrão **EMV (Europay, Mastercard, Visa)** definido pelo Banco Central do Brasil. Antes de enviar à API, você pode validar localmente:
```typescript theme={null}
function isValidPixQrCode(qrCode: string): boolean {
// Verificar tamanho mínimo e máximo
if (qrCode.length < 50 || qrCode.length > 500) {
return false;
}
// Verificar prefixo EMV obrigatório
if (!qrCode.startsWith('000201')) {
return false;
}
return true;
}
```
**Estrutura do QR Code EMV PIX:**
* `000201` — Payload Format Indicator (obrigatório)
* `0102XX` — Point of Initiation Method (`11` = estático, `12` = dinâmico)
* Campos com dados do recebedor, valor, cidade, etc.
* `6304XXXX` — CRC16 (checksum de validação)
A validação completa do QR Code (decodificação EMV, verificação de CRC e extração de dados) é feita automaticamente pela API. A validação local serve apenas para filtrar QR Codes claramente inválidos.
## Códigos de Resposta
| Código | Erro | Descrição |
| ------ | ------------------------ | ---------------------------------------------------- |
| `201` | — | Pagamento PIX via QR Code iniciado com sucesso |
| `400` | `INVALID_QR_CODE` | QR Code inválido ou malformado |
| `400` | `QR_CODE_VALUE_MISMATCH` | Valor informado diverge do valor embutido no QR Code |
| `400` | `INSUFFICIENT_BALANCE` | Saldo insuficiente para realizar a transação |
| `401` | — | Token não fornecido, expirado ou inválido |
| `409` | `DUPLICATE_EXTERNAL_ID` | `externalId` já utilizado em outra transação |
Consulte a [Referência da API](/api-reference/endpoints/pix-cash-out-qrcode) para detalhes completos dos campos de resposta.
## Boas Práticas
Verifique se a string começa com `000201` e tem pelo menos 50 caracteres. Isso evita chamadas desnecessárias à API para QR Codes claramente inválidos.
Consulte o saldo disponível antes de enviar o pagamento para evitar erros 400 de saldo insuficiente.
```typescript theme={null}
const balance = await getBalance(token);
if (balance.netBalance < amount) {
throw new Error('Saldo insuficiente');
}
```
Garante idempotência e facilita a conciliação: `QRPAY-{timestamp}-{uuid}`
Se enviar o mesmo `externalId` duas vezes, receberá erro 409 — evitando pagamentos duplicados.
QR Codes dinâmicos podem não conter valor embutido. Nesse caso, o campo `value` define o valor do pagamento. QR Codes estáticos com valor embutido exigem que o `value` informado corresponda ao valor do QR Code.
## Observações Importantes
* **Valor mínimo:** R\$ 0,01
* **Formato do QR Code:** Deve iniciar com `000201` e ter entre 50 e 500 caracteres
* **QR Codes dinâmicos:** QR Codes sem valor embutido são aceitos — o campo `value` define o valor do pagamento
* **QR Codes estáticos com valor:** O valor informado em `value` deve corresponder ao valor embutido no QR Code (tolerância de 1 centavo)
## Próximos Passos
Realize pagamentos informando a chave PIX do destinatário
Receba notificações quando o pagamento for confirmado
Verifique o saldo antes de realizar pagamentos
# PIX Refund-In (Estorno)
Source: https://docs.firebanking.dev/api-reference/guides/pix-refund-in
Como estornar pagamentos PIX recebidos
## Visão Geral
O endpoint **PIX Refund-In** permite que você estorne (devolva) pagamentos PIX recebidos através de cobranças geradas via Cash-In. Os estornos podem ser **totais** ou **parciais** e devem ser solicitados dentro do prazo de **89 dias** após o recebimento.
Este endpoint requer um token Bearer válido. Verifique a [documentação de autenticação](/api-reference/guides/authentication) para mais detalhes.
## Características
* Estornos totais ou parciais
* Múltiplos estornos parciais da mesma transação
* Prazo de até 89 dias
* Processamento instantâneo
* Rastreamento por motivo do estorno
## Quando Usar Estornos
Devolve 100% do valor recebido ao pagador original.
**Casos de uso:**
* Cancelamento completo do pedido
* Produto não enviado
* Duplicação de pagamento
* Erro no valor cobrado
Devolve apenas parte do valor recebido.
**Casos de uso:**
* Devolução de itens específicos
* Compensação por problemas no produto/serviço
* Ajuste de valores
* Desconto retroativo
## Endpoint
### POST /api/pix/refund-in/
Solicita o estorno de um pagamento recebido.
#### Headers Obrigatórios
```
Authorization: Bearer {token}
Content-Type: application/json
```
#### Path Parameters
ID da transação original (Cash-In) a ser estornada.
**Exemplo:** `"7845"`
#### Request Body
```json theme={null}
{
"refundValue": 75.00,
"reason": "Cliente solicitou devolução de 1 item do pedido"
}
```
#### Request
```bash theme={null}
curl -X POST https://api.public.firebanking.com.br/api/pix/refund-in/7845 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"refundValue": 75.00,
"reason": "Cliente solicitou devolução de 1 item do pedido"
}'
```
#### Response (201 Created)
```json theme={null}
{
"transactionId": "7846",
"externalId": "D123456789",
"status": "PENDING",
"refundValue": 75.00,
"providerTransactionId": "7ef4fc3f-a187-495e-857c-e84d70612761",
"generateTime": "2024-01-19T16:30:00.000Z"
}
```
## Parâmetros da Requisição
Valor a ser estornado em reais (BRL). Deve ter no máximo 2 casas decimais.
**Validações:**
* Deve ser maior ou igual a 0.01
* Não pode exceder o valor disponível para estorno
* Soma de todos os estornos não pode exceder o valor original
**Exemplo:** `75.00`
Motivo do estorno (opcional, mas recomendado).
**Máximo:** 255 caracteres
**Exemplo:** `"Cliente solicitou devolução de 1 item do pedido"`
**Recomendação:** Sempre forneça um motivo claro para fins de auditoria
ID externo para identificação da devolução (opcional).
Na API BACEN, corresponde ao parâmetro 'id' da URL.
**Exemplo:** `"D123456789"`
## Estrutura da Resposta
ID da nova transação de estorno gerada.
**Exemplo:** `"7846"`
**Nota:** Este é um ID diferente da transação original
ID externo da transação de estorno.
**Exemplo:** `"D123456789"`
Status atual da transação de estorno.
**Valores possíveis:**
* `PENDING`: Estorno em processamento
* `CONFIRMED`: Estorno confirmado e finalizado
* `ERROR`: Erro no processamento
**Exemplo:** `"PENDING"`
Valor do estorno em reais.
**Exemplo:** `75.00`
ID da transação no provedor (usado para correlação com webhooks).
**Exemplo:** `"7ef4fc3f-a187-495e-857c-e84d70612761"`
Data e hora de geração do estorno (ISO 8601 UTC).
**Exemplo:** `"2024-01-19T16:30:00.000Z"`
## Exemplos de Implementação
### Node.js / TypeScript
```typescript theme={null}
import axios from 'axios';
interface RefundRequest {
refundValue: number;
reason?: string;
externalId?: string;
}
interface RefundResponse {
transactionId: string;
externalId: string;
status: 'PENDING' | 'CONFIRMED' | 'ERROR';
refundValue: number;
providerTransactionId: string;
generateTime: string;
}
async function refundPixPayment(
token: string,
originalTransactionId: string,
refundAmount: number,
reason?: string
): Promise {
const payload: RefundRequest = {
refundValue: refundAmount,
reason: reason || 'Estorno solicitado pelo cliente'
};
try {
const response = await axios.post(
`https://api.public.firebanking.com.br/api/pix/refund-in/${originalTransactionId}`,
payload,
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
console.log('Estorno PIX iniciado com sucesso!');
console.log(`ID da Transação de Estorno: ${response.data.transactionId}`);
console.log(`ID Externo Original: ${response.data.externalId}`);
console.log(`Valor do Estorno: R$ ${response.data.refundValue.toFixed(2)}`);
console.log(`Status: ${response.data.status}`);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
const errorData = error.response?.data;
console.error('Erro ao processar estorno:', errorData);
// Tratar erros específicos
if (error.response?.status === 400) {
if (errorData?.message?.includes('prazo excedido')) {
throw new Error('Prazo de 89 dias para estorno foi excedido');
}
if (errorData?.message?.includes('valor inválido')) {
throw new Error('Valor do estorno excede o disponível para estorno');
}
}
if (error.response?.status === 404) {
throw new Error('Transação original não encontrada');
}
throw new Error(errorData?.message || 'Erro ao processar estorno');
}
throw error;
}
}
// Uso - Estorno Total
async function fullRefund(token: string, transactionId: string, originalValue: number) {
return await refundPixPayment(
token,
transactionId,
originalValue,
'Cancelamento total do pedido'
);
}
// Uso - Estorno Parcial
async function partialRefund(token: string, transactionId: string, itemValue: number) {
return await refundPixPayment(
token,
transactionId,
itemValue,
'Devolução de 1 item do pedido'
);
}
// Exemplo prático
const token = 'seu_token_aqui';
const transactionId = '7845';
// Estornar R$ 75,00 de uma transação de R$ 150,00
refundPixPayment(token, transactionId, 75.00, 'Cliente solicitou devolução parcial');
```
### Python
```python theme={null}
import requests
from datetime import datetime
from typing import Dict, Optional
def refund_pix_payment(
token: str,
original_transaction_id: str,
refund_amount: float,
reason: Optional[str] = None
) -> Dict:
"""
Estorna um pagamento PIX recebido
Args:
token: Token Bearer válido
original_transaction_id: ID da transação original (Cash-In)
refund_amount: Valor a ser estornado
reason: Motivo do estorno (opcional)
Returns:
Dados do estorno criado
"""
url = f'https://api.public.firebanking.com.br/api/pix/refund-in/{original_transaction_id}'
payload = {
'refundValue': round(refund_amount, 2),
'reason': reason or 'Estorno solicitado pelo cliente'
}
headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
try:
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
data = response.json()
print('Estorno PIX iniciado com sucesso!')
print(f"ID da Transação de Estorno: {data['transactionId']}")
print(f"ID Externo Original: {data['externalId']}")
print(f"Valor do Estorno: R$ {data['refundValue']:.2f}")
print(f"Status: {data['status']}")
return data
except requests.exceptions.HTTPError as e:
error_data = e.response.json() if e.response else {}
error_message = error_data.get('message', str(e))
# Tratar erros específicos
if e.response.status_code == 400:
if 'prazo excedido' in error_message:
raise Exception('Prazo de 89 dias para estorno foi excedido')
if 'valor inválido' in error_message:
raise Exception('Valor do estorno excede o disponível para estorno')
raise Exception(f'Dados inválidos: {error_message}')
if e.response.status_code == 404:
raise Exception('Transação original não encontrada')
raise Exception(f'Erro ao processar estorno: {error_message}')
# Uso
token = 'seu_token_aqui'
transaction_id = '7845'
# Estorno parcial
refund = refund_pix_payment(
token=token,
original_transaction_id=transaction_id,
refund_amount=75.00,
reason='Cliente solicitou devolução de 1 item do pedido'
)
# Estorno total
def full_refund(token: str, transaction_id: str, original_value: float):
"""Realiza estorno total"""
return refund_pix_payment(
token=token,
original_transaction_id=transaction_id,
refund_amount=original_value,
reason='Cancelamento total do pedido'
)
```
### PHP
```php theme={null}
round($refundAmount, 2),
'reason' => $reason ?? 'Estorno solicitado pelo cliente'
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $token,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 201) {
$errorData = json_decode($response, true);
$errorMessage = $errorData['message'] ?? "HTTP $httpCode";
if ($httpCode === 400) {
if (stripos($errorMessage, 'prazo excedido') !== false) {
throw new Exception('Prazo de 89 dias para estorno foi excedido');
}
if (stripos($errorMessage, 'valor inválido') !== false) {
throw new Exception('Valor do estorno excede o disponível para estorno');
}
}
if ($httpCode === 404) {
throw new Exception('Transação original não encontrada');
}
throw new Exception("Erro ao processar estorno: $errorMessage");
}
$data = json_decode($response, true);
echo "Estorno PIX iniciado com sucesso!" . PHP_EOL;
echo "ID da Transação de Estorno: {$data['transactionId']}" . PHP_EOL;
echo "ID Externo Original: {$data['externalId']}" . PHP_EOL;
echo "Valor do Estorno: R$ " . number_format($data['refundValue'], 2, ',', '.') . PHP_EOL;
echo "Status: {$data['status']}" . PHP_EOL;
return $data;
}
// Uso
$token = 'seu_token_aqui';
$transactionId = '7845';
// Estorno parcial
$refund = refundPixPayment(
$token,
$transactionId,
75.00,
'Cliente solicitou devolução de 1 item do pedido'
);
```
## Casos de Uso
### 1. E-commerce - Devolução de Produtos
```typescript theme={null}
class OrderRefundSystem {
constructor(private token: string) {}
async processItemReturn(orderId: string, returnedItems: OrderItem[]) {
// Buscar transação original do pedido
const originalTransaction = await this.getTransactionByOrderId(orderId);
// Calcular valor total a estornar
const refundAmount = returnedItems.reduce(
(sum, item) => sum + (item.price * item.quantity),
0
);
// Verificar se não excede o valor da transação original
const availableForRefund = await this.getAvailableRefundAmount(
originalTransaction.id
);
if (refundAmount > availableForRefund) {
throw new Error(
`Valor solicitado (R$ ${refundAmount.toFixed(2)}) excede o disponível ` +
`para estorno (R$ ${availableForRefund.toFixed(2)})`
);
}
// Gerar descrição do estorno
const itemsDescription = returnedItems
.map(item => `${item.name} (${item.quantity}x)`)
.join(', ');
// Realizar estorno
const refund = await refundPixPayment(
this.token,
originalTransaction.id,
refundAmount,
`Devolução de itens: ${itemsDescription}`
);
// Atualizar status do pedido
await this.updateOrderStatus(orderId, 'PARTIALLY_REFUNDED', refund);
// Notificar cliente
await this.notifyCustomerRefund(orderId, refundAmount);
return refund;
}
async getAvailableRefundAmount(transactionId: string): Promise {
// Buscar transação original e todos os estornos já realizados
const transaction = await this.getTransaction(transactionId);
const existingRefunds = await this.getTransactionRefunds(transactionId);
const totalRefunded = existingRefunds.reduce(
(sum, refund) => sum + refund.value,
0
);
return transaction.value - totalRefunded;
}
}
// Uso
interface OrderItem {
name: string;
price: number;
quantity: number;
}
const refundSystem = new OrderRefundSystem('seu_token_aqui');
const returnedItems: OrderItem[] = [
{ name: 'Camiseta Azul', price: 49.90, quantity: 1 }
];
await refundSystem.processItemReturn('ORDER-12345', returnedItems);
```
### 2. SaaS - Reembolso Proporcional
```python theme={null}
from datetime import datetime, timedelta
from decimal import Decimal
class SubscriptionRefundManager:
"""Gerencia reembolsos proporcionais de assinaturas"""
def __init__(self, token: str):
self.token = token
def calculate_prorated_refund(
self,
payment_date: datetime,
cancellation_date: datetime,
monthly_value: float
) -> float:
"""Calcula reembolso proporcional baseado em dias não utilizados"""
# Calcular dias da mensalidade (30 dias)
billing_period_days = 30
# Calcular dias utilizados
days_used = (cancellation_date - payment_date).days
# Calcular dias não utilizados
days_unused = billing_period_days - days_used
if days_unused <= 0:
return 0.0
# Calcular valor proporcional
daily_rate = Decimal(str(monthly_value)) / Decimal(str(billing_period_days))
refund_amount = float(daily_rate * Decimal(str(days_unused)))
return round(refund_amount, 2)
def process_subscription_cancellation(
self,
subscription_id: str,
transaction_id: str
) -> dict:
"""Processa cancelamento com reembolso proporcional"""
# Buscar dados da assinatura
subscription = self.get_subscription(subscription_id)
# Calcular reembolso proporcional
refund_amount = self.calculate_prorated_refund(
payment_date=subscription['last_payment_date'],
cancellation_date=datetime.now(),
monthly_value=subscription['monthly_value']
)
if refund_amount <= 0:
return {'refund': None, 'message': 'Sem valor a reembolsar'}
# Realizar estorno
refund = refund_pix_payment(
token=self.token,
original_transaction_id=transaction_id,
refund_amount=refund_amount,
reason=f'Cancelamento de assinatura - Reembolso proporcional'
)
# Atualizar status da assinatura
self.update_subscription_status(subscription_id, 'CANCELLED')
return refund
# Uso
manager = SubscriptionRefundManager('seu_token_aqui')
# Cliente pagou R$ 99,00 no dia 01/01 e cancelou no dia 15/01
# Reembolso proporcional: 15 dias não utilizados
refund = manager.process_subscription_cancellation(
subscription_id='SUB-12345',
transaction_id='7845'
)
```
### 3. Marketplace - Compensação por Problemas
```javascript theme={null}
class MarketplaceCompensation {
constructor(token) {
this.token = token;
}
async compensateForIssue(orderId, issueType) {
const order = await this.getOrder(orderId);
const compensationRules = this.getCompensationRules();
// Definir valor da compensação baseado no tipo de problema
const compensationPercent = compensationRules[issueType] || 0;
const compensationAmount = order.value * (compensationPercent / 100);
if (compensationAmount === 0) {
throw new Error('Tipo de problema não elegível para compensação');
}
// Realizar estorno parcial como compensação
const refund = await refundPixPayment(
this.token,
order.transactionId,
compensationAmount,
`Compensação por ${issueType} - ${compensationPercent}% de desconto`
);
// Registrar compensação
await this.recordCompensation(orderId, issueType, compensationAmount);
return refund;
}
getCompensationRules() {
return {
'ATRASO_ENTREGA': 10, // 10% de compensação
'PRODUTO_AVARIADO': 20, // 20% de compensação
'ITEM_FALTANTE': 15, // 15% de compensação
'QUALIDADE_INFERIOR': 25 // 25% de compensação
};
}
}
// Uso
const compensation = new MarketplaceCompensation('seu_token_aqui');
// Produto chegou avariado - compensar com 20%
await compensation.compensateForIssue('ORDER-12345', 'PRODUTO_AVARIADO');
```
## Validações e Regras de Negócio
### Verificar Valor Disponível para Estorno
```typescript theme={null}
async function validateRefundAmount(
transactionId: string,
requestedAmount: number
): Promise {
// Buscar transação original
const transaction = await getTransaction(transactionId);
// Buscar todos os estornos já realizados
const refunds = await getRefundsByTransaction(transactionId);
// Calcular total já estornado
const totalRefunded = refunds.reduce((sum, refund) => sum + refund.value, 0);
// Calcular valor disponível
const availableForRefund = transaction.value - totalRefunded;
// Validar
if (requestedAmount > availableForRefund) {
throw new Error(
`Valor solicitado (R$ ${requestedAmount.toFixed(2)}) excede o disponível ` +
`para estorno (R$ ${availableForRefund.toFixed(2)}). ` +
`Total já estornado: R$ ${totalRefunded.toFixed(2)}`
);
}
return true;
}
```
### Verificar Prazo de Estorno
```python theme={null}
from datetime import datetime, timedelta
def can_refund_transaction(transaction_date: datetime) -> bool:
"""Verifica se a transação ainda está dentro do prazo de estorno"""
max_refund_days = 89
cutoff_date = datetime.now() - timedelta(days=max_refund_days)
if transaction_date < cutoff_date:
days_passed = (datetime.now() - transaction_date).days
raise Exception(
f'Prazo para estorno excedido. '
f'Transação realizada há {days_passed} dias. '
f'Prazo máximo: {max_refund_days} dias.'
)
return True
# Uso
try:
can_refund_transaction(datetime(2024, 1, 1))
print('Transação pode ser estornada')
except Exception as e:
print(f'Erro: {e}')
```
## Monitoramento de Estornos
```typescript theme={null}
class RefundMonitor {
async monitorRefundStatus(refundTransactionId: string, timeout = 60000) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const status = await this.checkRefundStatus(refundTransactionId);
if (status === 'CONFIRMED') {
console.log('Estorno confirmado!');
await this.onRefundConfirmed(refundTransactionId);
return true;
}
if (status === 'ERROR') {
await this.onRefundFailed(refundTransactionId);
throw new Error('Estorno falhou');
}
// Aguardar 3 segundos antes de verificar novamente
await new Promise(resolve => setTimeout(resolve, 3000));
}
throw new Error('Timeout: Estorno não confirmado no tempo esperado');
}
async onRefundConfirmed(refundTransactionId: string) {
// Atualizar banco de dados
// Notificar cliente
// Registrar log
}
async onRefundFailed(refundTransactionId: string) {
// Notificar equipe de suporte
// Registrar incidente
// Criar ticket para análise manual
}
}
```
## Códigos de Resposta
| Código | Descrição | Significado |
| ------ | ------------------------ | ------------------------------------------ |
| `201` | Estorno Criado | Estorno PIX iniciado com sucesso |
| `400` | Valor Inválido | Valor do estorno excede o disponível |
| `400` | Prazo Excedido | Prazo de 89 dias para estorno foi excedido |
| `401` | Token Inválido | Token não fornecido, expirado ou inválido |
| `404` | Transação Não Encontrada | Transação pai não encontrada |
Consulte a [Referência da API](/api-reference/endpoints/pix-refund-in) para detalhes completos dos campos de resposta.
## Boas Práticas
O motivo do estorno é útil para auditoria e análise de métricas.
```javascript theme={null}
// Bom
reason: 'Cliente solicitou cancelamento - produto não atendeu expectativas'
// Ruim
reason: 'Cancelado'
```
Consulte a transação original e estornos anteriores para evitar erros.
Mantenha registro de todos os estornos para evitar ultrapassar o valor original.
Envie email/SMS informando sobre o estorno e prazo para crédito (geralmente instantâneo).
Mantenha um log completo com data, valor, motivo e usuário que solicitou o estorno.
## Observações Importantes
Estornos não podem ser cancelados após iniciados. Certifique-se dos valores antes de processar.
* **Prazo máximo:** 89 dias após o recebimento
* **Valor mínimo:** R\$ 0,01
* **Múltiplos estornos:** Permitidos, desde que a soma não exceda o valor original
## Próximos Passos
Crie cobranças para receber pagamentos
Verifique o saldo após estornos
# Quickstart
Source: https://docs.firebanking.dev/api-reference/guides/quickstart
Integre com a API Fire Banking em 5 minutos
## Pré-requisitos
Antes de começar, você precisa ter:
Certificado X.509 (arquivo `.pem`) vinculado à sua conta
Credenciais OAuth (`clientId` e `clientSecret`)
Solicite suas credenciais e certificado através do [Painel Fire Banking](https://dashboard.firebanking.com.br).
## 1. Configurar Ambiente
Crie um arquivo `.env` com suas credenciais:
```bash theme={null}
FIREBANKING_CLIENT_ID=account-93-seu-id
FIREBANKING_CLIENT_SECRET=sua-senha-secreta
FIREBANKING_API_URL=https://api.public.firebanking.com.br
```
Salve seu certificado como `client-cert.pem` no diretório do projeto.
## 2. Instalar Dependências
```bash Node.js theme={null}
npm install axios dotenv
```
```bash Python theme={null}
pip install requests python-dotenv
```
## 3. Código Completo
O exemplo abaixo autentica, consulta saldo e cria uma cobrança PIX:
```javascript Node.js theme={null}
require('dotenv').config();
const axios = require('axios');
const fs = require('fs');
const API_URL = process.env.FIREBANKING_API_URL;
const certificate = fs.readFileSync('./client-cert.pem', 'utf8');
const encodedCert = encodeURIComponent(certificate);
// 1. Obter token
async function getToken() {
const response = await axios.post(`${API_URL}/api/auth/token`, {
clientId: process.env.FIREBANKING_CLIENT_ID,
clientSecret: process.env.FIREBANKING_CLIENT_SECRET
}, {
headers: {
'Content-Type': 'application/json',
'X-SSL-Client-Cert': encodedCert
}
});
return response.data.access_token;
}
// 2. Consultar saldo
async function getBalance(token) {
const response = await axios.get(`${API_URL}/api/balance`, {
headers: { 'Authorization': `Bearer ${token}` }
});
return response.data;
}
// 3. Criar cobrança PIX
async function createPixCharge(token, value, description, externalId, payer) {
const response = await axios.post(`${API_URL}/api/pix/cash-in`, {
transaction: {
value,
description,
externalId,
expirationTime: 3600, // 1 hora
generateQrCode: true
},
payer: {
fullName: payer.name,
document: payer.document
}
}, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return response.data;
}
// Executar
async function main() {
try {
// Autenticar
console.log('Autenticando...');
const token = await getToken();
console.log('Token obtido com sucesso!');
// Consultar saldo
console.log('\nConsultando saldo...');
const balance = await getBalance(token);
console.log(`Saldo disponível: R$ ${balance.netBalance.toFixed(2)}`);
// Criar cobrança
console.log('\nCriando cobrança PIX...');
const charge = await createPixCharge(token, 100.00, 'Teste de integração', 'ORDER-001', {
name: 'João da Silva',
document: '12345678901'
});
console.log(`\nCobrança criada!`);
console.log(`ID: ${charge.transactionId}`);
console.log(`Status: ${charge.status}`);
console.log(`PIX Copia e Cola: ${charge.pixCode}`);
console.log(`Expira em: ${charge.expirationDate}`);
} catch (error) {
console.error('Erro:', error.response?.data || error.message);
}
}
main();
```
```python Python theme={null}
import os
import urllib.parse
import requests
from dotenv import load_dotenv
load_dotenv()
API_URL = os.getenv('FIREBANKING_API_URL')
# Carregar certificado
with open('client-cert.pem', 'r') as f:
certificate = f.read()
encoded_cert = urllib.parse.quote(certificate)
# 1. Obter token
def get_token():
response = requests.post(f'{API_URL}/api/auth/token',
json={
'clientId': os.getenv('FIREBANKING_CLIENT_ID'),
'clientSecret': os.getenv('FIREBANKING_CLIENT_SECRET')
},
headers={
'Content-Type': 'application/json',
'X-SSL-Client-Cert': encoded_cert
}
)
response.raise_for_status()
return response.json()['access_token']
# 2. Consultar saldo
def get_balance(token):
response = requests.get(f'{API_URL}/api/balance',
headers={'Authorization': f'Bearer {token}'}
)
response.raise_for_status()
return response.json()
# 3. Criar cobrança PIX
def create_pix_charge(token, value, description, external_id, payer_name, payer_document):
response = requests.post(f'{API_URL}/api/pix/cash-in',
json={
'transaction': {
'value': value,
'description': description,
'externalId': external_id,
'expirationTime': 3600,
'generateQrCode': True
},
'payer': {
'fullName': payer_name,
'document': payer_document
}
},
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
)
response.raise_for_status()
return response.json()
# Executar
def main():
try:
# Autenticar
print('Autenticando...')
token = get_token()
print('Token obtido com sucesso!')
# Consultar saldo
print('\nConsultando saldo...')
balance = get_balance(token)
print(f"Saldo disponível: R$ {balance['netBalance']:.2f}")
# Criar cobrança
print('\nCriando cobrança PIX...')
charge = create_pix_charge(
token,
100.00,
'Teste de integração',
'ORDER-001',
'João da Silva',
'12345678901'
)
print(f"\nCobrança criada!")
print(f"ID: {charge['transactionId']}")
print(f"Status: {charge['status']}")
print(f"PIX Copia e Cola: {charge['pixCode']}")
print(f"Expira em: {charge['expirationDate']}")
except requests.exceptions.RequestException as e:
print(f'Erro: {e.response.json() if e.response else e}')
if __name__ == '__main__':
main()
```
## 4. Executar
```bash Node.js theme={null}
node quickstart.js
```
```bash Python theme={null}
python quickstart.py
```
**Saída esperada:**
```
Autenticando...
Token obtido com sucesso!
Consultando saldo...
Saldo disponível: R$ 48734.90
Criando cobrança PIX...
Cobrança criada!
ID: 7845
Status: PENDING
PIX Copia e Cola: 00020126580014br.gov.bcb.pix...
Expira em: 2024-01-20T14:30:00.000Z
```
## 5. Receber Notificações (Webhook)
Configure um endpoint para receber notificações quando o pagamento for confirmado:
```javascript theme={null}
// Express.js
app.post('/webhook/firebanking', (req, res) => {
const { event, transactionId, status, finalAmount } = req.body;
if (event === 'CashIn' && status === 'CONFIRMED') {
console.log(`Pagamento ${transactionId} confirmado: R$ ${finalAmount}`);
// Atualizar pedido no seu sistema
}
res.status(200).send('OK');
});
```
Configure a URL do webhook no [Painel Fire Banking](https://dashboard.firebanking.com.br).
Veja o [Guia de Webhooks](/api-reference/guides/webhooks) para mais detalhes.
## Próximos Passos
Entenda o fluxo de autenticação em detalhes
Explore todas as opções de cobrança PIX
Envie pagamentos PIX
Pague via QR Code PIX
Configure notificações em tempo real
## Troubleshooting
Verifique se:
* O arquivo `client-cert.pem` existe no diretório
* O certificado está sendo enviado URL-encoded
* O header `X-SSL-Client-Cert` está presente
Verifique se:
* As variáveis de ambiente estão configuradas corretamente
* O `clientId` e `clientSecret` estão corretos
* O certificado está vinculado à sua conta
Entre em contato com o suporte Fire Banking para vincular o certificado à sua conta.
O token expira em 30 minutos. Implemente renovação automática:
```javascript theme={null}
// Renovar token antes de expirar
if (tokenExpiresAt < Date.now() + 30000) {
token = await getToken();
}
```
# Transações por Chave PIX
Source: https://docs.firebanking.dev/api-reference/guides/transactions-by-pix-key
Consulte e filtre transações associadas a uma chave PIX específica com paginação
## Visão Geral
Os endpoints de transações por chave PIX permitem consultar o histórico de transações associadas a uma chave PIX específica (CPF, CNPJ, telefone, e-mail ou chave aleatória EVP). São ideais para cenários como:
* **Reconciliação por chave**: verificar todos os recebimentos de um CNPJ ou CPF específico
* **Auditoria**: auditar movimentações de uma chave PIX em um período
* **Monitoramento de recebimentos**: acompanhar pagamentos recebidos via uma chave específica
Comparado ao endpoint geral `/api/transactions`, a diferença principal é o filtro obrigatório por chave PIX e o `size` máximo maior: até **1000 registros por página** (vs. 100 no endpoint geral). A consulta retorna no máximo 1000 resultados no total — se a chave tiver mais transações no período, use filtros de `type`, `status` ou períodos menores para refinar.
## Autenticação
Este endpoint requer um token Bearer válido no header `Authorization`:
```bash theme={null}
Authorization: Bearer
```
O token deve ser obtido através do endpoint [Gerar Token](/api-reference/endpoints/generate-token).
## Tipos de Chave PIX Suportados
| Tipo | Formato | Exemplo |
| --------------------- | ----------------------------------------- | -------------------------------------- |
| CPF | Apenas números (11 dígitos) | `12345678900` |
| CNPJ | Apenas números (14 dígitos) | `12345678000190` |
| Telefone | Formato E.164 com `+` encodado como `%2B` | `%2B5511999999999` |
| E-mail | Endereço de e-mail válido | `joao@example.com` |
| Chave aleatória (EVP) | UUID v4 | `550e8400-e29b-41d4-a716-446655440000` |
Ao usar chaves com caracteres especiais na URL (como `+` no telefone), sempre aplique URL encoding. Nos exemplos de código, `encodeURIComponent` (JavaScript) e `quote(..., safe="")` (Python) fazem isso automaticamente.
***
## Endpoint 1: Listar Transações por Chave PIX
```
GET /api/pix/transactions/pix-key/{pixKey}
```
### Parâmetros
| Parâmetro | Tipo | Obrigatório | Padrão | Descrição |
| ----------- | ------- | ----------- | ------------------- | -------------------------------------------------- |
| `pixKey` | string | ✓ (path) | — | Chave PIX (URL-encoded) |
| `page` | integer | — | `1` | Número da página |
| `size` | integer | — | `20` | Registros por página (máx. **1000**) |
| `status` | string | — | — | `PENDING`, `CONFIRMED` ou `ERROR` |
| `type` | string | — | — | `PAYMENT`, `WITHDRAW`, `REFUND_IN` ou `REFUND_OUT` |
| `startDate` | date | — | Últimos **30 dias** | Data inicial (ISO 8601) |
| `endDate` | date | — | Hoje | Data final (ISO 8601) |
O intervalo entre `startDate` e `endDate` não pode exceder **31 dias**. A consulta retorna no máximo **1000 resultados** — se houver mais transações no período, use filtros adicionais (`type`, `status`, ou períodos menores) para refinar.
### Chave sem resultados
Se a `pixKey` informada não tiver transações no período consultado, a API retorna HTTP 200 com `data` vazio:
```json theme={null}
{
"data": [],
"metadata": { "page": 1, "size": 20, "total": 0, "totalPages": 0, "hasNext": false, "hasPrevious": false }
}
```
### Exemplos
```bash cURL theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/joao%40example.com?status=CONFIRMED&page=1&size=50" \
-H "Authorization: Bearer seu_token_aqui"
```
```javascript Node.js theme={null}
const pixKey = 'joao@example.com';
const params = new URLSearchParams({
status: 'CONFIRMED',
page: '1',
size: '50'
});
const response = await fetch(
`https://api.public.firebanking.com.br/api/pix/transactions/pix-key/${encodeURIComponent(pixKey)}?${params}`,
{
headers: {
'Authorization': 'Bearer seu_token_aqui'
}
}
);
const data = await response.json();
console.log(data);
```
```python Python theme={null}
import requests
from urllib.parse import quote
pix_key = 'joao@example.com'
response = requests.get(
f'https://api.public.firebanking.com.br/api/pix/transactions/pix-key/{quote(pix_key, safe="")}',
params={
'status': 'CONFIRMED',
'page': 1,
'size': 50
},
headers={
'Authorization': 'Bearer seu_token_aqui'
}
)
data = response.json()
print(data)
```
### Exemplo de Resposta
```json theme={null}
{
"data": [
{
"transactionId": "12345",
"externalId": "order-abc123",
"status": "Confirmado",
"operationType": "Pix in",
"movementType": "CREDIT",
"originalAmount": 100.00,
"feeAmount": 1.00,
"finalAmount": 99.00,
"endToEndId": "E00416968202501151030VX5Sx8fIpkY",
"createdAt": "2025-01-15T10:30:00.000Z",
"processedAt": "2025-01-15T10:30:05.000Z",
"counterpart": {
"name": "João Silva",
"document": "***.456.789-**",
"bank": {
"bankISPB": "00000000",
"bankName": "Banco do Brasil",
"bankCode": "001",
"accountBranch": "0001",
"accountNumber": "123456-7"
}
}
}
],
"metadata": {
"page": 1,
"size": 50,
"total": 320,
"totalPages": 7,
"hasNext": true,
"hasPrevious": false
}
}
```
### Paginação
| Campo | Descrição |
| ------------- | ------------------------------------------ |
| `page` | Página atual |
| `size` | Quantidade de registros por página |
| `total` | Total de registros encontrados (máx. 1000) |
| `totalPages` | Total de páginas disponíveis |
| `hasNext` | Indica se existe próxima página |
| `hasPrevious` | Indica se existe página anterior |
Para obter todos os resultados de uma vez, use `size=1000`. Para processar em lotes, navegue pelas páginas enquanto `hasNext` for `true`:
```javascript theme={null}
async function getAllTransactions(pixKey, filters = {}) {
const results = [];
let page = 1;
do {
const params = new URLSearchParams({ ...filters, page, size: 100 });
const response = await fetch(
`https://api.public.firebanking.com.br/api/pix/transactions/pix-key/${encodeURIComponent(pixKey)}?${params}`,
{ headers: { 'Authorization': 'Bearer seu_token_aqui' } }
);
const { data, metadata } = await response.json();
results.push(...data);
if (!metadata.hasNext) break;
page++;
} while (true);
return results;
}
```
***
## Endpoint 2: Consultar Transação por Chave PIX e Identificador
```
GET /api/pix/transactions/pix-key/{pixKey}/{identifier}
```
### Parâmetros
| Parâmetro | Tipo | Obrigatório | Descrição |
| ------------ | ------ | ----------- | ---------------------------------------- |
| `pixKey` | string | ✓ | Chave PIX (URL-encoded) |
| `identifier` | string | ✓ | Identificador da transação (URL-encoded) |
O `identifier` é comparado simultaneamente contra três campos da transação:
* **`endToEndId`** — End-to-End ID do PIX (ex: `E00416968202501151030VX5Sx8fIpkY`)
* **`externalId`** — Identificador externo fornecido na criação da transação
* **`id` numérico** — ID interno da transação na Fire Banking (apenas se o valor for puramente numérico)
Na prática não há ambiguidade: o formato de cada tipo de identificador é único (e2eId começa com `E` seguido de 32 caracteres alfanuméricos; id é puramente numérico; externalId é qualquer string).
### Exemplos
```bash cURL (endToEndId) theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/joao%40example.com/E00416968202501151030VX5Sx8fIpkY" \
-H "Authorization: Bearer seu_token_aqui"
```
```bash cURL (externalId) theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/joao%40example.com/order-abc123" \
-H "Authorization: Bearer seu_token_aqui"
```
```javascript Node.js theme={null}
const pixKey = 'joao@example.com';
const identifier = 'E00416968202501151030VX5Sx8fIpkY';
const response = await fetch(
`https://api.public.firebanking.com.br/api/pix/transactions/pix-key/${encodeURIComponent(pixKey)}/${encodeURIComponent(identifier)}`,
{
headers: {
'Authorization': 'Bearer seu_token_aqui'
}
}
);
if (response.status === 404) {
console.log('Transação não encontrada');
} else {
const transaction = await response.json();
console.log(transaction);
}
```
```python Python theme={null}
import requests
from urllib.parse import quote
pix_key = 'joao@example.com'
identifier = 'E00416968202501151030VX5Sx8fIpkY'
response = requests.get(
f'https://api.public.firebanking.com.br/api/pix/transactions/pix-key/{quote(pix_key, safe="")}/{quote(identifier, safe="")}',
headers={
'Authorization': 'Bearer seu_token_aqui'
}
)
if response.status_code == 404:
print('Transação não encontrada')
else:
transaction = response.json()
print(transaction)
```
### Resposta 200 — Transação Encontrada
```json theme={null}
{
"transactionId": "12345",
"externalId": "order-abc123",
"status": "Confirmado",
"operationType": "Pix in",
"movementType": "CREDIT",
"originalAmount": 100.00,
"feeAmount": 1.00,
"finalAmount": 99.00,
"endToEndId": "E00416968202501151030VX5Sx8fIpkY",
"createdAt": "2025-01-15T10:30:00.000Z",
"processedAt": "2025-01-15T10:30:05.000Z",
"counterpart": {
"name": "João Silva",
"document": "***.456.789-**",
"bank": {
"bankISPB": "00000000",
"bankName": "Banco do Brasil",
"bankCode": "001",
"accountBranch": "0001",
"accountNumber": "123456-7"
}
}
}
```
### Resposta 404 — Não Encontrada
Retornado quando nenhuma transação corresponde ao `identifier` informado para a `pixKey` especificada.
```json theme={null}
{
"statusCode": 404,
"message": "Transação não encontrada"
}
```
***
## Mapeamento de Campos
### Status
| Valor Interno | Valor Retornado |
| ------------- | --------------- |
| `PENDING` | `Pendente` |
| `CONFIRMED` | `Confirmado` |
| `ERROR` | `Error` |
### Tipo de Operação
| Valor Interno | Valor Retornado | Descrição |
| ------------- | --------------- | ---------------------------- |
| `PAYMENT` | `Pix in` | Recebimento via PIX |
| `WITHDRAW` | `Pix out` | Pagamento via PIX |
| `REFUND_IN` | `Refund in` | Estorno solicitado (débito) |
| `REFUND_OUT` | `Refund out` | Devolução recebida (crédito) |
### Tipo de Movimento
| Tipo de Operação | Movimento |
| ---------------- | --------- |
| `Pix in` | `CREDIT` |
| `Pix out` | `DEBIT` |
| `Refund in` | `DEBIT` |
| `Refund out` | `CREDIT` |
***
## Diferenças em Relação a `/api/transactions`
| Aspecto | `/api/transactions` | `/api/pix/transactions/pix-key/{pixKey}` |
| ---------------------------------- | -------------------------- | ---------------------------------------- |
| Filtro principal | Toda a conta | Por chave PIX específica |
| Máx. `size` por página | 100 | **1000** |
| Máx. resultados totais | Sem limite | **1000** |
| Default `startDate` | Últimos 31 dias | Últimos **30 dias** |
| Chave inexistente / sem resultados | 200 com lista vazia | 200 com lista vazia |
| Busca por `externalId` | Query param `?externalId=` | Path param `/{identifier}` |
| Busca por `endToEndId` | Query param `?endToEndId=` | Path param `/{identifier}` |
***
## Casos de Uso
Verificar todos os recebimentos confirmados de um CNPJ em um período:
```bash theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/12345678000190?type=PAYMENT&status=CONFIRMED&startDate=2025-01-01&endDate=2025-01-31&size=1000" \
-H "Authorization: Bearer seu_token"
```
Confirmar se um pagamento PIX específico foi recebido pela chave:
```bash theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/joao%40example.com/E00416968202501151030VX5Sx8fIpkY" \
-H "Authorization: Bearer seu_token"
```
Buscar todas as movimentações de um CNPJ nos últimos 30 dias (padrão):
```bash theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/12345678000190?size=1000" \
-H "Authorization: Bearer seu_token"
```
Monitorar recebimentos de uma chave de telefone em uma semana específica:
```bash theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/pix/transactions/pix-key/%2B5511999999999?type=PAYMENT&startDate=2025-01-13&endDate=2025-01-19" \
-H "Authorization: Bearer seu_token"
```
***
## Códigos de Erro
| Código | Descrição |
| ------ | ---------------------------------------------------------------------- |
| `400` | Parâmetros inválidos, datas invertidas, ou intervalo excede 31 dias |
| `401` | Token não fornecido ou inválido |
| `404` | Transação não encontrada (apenas no endpoint `/{pixKey}/{identifier}`) |
***
## Próximos Passos
Consulte transações gerais da conta com filtros avançados
Verifique o status detalhado de uma transação específica
Receba notificações automáticas quando transações chegarem
# Buscar Transações
Source: https://docs.firebanking.dev/api-reference/guides/transactions-search
Consulte e filtre transações PIX da sua conta com paginação
## Visão Geral
O endpoint de busca de transações permite consultar o histórico de transações PIX da sua conta com diversos filtros e paginação. Os dados são retornados em um formato amigável, com status e tipos traduzidos para português e valores em reais.
## Autenticação
Este endpoint requer um token Bearer válido no header `Authorization`:
```bash theme={null}
Authorization: Bearer
```
O token deve ser obtido através do endpoint [Gerar Token](/api-reference/endpoints/generate-token).
## Parâmetros de Consulta
| Parâmetro | Tipo | Descrição |
| ------------ | ------- | ----------------------------------------------------------------- |
| `page` | integer | Número da página (padrão: 1) |
| `size` | integer | Registros por página (padrão: 20, máximo: 100) |
| `status` | string | Filtro por status: `PENDING`, `CONFIRMED`, `ERROR` |
| `type` | string | Filtro por tipo: `PAYMENT`, `WITHDRAW`, `REFUND_IN`, `REFUND_OUT` |
| `startDate` | date | Data inicial (ISO 8601). Padrão: últimos 31 dias |
| `endDate` | date | Data final (ISO 8601). Padrão: hoje |
| `externalId` | string | Filtro por seu identificador externo |
| `endToEndId` | string | Filtro por End-to-End ID do PIX |
O intervalo entre `startDate` e `endDate` não pode exceder **31 dias**.
## Mapeamento de Campos
### Status
Os status internos são traduzidos para o formato público:
| Valor Interno | Valor Retornado |
| ------------- | --------------- |
| `PENDING` | `Pendente` |
| `CONFIRMED` | `Confirmado` |
| `ERROR` | `Error` |
### Tipo de Operação
Os tipos de transação são traduzidos para português:
| Valor Interno | Valor Retornado | Descrição |
| ------------- | --------------- | ---------------------------- |
| `PAYMENT` | `Pix in` | Recebimento via PIX |
| `WITHDRAW` | `Pix out` | Pagamento via PIX |
| `REFUND_IN` | `Refund in` | Estorno solicitado (débito) |
| `REFUND_OUT` | `Refund out` | Devolução recebida (crédito) |
### Tipo de Movimento
Indica se a transação é entrada ou saída na conta:
| Tipo de Operação | Movimento |
| ---------------- | --------- |
| `Pix in` | `CREDIT` |
| `Pix out` | `DEBIT` |
| `Refund in` | `DEBIT` |
| `Refund out` | `CREDIT` |
### Valores
Todos os valores monetários são retornados em **reais** com 2 casas decimais:
* `originalAmount`: Valor original da transação
* `feeAmount`: Taxa aplicada
* `finalAmount`: Valor final (original ± taxa)
### Mascaramento de Documento
Por segurança, documentos de contrapartes são mascarados:
* **CPF**: `123.456.789-00` → `***.456.789-**`
* **CNPJ**: `12.345.678/0001-90` → `**.345.678/****-**`
## Exemplo de Uso
### Buscar todas as transações confirmadas
```bash cURL theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/transactions?status=CONFIRMED&page=1&size=10" \
-H "Authorization: Bearer seu_token_aqui"
```
```javascript Node.js theme={null}
const response = await fetch(
'https://api.public.firebanking.com.br/api/transactions?status=CONFIRMED&page=1&size=10',
{
headers: {
'Authorization': 'Bearer seu_token_aqui'
}
}
);
const data = await response.json();
console.log(data);
```
```python Python theme={null}
import requests
response = requests.get(
'https://api.public.firebanking.com.br/api/transactions',
params={
'status': 'CONFIRMED',
'page': 1,
'size': 10
},
headers={
'Authorization': 'Bearer seu_token_aqui'
}
)
data = response.json()
print(data)
```
### Buscar transações por período
```bash cURL theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/transactions?startDate=2025-01-01&endDate=2025-01-15&type=PAYMENT" \
-H "Authorization: Bearer seu_token_aqui"
```
```javascript Node.js theme={null}
const params = new URLSearchParams({
startDate: '2025-01-01',
endDate: '2025-01-15',
type: 'PAYMENT'
});
const response = await fetch(
`https://api.public.firebanking.com.br/api/transactions?${params}`,
{
headers: {
'Authorization': 'Bearer seu_token_aqui'
}
}
);
```
### Buscar por identificador específico
```bash theme={null}
# Por externalId (seu identificador)
curl -X GET "https://api.public.firebanking.com.br/api/transactions?externalId=order-12345" \
-H "Authorization: Bearer seu_token_aqui"
# Por endToEndId (ID do PIX)
curl -X GET "https://api.public.firebanking.com.br/api/transactions?endToEndId=E12345678901234567890123456789012" \
-H "Authorization: Bearer seu_token_aqui"
```
## Exemplo de Resposta
```json theme={null}
{
"data": [
{
"transactionId": "12345",
"externalId": "order-abc123",
"status": "Confirmado",
"operationType": "Pix in",
"movementType": "CREDIT",
"originalAmount": 100.00,
"feeAmount": 1.00,
"finalAmount": 99.00,
"endToEndId": "E12345678901234567890123456789012",
"createdAt": "2025-01-15T10:30:00.000Z",
"processedAt": "2025-01-15T10:30:05.000Z",
"counterpart": {
"name": "João Silva",
"document": "***.456.789-**",
"bank": {
"bankISPB": "00000000",
"bankName": "Banco do Brasil",
"bankCode": "001",
"accountBranch": "0001",
"accountNumber": "123456-7"
}
}
}
],
"metadata": {
"page": 1,
"size": 20,
"total": 150,
"totalPages": 8,
"hasNext": true,
"hasPrevious": false
}
}
```
## Paginação
A resposta inclui metadados de paginação para facilitar a navegação:
| Campo | Descrição |
| ------------- | ---------------------------------- |
| `page` | Página atual |
| `size` | Quantidade de registros por página |
| `total` | Total de registros encontrados |
| `totalPages` | Total de páginas disponíveis |
| `hasNext` | Indica se existe próxima página |
| `hasPrevious` | Indica se existe página anterior |
### Navegação entre páginas
```javascript theme={null}
// Primeira página
const page1 = await fetchTransactions({ page: 1, size: 20 });
if (page1.metadata.hasNext) {
// Próxima página
const page2 = await fetchTransactions({ page: 2, size: 20 });
}
```
## Casos de Uso Comuns
```bash theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/transactions?type=PAYMENT&status=CONFIRMED&startDate=2025-01-15&endDate=2025-01-15" \
-H "Authorization: Bearer seu_token"
```
```bash theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/transactions?status=PENDING" \
-H "Authorization: Bearer seu_token"
```
```bash theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/transactions?type=REFUND_IN" \
-H "Authorization: Bearer seu_token"
```
```bash theme={null}
curl -X GET "https://api.public.firebanking.com.br/api/transactions?externalId=MEU-PEDIDO-123" \
-H "Authorization: Bearer seu_token"
```
## Códigos de Erro
| Código | Descrição |
| ------ | --------------------------------------------------------- |
| `400` | Parâmetros inválidos ou intervalo de datas excede 31 dias |
| `401` | Token não fornecido ou inválido |
## Próximos Passos
Consulte o status detalhado de uma transação específica
Reenvie notificações de transações para seu sistema
Filtre transações por chave PIX específica com paginação
# Reenvio de Webhooks
Source: https://docs.firebanking.dev/api-reference/guides/webhook-resend
Reenvie webhooks de transações manualmente quando necessário
## Visão Geral
O endpoint de **Reenvio de Webhook** permite que você solicite o reenvio manual de notificações de transações específicas. Isso é útil em cenários onde:
* Seu servidor estava indisponível quando o webhook original foi enviado
* Você precisa reprocessar uma transação específica
* Deseja testar a integração com uma URL diferente temporariamente
Este endpoint não altera a configuração de webhook da sua conta. A URL fornecida é usada apenas para o reenvio específico.
***
## Funcionamento
### Identificação da Transação
O endpoint aceita três tipos de identificadores:
| Tipo | Descrição | Escopo |
| ----------------- | ------------------------------------------------------------------------ | ------------------- |
| **ID Numérico** | ID interno da transação (campo `transactionId` nos webhooks) | Global |
| **ID Externo** | Identificador fornecido por você na criação (campo `externalId`) | Único por conta |
| **End-to-End ID** | Identificador PIX do BACEN (campo `endToEndId`, formato: E/D + 32 chars) | Único por transação |
O sistema busca simultaneamente por todos os tipos de identificador na sua conta. Na prática não há ambiguidade: id numérico é puramente dígitos; e2eId começa com 'E' ou 'D' seguido de 32 caracteres alfanuméricos; externalId é qualquer string fornecida por você.
**Qual identificador usar?** Use o `transactionId` numérico retornado pela Fire Banking, o `externalId` que você forneceu na criação da transação, ou o `endToEndId` do PIX recebido nos webhooks. Todos são igualmente válidos.
### Processamento Síncrono
O reenvio de webhook é processado de forma **síncrona**. Isso significa que:
* A requisição aguarda o envio do webhook ser concluído
* O resultado é comunicado via HTTP status code (200, 502, 504)
* O tempo de resposta depende da latência do seu servidor (timeout: 10s)
```mermaid theme={null}
flowchart TD
A[Requisição de Reenvio] --> B{URL fornecida?}
B -->|Sim| C[Usa URL temporária]
B -->|Não| D{Webhook configurado?}
D -->|Sim| E[Usa URL configurada]
D -->|Não| F[Erro 400: Sem URL]
C --> G[Envia Webhook HTTP]
E --> G
G --> H{Resposta do servidor}
H -->|2xx| I[HTTP 200: Sucesso]
H -->|4xx/5xx| J[HTTP 502: Bad Gateway]
H -->|Timeout| K[HTTP 504: Gateway Timeout]
I --> L[Registra log de auditoria]
J --> L
K --> L
L --> M[Retorna resultado]
```
Diferentemente dos webhooks automáticos (que utilizam filas com retry), o reenvio manual é executado imediatamente e retorna o resultado na mesma requisição.
***
## Casos de Uso
### 1. Reenvio para URL Configurada
Se você já tem um webhook configurado na sua conta, basta chamar o endpoint sem body:
```bash theme={null}
curl -X POST https://api.public.firebanking.com.br/api/resend-webhook/external-teste-001 \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json"
```
### 2. Reenvio com URL Temporária
Para testar com uma URL diferente ou reenviar para um endpoint de contingência:
```bash theme={null}
curl -X POST https://api.public.firebanking.com.br/api/resend-webhook/external-teste-001 \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://meu-servidor-backup.com/webhooks/firebanking"
}'
```
A URL temporária **não é persistida**. O próximo webhook automático será enviado para a URL configurada na conta.
***
## Resposta
### Sucesso (200)
Webhook enviado com sucesso para a URL de destino.
```json theme={null}
{
"message": "Webhook resent successfully",
"webhookLogId": 12345,
"sentAt": "2024-01-15T10:30:00.000Z",
"statusCode": 200
}
```
### Erro: Sem URL Configurada (400)
```json theme={null}
{
"statusCode": 400,
"message": "No webhook configured and no override URL provided",
"error": "Bad Request"
}
```
### Erro: Transação Não Encontrada (404)
```json theme={null}
{
"statusCode": 404,
"message": "Transaction not found",
"error": "Not Found"
}
```
### Erro: Destino Retornou Erro (502)
O servidor de destino retornou um erro (4xx ou 5xx) ou houve falha de conexão.
```json theme={null}
{
"statusCode": 502,
"message": "Webhook failed with status 500",
"webhookLogId": 12345,
"sentAt": "2024-01-15T10:30:00.000Z"
}
```
Mesmo em caso de erro, o webhook é registrado no log de auditoria. Use o `webhookLogId` para rastreamento.
### Erro: Timeout (504)
O servidor de destino não respondeu dentro do tempo limite (10 segundos).
```json theme={null}
{
"statusCode": 504,
"message": "Timeout after 10000ms",
"webhookLogId": 12345,
"sentAt": "2024-01-15T10:30:00.000Z"
}
```
Se você está recebendo timeouts frequentes, verifique se seu servidor está respondendo em menos de 10 segundos.
***
## Rate Limiting
Este endpoint possui rate limiting de **60 requisições por minuto** por conta para evitar abusos.
Se o limite for excedido, você receberá um erro `429 Too Many Requests`:
```json theme={null}
{
"statusCode": 429,
"message": "Too Many Requests"
}
```
***
## Auditoria
Todos os reenvios manuais são registrados para fins de auditoria e rastreabilidade:
| Informação | Descrição |
| ------------- | --------------------------------------------------- |
| Tipo de envio | Marcado como reenvio manual |
| URL utilizada | Registra se foi usada URL temporária ou configurada |
| Resultado | Status HTTP e tempo de resposta |
| Identificador | ID único do log para rastreamento |
Use o `webhookLogId` retornado na resposta para correlacionar com logs de suporte se necessário.
***
## Exemplos de Integração
```javascript Node.js theme={null}
const axios = require('axios');
async function resendWebhook(transactionId, overrideUrl = null) {
const config = {
headers: {
'Authorization': `Bearer ${process.env.FIREBANKING_TOKEN}`,
'Content-Type': 'application/json'
}
};
const body = overrideUrl ? { url: overrideUrl } : {};
try {
const response = await axios.post(
`https://api.public.firebanking.com.br/api/resend-webhook/${transactionId}`,
body,
config
);
console.log('Webhook reenviado:', response.data);
return response.data;
} catch (error) {
console.error('Erro ao reenviar webhook:', error.response?.data);
throw error;
}
}
// Uso
resendWebhook('external-teste-001');
resendWebhook('external-teste-001', 'https://backup.meusite.com/webhook'); // Com URL temporária
```
```python Python theme={null}
import requests
import os
def resend_webhook(transaction_id: str, override_url: str = None):
headers = {
'Authorization': f'Bearer {os.environ["FIREBANKING_TOKEN"]}',
'Content-Type': 'application/json'
}
body = {'url': override_url} if override_url else {}
response = requests.post(
f'https://api.public.firebanking.com.br/api/resend-webhook/{transaction_id}',
json=body,
headers=headers
)
response.raise_for_status()
return response.json()
# Uso
result = resend_webhook('external-teste-001')
print(f"Webhook reenviado: {result}")
# Com URL temporária
result = resend_webhook('external-teste-001', 'https://backup.meusite.com/webhook')
```
```csharp C# theme={null}
using System.Net.Http;
using System.Text;
using System.Text.Json;
public class FireBankingClient
{
private readonly HttpClient _client;
private readonly string _token;
public FireBankingClient(string token)
{
_client = new HttpClient();
_token = token;
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_token}");
}
public async Task ResendWebhookAsync(
string transactionId,
string overrideUrl = null)
{
var url = $"https://api.public.firebanking.com.br/api/resend-webhook/{transactionId}";
var body = overrideUrl != null
? JsonSerializer.Serialize(new { url = overrideUrl })
: "{}";
var content = new StringContent(body, Encoding.UTF8, "application/json");
var response = await _client.PostAsync(url, content);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize(json);
}
}
```
***
## Próximos Passos
Entenda como os webhooks funcionam na Fire Banking
Guia completo de implementação de webhooks
# CashIn
Source: https://docs.firebanking.dev/api-reference/guides/webhooks/cash-in
Evento de recebimento PIX confirmado
## Visão Geral
O evento **CashIn** é enviado quando um pagamento PIX é **recebido** com sucesso na sua conta. Este é o evento mais comum e indica que o dinheiro está disponível.
O `movementType` para CashIn é sempre `CREDIT`, indicando entrada de recursos na conta.
| Campo | Valor |
| -------------- | ---------------------------- |
| `event` | `CashIn` |
| `movementType` | `CREDIT` |
| Significado | Dinheiro entrou na sua conta |
***
## Payload Completo
```json theme={null}
{
"event": "CashIn",
"status": "CONFIRMED",
"transactionType": "PIX",
"movementType": "CREDIT",
"transactionId": "12345",
"externalId": "PIX-5482123298-EJUYFSMU1UU",
"endToEndId": "E00416968202512111942rjzxxzSSTD9",
"pixKey": "1ff6ce09-4244-44d5-aa8f-1fe69f8986a9",
"feeAmount": 0.01,
"originalAmount": 0.5,
"finalAmount": 0.49,
"processingDate": "2025-12-11T19:42:04.080Z",
"errorCode": null,
"errorMessage": null,
"counterpart": {
"name": "Carlos Oliveira",
"document": "*.345.678-**",
"bank": {
"bankISPB": null,
"bankName": null,
"bankCode": null,
"accountBranch": null,
"accountNumber": null
}
},
"metadata": {}
}
```
***
## Campos Específicos do CashIn
O CashIn inclui o objeto `counterpart` com dados do **pagador** (quem enviou o PIX).
Dados do **pagador** (quem enviou o PIX para você).
Nome completo do pagador conforme cadastrado no banco de origem.
CPF/CNPJ do pagador (parcialmente mascarado por questões de privacidade).
**Exemplo:** `"*.345.678-**"`
Dados bancários do pagador.
Código ISPB do banco do pagador (identificador único no Sistema de Pagamentos Brasileiro).
Nome do banco do pagador.
Código COMPE do banco (ex: "001" para Banco do Brasil, "260" para Nubank).
Agência do pagador (quando disponível).
Número da conta do pagador (quando disponível).
***
## Cálculo do Valor Final
Para eventos de `CREDIT` (entrada), o valor final é calculado como:
```
finalAmount = originalAmount - feeAmount
```
A taxa (`feeAmount`) é descontada do valor original. Se o pagador enviou R$ 100,00 e a taxa é R$ 0,50, você receberá R\$ 99,50.
***
## Casos de Uso
### 1. Pagamento de Pedido
```javascript theme={null}
async function handleCashIn(payload) {
// Usar externalId para correlacionar com o pedido
const orderId = payload.externalId.replace('PIX-', '');
await orderService.markAsPaid({
orderId,
transactionId: payload.transactionId,
amount: payload.finalAmount,
paidAt: payload.processingDate
});
// Notificar cliente
await notificationService.sendPaymentConfirmation(orderId);
}
```
### 2. Recarga de Saldo
```javascript theme={null}
async function handleCashIn(payload) {
await walletService.credit({
userId: payload.metadata.userId,
amount: payload.finalAmount,
reference: payload.transactionId
});
}
```
***
## Fluxo Típico
```mermaid theme={null}
sequenceDiagram
participant Pagador
participant Fire Banking
participant SeuSistema
Pagador->>Fire Banking: Envia PIX
Fire Banking->>Fire Banking: Processa transação
Fire Banking->>SeuSistema: Webhook CashIn
SeuSistema->>SeuSistema: Valida autenticação
SeuSistema->>SeuSistema: Verifica idempotência
SeuSistema-->>Fire Banking: HTTP 200 OK
SeuSistema->>SeuSistema: Processa pagamento
```
***
## Próximos Passos
Aprenda a gerar cobranças PIX
Entenda o evento de estorno
# CashInReversal
Source: https://docs.firebanking.dev/api-reference/guides/webhooks/cash-in-reversal
Evento de estorno de recebimento PIX
## Visão Geral
O evento **CashInReversal** é enviado quando você inicia um **estorno** de um PIX recebido anteriormente, devolvendo o valor ao pagador original. Este evento ocorre quando você chama a API de Refund-In.
O `movementType` para CashInReversal é `DEBIT`, pois você está devolvendo dinheiro que havia entrado na sua conta.
| Campo | Valor |
| -------------- | ----------------------------------------- |
| `event` | `CashInReversal` |
| `movementType` | `DEBIT` |
| Significado | Você devolveu dinheiro que havia recebido |
***
## Payload Completo
```json theme={null}
{
"event": "CashInReversal",
"status": "CONFIRMED",
"transactionType": "PIX",
"movementType": "DEBIT",
"transactionId": "11111",
"externalId": "refund-678689ca-3e16-4f5e-a08f-09a984a97781",
"endToEndId": "D07136847202512112011O5222ZRBI5A",
"pixKey": null,
"feeAmount": 0.01,
"originalAmount": 0.3,
"finalAmount": 0.31,
"processingDate": "2025-12-11T20:11:13.289Z",
"errorCode": null,
"errorMessage": null,
"metadata": {},
"parentTransaction": {
"transactionId": "12345",
"externalId": "PIX-5482123298-EJUYFSMU1UU",
"endToEndId": "E00416968202512111942rjzxxzSSTD9",
"processingDate": "2025-12-11T19:42:04.080Z",
"wasTotalRefunded": false,
"remainingAmountForRefund": 0.2,
"metadata": {},
"counterpart": {
"name": "Carlos Oliveira",
"document": "*.345.678-**",
"bank": {
"bankISPB": null,
"bankName": null,
"bankCode": null,
"accountBranch": null,
"accountNumber": null
}
}
}
}
```
***
## Campos Específicos do CashInReversal
O CashInReversal inclui o objeto `parentTransaction` com dados da transação original que está sendo estornada.
### parentTransaction
Dados da transação **PIX In original** que está sendo estornada.
ID numérico da transação PIX In original (retornado como string).
ID externo da transação original.
ID End-to-End da transação original.
Data de processamento da transação original.
Indica se o valor total da transação original foi estornado.
* `true`: Estorno total (não pode estornar mais)
* `false`: Estorno parcial (ainda pode estornar o restante)
Valor restante que ainda pode ser estornado (em reais).
**Exemplo:** `0.2` (ainda pode estornar R\$ 0,20)
Dados do pagador original que receberá o estorno.
***
## Estorno Total vs Parcial
### Estorno Total
Quando você devolve 100% do valor recebido:
```json theme={null}
{
"parentTransaction": {
"wasTotalRefunded": true,
"remainingAmountForRefund": 0
}
}
```
### Estorno Parcial
Quando você devolve apenas parte do valor:
```json theme={null}
{
"parentTransaction": {
"wasTotalRefunded": false,
"remainingAmountForRefund": 0.2
}
}
```
Você pode fazer múltiplos estornos parciais até que `wasTotalRefunded` seja `true`.
***
## Casos de Uso
### 1. Devolução de Pagamento Duplicado
```javascript theme={null}
async function handleCashInReversal(payload) {
const refundId = payload.transactionId;
const originalOrderId = payload.parentTransaction.externalId;
await refundService.markAsCompleted({
refundId,
originalOrderId,
amount: payload.originalAmount,
completedAt: payload.processingDate
});
// Notificar cliente sobre a devolução
await notificationService.sendRefundConfirmation({
orderId: originalOrderId,
amount: payload.originalAmount
});
}
```
### 2. Controle de Estornos Parciais
```javascript theme={null}
async function handleCashInReversal(payload) {
const { parentTransaction } = payload;
await refundService.updateStatus({
originalTransactionId: parentTransaction.transactionId,
totalRefunded: !parentTransaction.wasTotalRefunded
? false
: true,
remainingAmount: parentTransaction.remainingAmountForRefund
});
if (parentTransaction.wasTotalRefunded) {
console.log('Transação totalmente estornada');
} else {
console.log(`Ainda disponível para estorno: R$ ${parentTransaction.remainingAmountForRefund}`);
}
}
```
***
## Fluxo Típico
```mermaid theme={null}
sequenceDiagram
participant SeuSistema
participant Fire Banking
participant PagadorOriginal
Note over SeuSistema,PagadorOriginal: Transação original (CashIn)
PagadorOriginal->>Fire Banking: PIX recebido anteriormente
Fire Banking->>SeuSistema: Webhook CashIn
Note over SeuSistema,PagadorOriginal: Estorno (CashInReversal)
SeuSistema->>Fire Banking: POST /api/pix/refund-in/{id}
Fire Banking->>PagadorOriginal: Devolve PIX
Fire Banking->>SeuSistema: Webhook CashInReversal
SeuSistema-->>Fire Banking: HTTP 200 OK
```
***
## Próximos Passos
Aprenda a estornar recebimentos
Entenda o evento de recebimento
# CashOut
Source: https://docs.firebanking.dev/api-reference/guides/webhooks/cash-out
Evento de envio PIX confirmado
## Visão Geral
O evento **CashOut** é enviado quando um pagamento PIX é **enviado** com sucesso da sua conta para outra conta. Indica que a transferência foi completada.
Este evento é disparado tanto para pagamentos via chave PIX (`/api/pix/cash-out`) quanto para pagamentos via QR Code (`/api/pix/cash-out-qrcode`).
O `movementType` para CashOut é sempre `DEBIT`, indicando saída de recursos da conta.
| Campo | Valor |
| -------------- | -------------------------- |
| `event` | `CashOut` |
| `movementType` | `DEBIT` |
| Significado | Dinheiro saiu da sua conta |
***
## Payload Completo
```json theme={null}
{
"event": "CashOut",
"status": "CONFIRMED",
"transactionType": "PIX",
"movementType": "DEBIT",
"transactionId": "67890",
"externalId": "PIX-OUT-5483571657-OWUJDUDVDO",
"endToEndId": "E071368472025121120065P1T3N1CS1A",
"pixKey": "07646173380",
"feeAmount": 0.01,
"originalAmount": 0.30,
"finalAmount": 0.31,
"processingDate": "2025-12-11T20:06:12.117Z",
"errorCode": null,
"errorMessage": null,
"counterpart": {
"name": "Ana Costa",
"document": "*.765.432-**",
"bank": {
"bankISPB": null,
"bankName": null,
"bankCode": "260",
"accountBranch": null,
"accountNumber": null
}
},
"metadata": {}
}
```
***
## Campos Específicos do CashOut
O CashOut inclui o objeto `counterpart` com dados do **recebedor** (quem recebeu o PIX).
Dados do **recebedor** (quem recebeu o PIX que você enviou).
Nome completo do recebedor conforme cadastrado no banco de destino.
CPF/CNPJ do recebedor (parcialmente mascarado por questões de privacidade).
**Exemplo:** `"*.765.432-**"`
Dados bancários do recebedor.
Código COMPE do banco do recebedor.
**Exemplo:** `"260"` (Nubank)
Código ISPB do banco do recebedor.
Nome do banco do recebedor.
***
## Cálculo do Valor Final
Para eventos de `DEBIT` (saída), o valor final é calculado como:
```
finalAmount = originalAmount + feeAmount
```
A taxa (`feeAmount`) é somada ao valor original. Se você enviou R$ 100,00 e a taxa é R$ 0,50, o débito total na sua conta será R\$ 100,50.
***
## Casos de Uso
### 1. Pagamento a Fornecedor
```javascript theme={null}
async function handleCashOut(payload) {
const paymentId = payload.externalId.replace('PIX-OUT-', '');
await paymentService.markAsCompleted({
paymentId,
transactionId: payload.transactionId,
endToEndId: payload.endToEndId,
completedAt: payload.processingDate
});
// Notificar equipe financeira
await notificationService.sendPaymentCompleted(paymentId);
}
```
### 2. Saque de Cliente
```javascript theme={null}
async function handleCashOut(payload) {
await withdrawalService.confirm({
withdrawalId: payload.externalId,
transactionId: payload.transactionId,
amount: payload.originalAmount,
fee: payload.feeAmount
});
}
```
***
## Fluxo Típico
```mermaid theme={null}
sequenceDiagram
participant SeuSistema
participant Fire Banking
participant Recebedor
SeuSistema->>Fire Banking: POST /api/pix/payment
Fire Banking->>Fire Banking: Valida e processa
Fire Banking->>Recebedor: Transfere PIX
Recebedor-->>Fire Banking: Confirmação
Fire Banking->>SeuSistema: Webhook CashOut
SeuSistema-->>Fire Banking: HTTP 200 OK
```
***
## Tratamento de Erros
Quando um CashOut falha, você receberá o webhook com `status: "ERROR"`:
```json theme={null}
{
"event": "CashOut",
"status": "ERROR",
"errorCode": "INVALID_PIX_KEY",
"errorMessage": "Chave PIX não encontrada ou inválida",
...
}
```
Quando `status` é `ERROR`, o valor **não foi debitado** da sua conta. Trate o erro e informe o usuário.
***
## Próximos Passos
Aprenda a enviar pagamentos PIX
Pague via QR Code PIX
Entenda o evento de devolução
# CashOutReversal
Source: https://docs.firebanking.dev/api-reference/guides/webhooks/cash-out-reversal
Evento de devolução de PIX enviado
## Visão Geral
O evento **CashOutReversal** é enviado quando você **recebe uma devolução** de um PIX que enviou anteriormente. Isso pode ocorrer quando:
* O recebedor devolve o valor voluntariamente
* Há um problema com a transação original (dados inválidos, conta encerrada, etc.)
* O banco destino rejeita a transação
O `movementType` para CashOutReversal é `CREDIT`, pois você está recebendo de volta dinheiro que havia saído da sua conta.
| Campo | Valor |
| -------------- | ------------------------------------------------ |
| `event` | `CashOutReversal` |
| `movementType` | `CREDIT` |
| Significado | Você recebeu de volta dinheiro que havia enviado |
***
## Payload Completo
```json theme={null}
{
"event": "CashOutReversal",
"status": "CONFIRMED",
"transactionType": "PIX",
"movementType": "CREDIT",
"transactionId": "22222",
"externalId": null,
"endToEndId": "D18236120202512112009s0018351d9f",
"pixKey": "07646173380",
"feeAmount": 0.01,
"originalAmount": 0.08,
"finalAmount": 0.07,
"processingDate": "2025-12-11T20:09:27.786Z",
"errorCode": null,
"errorMessage": null,
"metadata": {
"refund": {
"value": 8,
"originalValue": 31000,
"referenceTransactionId": 917561
},
"provider": "hyperwallet",
"counterpart": {
"bankCode": "260",
"bankIspb": "18236120",
"bankName": "NU PAGAMENTOS S.A. - INSTITUIÇÃO DE PAGAMENTO"
},
"webhookEvent": "PixOutReversalExternal",
"originatedFrom": "WEBHOOK_DIRECT"
},
"parentTransaction": {
"transactionId": "67890",
"externalId": "PIX-OUT-5483571657-OWUJDUDVDO",
"endToEndId": "E071368472025121120065P1T3N1CS1A",
"processingDate": "2025-12-11T20:06:12.117Z",
"wasTotalRefunded": false,
"remainingAmountForRefund": 0.22,
"metadata": {},
"counterpart": {
"name": "Ana Costa",
"document": "*.765.432-**",
"bank": {
"bankISPB": null,
"bankName": null,
"bankCode": "260",
"accountBranch": null,
"accountNumber": null
}
}
}
}
```
***
## Campos Específicos do CashOutReversal
O CashOutReversal inclui campos adicionais no `metadata` e o objeto `parentTransaction`.
### metadata.refund
Detalhes da devolução recebida.
Valor devolvido **em centavos**.
**Exemplo:** `8` (R\$ 0,08)
Valor original da transação **em centavos**.
**Exemplo:** `31000` (R\$ 310,00)
ID interno de referência da transação original no provedor.
### parentTransaction
Dados da transação **PIX Out original** que foi devolvida.
ID numérico da transação PIX Out original (retornado como string).
ID externo que você forneceu ao criar o PIX Out.
Indica se o valor total foi devolvido.
* `true`: Devolução total
* `false`: Devolução parcial
Valor restante que ainda pode ser devolvido (em reais).
Dados do recebedor original que devolveu o PIX.
***
## Diferença: CashInReversal vs CashOutReversal
| Aspecto | CashInReversal | CashOutReversal |
| ----------------- | ------------------------ | ------------------------------ |
| **Quem inicia** | Você (via API Refund-In) | O recebedor ou o banco destino |
| **Direção** | Você → Pagador original | Recebedor → Você |
| **movementType** | `DEBIT` (saída) | `CREDIT` (entrada) |
| **Quando ocorre** | Você decide devolver | Você recebe de volta |
***
## Casos de Uso
### 1. Devolução Recebida
```javascript theme={null}
async function handleCashOutReversal(payload) {
const { parentTransaction, metadata } = payload;
// Creditar o valor devolvido no saldo
await balanceService.credit({
amount: payload.finalAmount,
reference: payload.transactionId,
originalPaymentId: parentTransaction.transactionId
});
// Atualizar status do pagamento original
await paymentService.markAsRefunded({
paymentId: parentTransaction.externalId,
refundAmount: payload.originalAmount,
wasFullRefund: parentTransaction.wasTotalRefunded
});
// Notificar equipe financeira
await notificationService.sendRefundReceived({
originalAmount: metadata.refund.originalValue / 100,
refundAmount: metadata.refund.value / 100
});
}
```
### 2. Tratamento de Rejeição
```javascript theme={null}
async function handleCashOutReversal(payload) {
// Se o PIX foi devolvido, pode ser rejeição do banco
if (payload.metadata.originatedFrom === 'WEBHOOK_DIRECT') {
console.log('PIX rejeitado pelo banco destino');
await transferService.markAsFailed({
transferId: payload.parentTransaction.externalId,
reason: 'Devolvido pelo banco destino'
});
// Notificar usuário para verificar dados
await notificationService.sendTransferFailed();
}
}
```
***
## Fluxo Típico
```mermaid theme={null}
sequenceDiagram
participant SeuSistema
participant Fire Banking
participant Recebedor
Note over SeuSistema,Recebedor: Transação original (CashOut)
SeuSistema->>Fire Banking: POST /api/pix/payment
Fire Banking->>Recebedor: PIX enviado
Fire Banking->>SeuSistema: Webhook CashOut
Note over SeuSistema,Recebedor: Devolução (CashOutReversal)
Recebedor->>Fire Banking: Devolve PIX
Fire Banking->>Fire Banking: Processa devolução
Fire Banking->>SeuSistema: Webhook CashOutReversal
SeuSistema-->>Fire Banking: HTTP 200 OK
SeuSistema->>SeuSistema: Credita saldo
```
***
## Próximos Passos
Aprenda a enviar pagamentos PIX
Entenda o evento de envio
# Implementação
Source: https://docs.firebanking.dev/api-reference/guides/webhooks/implementation
Exemplos de código e boas práticas para implementar webhooks
## Exemplos Completos
```typescript theme={null}
import express from 'express';
interface PixWebhookPayload {
event: 'CashIn' | 'CashOut' | 'CashInReversal' | 'CashOutReversal';
status: 'PENDING' | 'CONFIRMED' | 'ERROR';
transactionType: 'PIX';
movementType: 'CREDIT' | 'DEBIT';
transactionId: string;
externalId: string | null;
endToEndId: string;
pixKey: string | null;
feeAmount: number;
originalAmount: number;
finalAmount: number;
processingDate: string;
errorCode: string | null;
errorMessage: string | null;
counterpart?: Counterpart;
parentTransaction?: ParentTransaction;
metadata: Record;
}
interface Counterpart {
name: string;
document: string;
bank: {
bankISPB: string | null;
bankName: string | null;
bankCode: string | null;
accountBranch: string | null;
accountNumber: string | null;
};
}
interface ParentTransaction {
transactionId: string;
externalId: string;
endToEndId: string;
processingDate: string;
wasTotalRefunded: boolean;
remainingAmountForRefund: number;
metadata: Record;
counterpart: Counterpart;
}
const app = express();
app.use(express.json());
// Middleware de autenticação Basic Auth
function validateBasicAuth(
req: express.Request,
res: express.Response,
next: express.NextFunction
) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Basic ')) {
return res.status(401).json({ error: 'Unauthorized' });
}
const base64Credentials = authHeader.split(' ')[1];
const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
const [username, password] = credentials.split(':');
if (
username !== process.env.WEBHOOK_USER ||
password !== process.env.WEBHOOK_PASS
) {
return res.status(401).json({ error: 'Invalid credentials' });
}
next();
}
// Set para controle de idempotência
const processedTransactions = new Set();
app.post('/webhooks/pix', validateBasicAuth, async (req, res) => {
const payload: PixWebhookPayload = req.body;
// Responder rapidamente (webhook exige resposta em até 10s)
res.status(200).json({ acknowledged: true });
// Verificar idempotência
if (processedTransactions.has(payload.transactionId)) {
console.log(`Transação ${payload.transactionId} já processada`);
return;
}
// Marcar como processada
processedTransactions.add(payload.transactionId);
// Processar assincronamente
try {
switch (payload.event) {
case 'CashIn':
await handleCashIn(payload);
break;
case 'CashOut':
await handleCashOut(payload);
break;
case 'CashInReversal':
await handleCashInReversal(payload);
break;
case 'CashOutReversal':
await handleCashOutReversal(payload);
break;
}
} catch (error) {
console.error(`Erro ao processar ${payload.event}:`, error);
processedTransactions.delete(payload.transactionId);
}
});
async function handleCashIn(payload: PixWebhookPayload) {
console.log(`[CashIn] Recebido: R$ ${payload.finalAmount}`);
// await orderService.markAsPaid(payload.externalId);
}
async function handleCashOut(payload: PixWebhookPayload) {
console.log(`[CashOut] Enviado: R$ ${payload.originalAmount}`);
// await transferService.markAsCompleted(payload.transactionId);
}
async function handleCashInReversal(payload: PixWebhookPayload) {
console.log(`[CashInReversal] Estornado: R$ ${payload.originalAmount}`);
// await refundService.markAsCompleted(payload.transactionId);
}
async function handleCashOutReversal(payload: PixWebhookPayload) {
console.log(`[CashOutReversal] Devolvido: R$ ${payload.finalAmount}`);
// await balanceService.credit(payload.finalAmount);
}
app.listen(3000);
```
```python theme={null}
from flask import Flask, request, jsonify
from functools import wraps
import base64
import os
from typing import Dict, Any, Optional
from dataclasses import dataclass
app = Flask(__name__)
processed_transactions: set = set()
@dataclass
class PixWebhookPayload:
event: str
status: str
transaction_id: str
external_id: Optional[str]
end_to_end_id: str
fee_amount: float
original_amount: float
final_amount: float
counterpart: Optional[Dict[str, Any]]
parent_transaction: Optional[Dict[str, Any]]
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'PixWebhookPayload':
return cls(
event=data.get('event'),
status=data.get('status'),
transaction_id=data.get('transactionId'),
external_id=data.get('externalId'),
end_to_end_id=data.get('endToEndId'),
fee_amount=data.get('feeAmount', 0),
original_amount=data.get('originalAmount', 0),
final_amount=data.get('finalAmount', 0),
counterpart=data.get('counterpart'),
parent_transaction=data.get('parentTransaction'),
)
def require_basic_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Basic '):
return jsonify({'error': 'Unauthorized'}), 401
try:
credentials = base64.b64decode(
auth_header.split(' ')[1]
).decode('utf-8')
username, password = credentials.split(':')
if (
username != os.environ.get('WEBHOOK_USER') or
password != os.environ.get('WEBHOOK_PASS')
):
return jsonify({'error': 'Invalid credentials'}), 401
except Exception:
return jsonify({'error': 'Invalid auth header'}), 401
return f(*args, **kwargs)
return decorated
@app.route('/webhooks/pix', methods=['POST'])
@require_basic_auth
def handle_pix_webhook():
data = request.get_json()
payload = PixWebhookPayload.from_dict(data)
# Idempotência
if payload.transaction_id in processed_transactions:
return jsonify({'acknowledged': True}), 200
processed_transactions.add(payload.transaction_id)
# Processar
if payload.event == 'CashIn':
print(f"[CashIn] R$ {payload.final_amount:.2f}")
elif payload.event == 'CashOut':
print(f"[CashOut] R$ {payload.original_amount:.2f}")
elif payload.event == 'CashInReversal':
print(f"[CashInReversal] R$ {payload.original_amount:.2f}")
elif payload.event == 'CashOutReversal':
print(f"[CashOutReversal] R$ {payload.final_amount:.2f}")
return jsonify({'acknowledged': True}), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3000)
```
```php theme={null}
'Unauthorized']);
exit;
}
$payload = json_decode(file_get_contents('php://input'), true);
// Responder rapidamente
http_response_code(200);
header('Content-Type: application/json');
echo json_encode(['acknowledged' => true]);
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}
// Idempotência
if (isProcessed($payload['transactionId'])) {
exit;
}
markProcessed($payload['transactionId']);
// Processar
switch ($payload['event']) {
case 'CashIn':
error_log("[CashIn] R$ " . $payload['finalAmount']);
break;
case 'CashOut':
error_log("[CashOut] R$ " . $payload['originalAmount']);
break;
case 'CashInReversal':
error_log("[CashInReversal] R$ " . $payload['originalAmount']);
break;
case 'CashOutReversal':
error_log("[CashOutReversal] R$ " . $payload['finalAmount']);
break;
}
```
***
## Idempotência
Webhooks podem ser enviados mais de uma vez (em caso de retentativas). Implemente tratamento de idempotência para evitar processamento duplicado.
Use o campo `transactionId` como chave única:
```typescript theme={null}
// Verificar se já processou
const isProcessed = await redis.get(`webhook:${payload.transactionId}`);
if (isProcessed) {
console.log('Webhook já processado, ignorando');
return;
}
// Marcar como processado ANTES de processar
await redis.set(`webhook:${payload.transactionId}`, '1', 'EX', 86400);
// Processar webhook
await processWebhook(payload);
```
* **Performance**: Verificação em memória é extremamente rápida
* **Distribuído**: Funciona com múltiplas instâncias do servidor
* **TTL automático**: Limpeza automática de registros antigos
```sql theme={null}
CREATE TABLE processed_webhooks (
transaction_id VARCHAR PRIMARY KEY,
processed_at TIMESTAMP DEFAULT NOW()
);
INSERT INTO processed_webhooks (transaction_id)
VALUES ($1)
ON CONFLICT (transaction_id) DO NOTHING
RETURNING transaction_id;
```
***
## Boas Práticas
O sistema espera resposta em até 10 segundos. Responda imediatamente e processe de forma assíncrona para evitar timeouts.
```javascript theme={null}
app.post('/webhooks/pix', (req, res) => {
res.status(200).json({ acknowledged: true });
processWebhookAsync(req.body).catch(console.error);
});
```
Configure seu endpoint apenas com HTTPS para garantir transmissão segura.
Sempre valide o header `Authorization` com Basic Auth.
```javascript theme={null}
console.log({
timestamp: new Date().toISOString(),
event: payload.event,
transactionId: payload.transactionId,
amount: payload.finalAmount
});
```
O campo `externalId` contém o identificador enviado na criação. Use-o para correlacionar com seus registros.
***
## Retentativas
Se seu endpoint não responder com HTTP 200 em até 10 segundos:
| Tentativa | Intervalo | Tempo acumulado |
| ------------- | ---------- | --------------- |
| 1ª | Imediato | 0 min |
| 2ª (1º retry) | 5 minutos | 5 min |
| 3ª (2º retry) | 5 minutos | 10 min |
| 4ª (3º retry) | 15 minutos | 25 min |
Após 4 tentativas sem sucesso (tempo total \~25 minutos), o webhook é movido para uma fila de falhas (DLQ). Implemente consulta periódica como fallback para garantir que nenhuma transação seja perdida.
A estratégia de retry diferencia erros temporários (network, timeout, 5xx) de erros permanentes (validação, formato inválido). Erros permanentes não são retentados.
***
## Códigos de Resposta
Seu endpoint deve retornar um código HTTP apropriado:
| Código | Descrição | Ação do Sistema |
| ------ | ----------------------------- | ---------------------------------------- |
| `2xx` | Sucesso (200, 201, 204, etc.) | ✅ Webhook confirmado, não será retentado |
| `3xx` | Redirecionamento | ⚠️ Considerado falha, será retentado |
| `4xx` | Erro do cliente | ⚠️ Considerado falha, será retentado |
| `5xx` | Erro do servidor | ⚠️ Considerado falha, será retentado |
O sistema valida **apenas o código HTTP**. Qualquer resposta 2xx (200-299) é considerada sucesso, independente do conteúdo do body. Você pode retornar body vazio, `"OK"`, ou qualquer JSON.
***
## Próximos Passos
Aprenda a gerar cobranças PIX
Aprenda a enviar pagamentos PIX
Aprenda a estornar recebimentos
Configure a autenticação da API
# Visão Geral
Source: https://docs.firebanking.dev/api-reference/guides/webhooks/overview
Receba notificações automáticas sobre o status das suas transações PIX
## O que são Webhooks?
Os **Webhooks PIX** permitem que você receba notificações em tempo real quando o status de uma transação PIX muda. Em vez de fazer polling constantemente na API, seu sistema é notificado automaticamente quando eventos importantes ocorrem.
Webhooks são a forma recomendada de acompanhar o status das transações. Eles reduzem a latência e o consumo de recursos comparado ao polling.
### Características
* Notificações em tempo real
* Suporte a 4 tipos de eventos (Cash In, Cash Out, Refund In, Refund Out)
* Retentativas automáticas em caso de falha
* Autenticação via Basic Auth
* Payload padronizado em JSON
***
## Eventos Disponíveis
Recebimento PIX confirmado (CREDIT)
Envio PIX confirmado (DEBIT)
Estorno de recebimento (DEBIT)
Devolução de envio recebida (CREDIT)
| Evento | `event` | `movementType` | Descrição |
| ---------- | ----------------- | -------------- | ---------------------------------------------------- |
| PIX In | `CashIn` | `CREDIT` | Recebimento PIX confirmado |
| PIX Out | `CashOut` | `DEBIT` | Envio PIX confirmado |
| Refund In | `CashInReversal` | `DEBIT` | Estorno de recebimento (devolução iniciada por você) |
| Refund Out | `CashOutReversal` | `CREDIT` | Devolução de envio (devolução recebida) |
***
## Configuração do Endpoint
Para receber webhooks, você precisa:
Use a [API de Configuração de Webhooks](/api-reference/guides/webhooks/setup) para definir a URL do seu endpoint programaticamente.
Crie um endpoint HTTPS que aceite requisições POST e retorne HTTP 200 rapidamente.
Configure a validação do header de autenticação Basic Auth.
### Requisitos Técnicos
| Requisito | Descrição |
| ------------ | ---------------------------- |
| Protocolo | HTTPS obrigatório |
| Método | POST |
| Timeout | Responder em até 10 segundos |
| Response | HTTP 200 OK |
| Content-Type | application/json |
Se seu endpoint não responder com HTTP 200 dentro de 10 segundos, o webhook será considerado como falha e será retentado.
***
## Autenticação Basic Auth
Os webhooks são enviados com autenticação **Basic Auth** no header:
```
Authorization: Basic base64(username:password)
```
```javascript theme={null}
// Node.js/Express - Validação
app.post('/webhooks/pix', (req, res) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Basic ')) {
return res.status(401).send('Unauthorized');
}
const base64Credentials = authHeader.split(' ')[1];
const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
const [username, password] = credentials.split(':');
if (username !== process.env.WEBHOOK_USER || password !== process.env.WEBHOOK_PASS) {
return res.status(401).send('Unauthorized');
}
// Processar webhook...
res.status(200).json({ acknowledged: true });
});
```
***
## Estrutura Base do Payload
Todos os webhooks compartilham uma estrutura base comum:
```json theme={null}
{
"event": "CashIn",
"status": "CONFIRMED",
"transactionType": "PIX",
"movementType": "CREDIT",
"transactionId": "12345",
"externalId": "PIX-5482123298-EJUYFSMU1UU",
"endToEndId": "E00416968202512111942rjzxxzSSTD9",
"pixKey": "1ff6ce09-4244-44d5-aa8f-1fe69f8986a9",
"feeAmount": 0.01,
"originalAmount": 0.5,
"finalAmount": 0.49,
"processingDate": "2025-12-11T19:42:04.080Z",
"errorCode": null,
"errorMessage": null,
"metadata": {}
}
```
Tipo do evento.
**Valores possíveis:** `CashIn`, `CashOut`, `CashInReversal`, `CashOutReversal`
Status da transação.
**Valores possíveis:** `PENDING`, `CONFIRMED`, `ERROR`
Tipo de transação. Sempre `PIX` para webhooks PIX.
Tipo de movimento na conta.
* `CREDIT`: Entrada de recursos (recebimento ou devolução recebida)
* `DEBIT`: Saída de recursos (envio ou estorno)
ID numérico da transação na Fire Banking (retornado como string).
**Exemplo:** `"12345"`
ID End-to-End gerado pelo Banco Central para rastreamento.
**Exemplo:** `"E00416968202512111942rjzxxzSSTD9"`
Data e hora do processamento (ISO 8601 UTC).
**Exemplo:** `"2025-12-11T19:42:04.080Z"`
Taxa cobrada pela transação em reais (BRL).
**Exemplo:** `0.01`
Valor original da transação em reais (BRL).
**Exemplo:** `0.50`
Valor final após aplicação de taxas.
* Para `CREDIT`: `originalAmount - feeAmount`
* Para `DEBIT`: `originalAmount + feeAmount`
ID externo fornecido na criação da transação.
**Exemplo:** `"PIX-5482123298-EJUYFSMU1UU"`
Chave PIX utilizada na transação (CPF, CNPJ, email, telefone ou chave aleatória).
Código de erro quando `status` é `ERROR`. Nulo se sucesso.
Mensagem de erro descritiva. Nulo se sucesso.
Metadados adicionais específicos do evento.
***
## Próximos Passos
Configure URLs de webhook via API
Detalhes do evento de recebimento
Detalhes do evento de envio
Detalhes do evento de estorno
Detalhes do evento de devolução
# Configurar Webhooks via API
Source: https://docs.firebanking.dev/api-reference/guides/webhooks/setup
Configure URLs de webhook programaticamente para receber notificações de eventos PIX
## Visão Geral
A API de configuração de webhooks permite que você defina programaticamente onde sua aplicação receberá notificações de eventos PIX. Isso elimina a necessidade de contato com o suporte para configurar webhooks.
Mudanças na configuração de webhooks são aplicadas **imediatamente**.
Transações subsequentes usarão a nova URL configurada.
## Endpoint
**POST** `/api/webhooks`
## Autenticação
Requer token Bearer da conta (Account Token) no header Authorization.
```bash theme={null}
Authorization: Bearer {account_token}
```
O token deve ser obtido através do endpoint de autenticação usando seu certificado de cliente.
## Parâmetros
URL HTTPS do seu endpoint de webhook.
**Requisitos:**
* Deve usar protocolo HTTPS (HTTP não é aceito)
* Deve ser uma URL válida e acessível
**Exemplo:** `https://api.example.com/webhooks/pix`
Tipo de evento para receber notificações.
**Valores possíveis:**
* `cash_in` - PIX recebido
* `cash_out` - PIX enviado
* `refund_in` - Estorno de recebimento (devolução solicitada)
* `refund_out` - Devolução recebida
Headers customizados para autenticação do seu endpoint (máximo 5).
Cada item deve ter:
* `key`: Nome do header
* `value`: Valor do header
**Headers bloqueados (nao permitidos):**
* host
* content-length
* connection
* transfer-encoding
* content-type
* user-agent
## Exemplo de Request
```bash cURL theme={null}
curl -X POST https://api.public.firebanking.com.br/api/webhooks \
-H "Authorization: Bearer {account_token}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/webhooks/pix",
"eventType": "cash_in",
"headers": [
{
"key": "Authorization",
"value": "Bearer my-secret-token"
},
{
"key": "X-Webhook-Secret",
"value": "abc123"
}
]
}'
```
```javascript Node.js theme={null}
const response = await fetch('https://api.public.firebanking.com.br/api/webhooks', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accountToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://api.example.com/webhooks/pix',
eventType: 'cash_in',
headers: [
{ key: 'Authorization', value: 'Bearer my-secret-token' },
{ key: 'X-Webhook-Secret', value: 'abc123' },
],
}),
});
const data = await response.json();
console.log(data);
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.public.firebanking.com.br/api/webhooks',
headers={
'Authorization': f'Bearer {account_token}',
'Content-Type': 'application/json',
},
json={
'url': 'https://api.example.com/webhooks/pix',
'eventType': 'cash_in',
'headers': [
{'key': 'Authorization', 'value': 'Bearer my-secret-token'},
{'key': 'X-Webhook-Secret', 'value': 'abc123'},
],
},
)
print(response.json())
```
## Exemplo de Response
```json theme={null}
{
"success": true,
"message": "Webhook configurado com sucesso"
}
```
## Comportamento de Upsert
Se já existir um webhook configurado para o mesmo `eventType`, ele será **atualizado** com a nova URL e headers. Não é criado um webhook duplicado.
Ao atualizar um webhook existente, os headers anteriores são **substituídos** pelos novos.
Se você não enviar headers, os headers anteriores serão removidos.
## Códigos de Erro
| Código | Descrição |
| ------ | ------------------------------------------------------------------------- |
| 400 | URL inválida (não é HTTPS), tipo de evento inválido, ou mais de 5 headers |
| 401 | Token não fornecido ou inválido |
| 404 | Conta não encontrada |
| 500 | Erro interno ao configurar webhook |
## Configurando Múltiplos Eventos
Para receber notificações de múltiplos tipos de eventos, faça uma chamada para cada tipo:
```javascript theme={null}
const eventTypes = ['cash_in', 'cash_out', 'refund_in', 'refund_out'];
for (const eventType of eventTypes) {
await fetch('https://api.public.firebanking.com.br/api/webhooks', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accountToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://api.example.com/webhooks/pix',
eventType,
headers: [
{ key: 'X-Webhook-Secret', value: 'abc123' },
],
}),
});
}
```
Você pode usar a mesma URL para todos os tipos de evento e diferenciar pelo campo `type` no payload do webhook.
## Próximos Passos
Entenda a estrutura dos webhooks recebidos
Exemplos de codigo para processar webhooks
Reenvie webhooks perdidos ou para testes
Detalhes do webhook de PIX recebido
# Introdução à API
Source: https://docs.firebanking.dev/api-reference/introduction
Bem-vindo à documentação da API Pública Fire Banking
## Visão Geral
A **API Pública Fire Banking** é uma plataforma completa para integração com serviços de pagamento PIX e gestão de contas. Nossa API permite que você:
* Gere cobranças PIX dinâmicas para recebimento
* Realize pagamentos PIX para qualquer chave
* Realize pagamentos PIX via QR Code
* Consulte saldos em tempo real
* Gerencie estornos de transações
* Integre pagamentos instantâneos em sua aplicação
## Ambiente
```
https://api.public.firebanking.com.br
```
## Autenticação
Todos os endpoints da API (exceto o de geração de token) requerem autenticação via Bearer token. O processo de autenticação segue o padrão OAuth 2.0 com certificado X.509:
Solicite seu `clientId` e `clientSecret` através do portal Fire Banking
Instale o certificado cliente fornecido em seu ambiente
Use o endpoint `/api/auth/token` com suas credenciais e certificado para gerar um token Bearer
Inclua o token no header `Authorization: Bearer {token}` em todas as requisições
O token gerado tem validade de **30 minutos** e deve ser renovado após esse período.
## Códigos de Status HTTP
A API utiliza códigos de status HTTP padrão para indicar o sucesso ou falha de uma requisição:
| Código | Significado | Descrição |
| ------ | --------------------- | ----------------------------------- |
| `200` | OK | Requisição bem-sucedida (GET) |
| `201` | Created | Recurso criado com sucesso (POST) |
| `400` | Bad Request | Dados inválidos na requisição |
| `401` | Unauthorized | Token ausente, inválido ou expirado |
| `404` | Not Found | Recurso não encontrado |
| `500` | Internal Server Error | Erro interno do servidor |
## Formato de Datas
Todas as datas na API seguem o padrão **ISO 8601** com timezone UTC:
```
2024-01-15T10:30:00.000Z
```
## Suporte
Para questões técnicas ou suporte, entre em contato:
* **Email:** [suporte@firebanking.com.br](mailto:suporte@firebanking.com.br)
* **Documentação:** [https://docs.firebanking.com.br](https://docs.firebanking.com.br)
* **Status da API:** [https://status.firebanking.io](https://status.firebanking.io)
# Ativação
Source: https://docs.firebanking.dev/pix-bacen/activation
Como ativar o modo PIX Bacen e Webhooks V2 na sua conta
## Visão Geral
O modo PIX Bacen inclui duas funcionalidades que precisam ser ativadas:
1. **Endpoints BACEN**: Acesso aos endpoints compatíveis com a especificação do Banco Central
2. **Webhooks V2**: Novo formato de notificações com envelope `{type, data}`
A ativação do modo PIX Bacen é uma **breaking change**. Os webhooks passam a usar um formato completamente diferente. Certifique-se de atualizar sua integração antes de solicitar a ativação.
## Como Solicitar Ativação
### 1. Entre em contato com o suporte
Envie um email para **[suporte@firebanking.com.br](mailto:suporte@firebanking.com.br)** com:
* Nome da empresa
* CNPJ
* Client ID da aplicação
* Confirmação de que já implementou suporte ao Webhook V2
### 2. Aguarde a configuração
Nossa equipe irá:
1. Ativar o modo PIX Bacen na sua conta
2. Habilitar os endpoints e o formato de webhook V2
3. Confirmar a ativação por email
### 3. Teste a integração
Após a ativação:
1. Faça uma cobrança teste via `PUT /cob/:txid`
2. Verifique se o webhook V2 chegou corretamente
3. Confirme que sua aplicação processou o novo formato
## O que muda com a ativação?
### Endpoints
Você passa a ter acesso aos endpoints BACEN:
| Antes | Depois |
| ---------------------- | ------------------------------- |
| `POST /pix/cash-in` | `PUT /cob/:txid` |
| `POST /pix/cash-out` | `POST /dict/pix` |
| `POST /pix/:id/refund` | `PUT /pix/:e2eid/devolucao/:id` |
| `GET /balance` | `GET /accounts/balances` |
Os endpoints antigos continuam funcionando. Você pode usar ambas as APIs simultaneamente.
### Webhooks
O formato de webhook muda completamente:
```json theme={null}
{
"event": "CashIn",
"status": "CONFIRMED",
"transactionId": "12345",
"movementType": "CREDIT",
"originalAmount": 100.00,
"finalAmount": 100.00,
"counterpart": {
"name": "João Silva",
"document": "123.xxx.xxx-xx"
}
}
```
```json theme={null}
{
"type": "RECEIVE",
"data": {
"id": 123,
"txId": "abc123",
"status": "LIQUIDATED",
"payment": {
"amount": "100.00",
"currency": "BRL"
},
"creditDebitType": "CREDIT",
"debtorAccount": {
"name": "João Silva",
"document": "123.xxx.xxx-xx"
},
"creditorAccount": {...}
}
}
```
### Principais diferenças nos Webhooks
| Aspecto | V1 | V2 |
| -------------- | ----------------- | ----------------------------------- |
| Estrutura | Campos na raiz | Envelope `{type, data}` |
| Tipo de evento | `event: "CashIn"` | `type: "RECEIVE"` |
| Status sucesso | `CONFIRMED` | `LIQUIDATED` |
| Status refund | `CONFIRMED` | `REFUNDED` |
| Valores | `number` (100.00) | `string` ("100.00") |
| Contraparte | `counterpart` | `debtorAccount` / `creditorAccount` |
## Preparando sua integração
### 1. Atualize o handler de webhooks
```typescript theme={null}
// ANTES (V1)
function handleWebhookV1(payload: any) {
if (payload.event === 'CashIn' && payload.status === 'CONFIRMED') {
processPayment(payload.transactionId, payload.finalAmount);
}
}
// DEPOIS (V2)
function handleWebhookV2(payload: any) {
if (payload.type === 'RECEIVE' && payload.data.status === 'LIQUIDATED') {
const amount = parseFloat(payload.data.payment.amount);
processPayment(payload.data.id, amount);
}
}
```
### 2. Atualize os tipos/interfaces
```typescript theme={null}
// V2 Types
interface WebhookV2Payload {
type: 'RECEIVE' | 'TRANSFER' | 'REFUND';
data: WebhookV2Data;
}
interface WebhookV2Data {
id: number;
txId: string | null;
status: 'PENDING' | 'LIQUIDATED' | 'REFUNDED' | 'ERROR';
payment: {
amount: string; // Note: string, não number!
currency: string;
};
creditDebitType: 'CREDIT' | 'DEBIT';
debtorAccount: AccountInfo;
creditorAccount: AccountInfo;
endToEndId: string | null;
refunds: RefundInfo[];
// ... outros campos
}
```
### 3. Teste em ambiente de desenvolvimento
Antes de solicitar a ativação em produção:
1. Solicite ativação no ambiente de sandbox
2. Execute testes completos de Cash-In, Cash-Out e Refund
3. Valide que todos os webhooks são processados corretamente
## Rollback
Após a ativação, **não é possível voltar para V1** automaticamente. Se precisar reverter, entre em contato com o suporte.
Recomendamos manter suporte a ambas as versões durante a transição:
```typescript theme={null}
function handleWebhook(payload: any) {
// Detecta versão pelo formato
if (payload.type && payload.data) {
return handleWebhookV2(payload);
} else if (payload.event) {
return handleWebhookV1(payload);
}
throw new Error('Formato de webhook desconhecido');
}
```
## Checklist de Ativação
Atualize seu código para processar o formato envelope `{type, data}`
Solicite ativação em sandbox e execute testes completos
Teste: RECEIVE, TRANSFER, REFUND com status LIQUIDATED, REFUNDED e ERROR
Envie email para [suporte@firebanking.com.br](mailto:suporte@firebanking.com.br) com as informações necessárias
Acompanhe as primeiras transações após a ativação para garantir funcionamento
## Dúvidas Frequentes
Os **endpoints** podem ser usados simultaneamente (ex: `POST /pix/cash-in` e `PUT /cob/:txid`).
Os **webhooks** são sempre na versão configurada na conta. Não é possível receber V1 e V2 ao mesmo tempo.
Transações criadas antes da ativação continuarão enviando webhooks no formato antigo até serem concluídas.
Novas transações usarão o formato V2.
Não. A URL permanece a mesma. Apenas o formato do payload muda.
## Próximos Passos
Entenda o novo formato de webhooks
Use o endpoint BACEN para criar cobranças
# Autenticação
Source: https://docs.firebanking.dev/pix-bacen/authentication
Como autenticar suas requisições na API PIX Bacen
## Visão Geral
A API PIX Bacen utiliza o mesmo sistema de autenticação da API padrão Fire Banking. Todas as requisições devem incluir um token Bearer válido no header `Authorization`.
A autenticação é idêntica à [API padrão](/api-reference/guides/authentication). Se você já possui credenciais, pode usá-las diretamente.
## Obtendo o Token
### Endpoint
```
POST /oauth/token
```
### Request
```bash cURL theme={null}
curl -X POST https://api.public.firebanking.com.br/oauth/token \
-H "Content-Type: application/json" \
-d '{
"clientId": "seu-client-id",
"clientSecret": "seu-client-secret"
}'
```
```typescript Node.js theme={null}
const response = await fetch('https://api.public.firebanking.com.br/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
clientId: 'seu-client-id',
clientSecret: 'seu-client-secret',
}),
});
const { access_token } = await response.json();
```
```python Python theme={null}
import requests
response = requests.post(
'https://api.public.firebanking.com.br/oauth/token',
json={
'clientId': 'seu-client-id',
'clientSecret': 'seu-client-secret'
}
)
access_token = response.json()['access_token']
```
### Response
```json theme={null}
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "pix:read pix:write balance:read"
}
```
## Usando o Token
Inclua o token em todas as requisições da API PIX Bacen:
```bash theme={null}
curl -X PUT https://api.public.firebanking.com.br/cob/abc123 \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{...}'
```
## Parâmetros de Autenticação
Identificador único da sua aplicação. Fornecido durante o cadastro.
Chave secreta da sua aplicação. Deve ter entre 8 e 64 caracteres.
Nunca exponha o `clientSecret` em código frontend ou repositórios públicos.
## Campos da Resposta
Token JWT para autenticação nas requisições.
Tipo do token. Sempre `"Bearer"`.
Tempo de vida do token em segundos. Padrão: 3600 (1 hora).
Escopos de permissão do token.
## Renovação do Token
O token expira após `expires_in` segundos. Implemente renovação automática:
```typescript theme={null}
class TokenManager {
private token: string | null = null;
private expiresAt: number = 0;
async getToken(): Promise {
// Renovar 5 minutos antes de expirar
if (!this.token || Date.now() >= this.expiresAt - 300000) {
await this.refreshToken();
}
return this.token!;
}
private async refreshToken(): Promise {
const response = await fetch('https://api.public.firebanking.com.br/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
}),
});
const data = await response.json();
this.token = data.access_token;
this.expiresAt = Date.now() + (data.expires_in * 1000);
}
}
```
## Erros de Autenticação
| Código | Descrição | Solução |
| ------ | ------------------- | ----------------------------------------------- |
| 401 | Token não fornecido | Inclua o header `Authorization: Bearer ` |
| 401 | Token inválido | Verifique se o token está correto e não expirou |
| 401 | Token expirado | Obtenha um novo token via `/oauth/token` |
| 403 | Permissão negada | Verifique os escopos do token |
## Boas Práticas
* Use variáveis de ambiente
* Nunca commite credenciais no código
* Use secret managers em produção (AWS Secrets Manager, HashiCorp Vault)
* Cache o token até próximo da expiração
* Renove alguns minutos antes de expirar
* Evite requisições desnecessárias ao endpoint de token
* Todas as requisições devem usar HTTPS
* Verifique certificados SSL/TLS
* Configure timeouts apropriados
## Próximos Passos
Ative o modo PIX Bacen na sua conta
Faça sua primeira cobrança
# Consultar Saldo
Source: https://docs.firebanking.dev/pix-bacen/endpoints/balance
Consulte o saldo da conta autenticada no formato BACEN
## Visão Geral
O endpoint `GET /accounts/balances` retorna o saldo da conta autenticada no formato compatível com a especificação do Banco Central.
## Endpoint
```
GET /accounts/balances
```
## Autenticação
Token Bearer obtido via `/oauth/token`.
## Request
```bash cURL theme={null}
curl -X GET https://api.public.firebanking.com.br/accounts/balances \
-H "Authorization: Bearer "
```
```typescript Node.js theme={null}
const response = await fetch('https://api.public.firebanking.com.br/accounts/balances', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
},
});
const balance = await response.json();
```
```python Python theme={null}
import requests
response = requests.get(
'https://api.public.firebanking.com.br/accounts/balances',
headers={
'Authorization': f'Bearer {token}',
}
)
balance = response.json()
```
## Response
```json theme={null}
{
"data": [
{
"eventDate": "2025-01-15T10:30:00.000Z",
"balanceAmount": {
"available": 48734.90,
"blocked": 1500.00,
"overdraft": 0
}
}
]
}
```
```json theme={null}
{
"statusCode": 401,
"message": "Token não fornecido ou inválido",
"error": "Unauthorized"
}
```
## Campos da Resposta
Lista de saldos. Atualmente retorna apenas um item.
Data e hora da consulta (ISO 8601).
Valores do saldo.
Saldo disponível para uso imediato. Já desconta valores bloqueados.
Saldo bloqueado. Valores reservados para operações pendentes (ex: PIX Out em processamento).
Limite de crédito (cheque especial). Atualmente sempre `0`.
## Tipos de Saldo
| Tipo | Descrição |
| ------------- | ---------------------------------------------------------- |
| **available** | Saldo que pode ser usado imediatamente para transferências |
| **blocked** | Valores reservados para operações em processamento |
| **overdraft** | Limite de crédito adicional (não implementado) |
### Cálculo do Saldo Total
```
saldoTotal = available + blocked
```
O saldo `available` já desconta os valores `blocked`.
## Comparação com API Padrão
| API Padrão (`GET /balance`) | API BACEN (`GET /accounts/balances`) |
| --------------------------- | ------------------------------------ |
| `grossBalance` | `available + blocked` |
| `blockedBalance` | `blocked` |
| `netBalance` | `available` |
| `consultedAt` | `eventDate` |
### Exemplo de Equivalência
```json theme={null}
// API Padrão
{
"grossBalance": 50234.90,
"blockedBalance": 1500.00,
"netBalance": 48734.90,
"consultedAt": "2025-01-15T10:30:00.000Z"
}
// API BACEN (equivalente)
{
"data": [{
"eventDate": "2025-01-15T10:30:00.000Z",
"balanceAmount": {
"available": 48734.90, // = netBalance
"blocked": 1500.00, // = blockedBalance
"overdraft": 0
}
}]
}
```
## Fluxo de Saldo em Operações
### PIX Out (Transferência)
```mermaid theme={null}
sequenceDiagram
participant App
participant API
participant Banco
Note over App,Banco: Estado inicial: available=1000, blocked=0
App->>API: POST /dict/pix (R$ 100)
API-->>App: { type: "PENDING" }
Note over App,Banco: Estado: available=900, blocked=100
Banco->>API: Confirmação
API->>App: Webhook TRANSFER (LIQUIDATED)
Note over App,Banco: Estado final: available=900, blocked=0
```
### PIX In (Recebimento)
```mermaid theme={null}
sequenceDiagram
participant Pagador
participant API
participant App
Note over Pagador,App: Estado inicial: available=1000
Pagador->>API: Paga QR Code
API->>App: Webhook RECEIVE (LIQUIDATED)
Note over Pagador,App: Estado final: available=1100
```
## Boas Práticas
Sempre verifique o saldo disponível antes de iniciar uma transferência para evitar erros de saldo insuficiente.
```typescript theme={null}
async function transferir(valor: number) {
const balance = await getBalance();
const disponivel = balance.data[0].balanceAmount.available;
if (valor > disponivel) {
throw new Error(`Saldo insuficiente. Disponível: ${disponivel}`);
}
return await createTransfer(valor);
}
```
O saldo `blocked` representa operações em andamento. Em caso de falha, esse valor retorna para `available`.
Se precisar cachear o saldo, use TTL curto (ex: 5-10 segundos) para manter valores atualizados.
## Erros Comuns
| Código | Erro | Solução |
| ------ | ------------------- | --------------------------------------------- |
| 401 | Token não fornecido | Inclua header `Authorization: Bearer ` |
| 401 | Token inválido | Verifique se o token está correto |
| 401 | Token expirado | Obtenha novo token via `/oauth/token` |
## Próximos Passos
Gere um QR Code para receber PIX
Envie um PIX para outra conta
# Criar Cobrança
Source: https://docs.firebanking.dev/pix-bacen/endpoints/cob
Crie uma cobrança PIX imediata (QR Code) seguindo a especificação BACEN
## Visão Geral
O endpoint `PUT /cob/:txid` cria uma cobrança imediata (cob) associada ao identificador de transação (txid) informado. Este endpoint segue a especificação oficial do Banco Central do Brasil para cobranças PIX.
O `txid` é um identificador único gerado pelo seu sistema. Deve ter entre 26 e 35 caracteres alfanuméricos.
## Endpoint
```
PUT /cob/{txid}
```
## Autenticação
Token Bearer obtido via `/oauth/token`.
Exemplo: `Bearer eyJhbGciOiJSUzI1NiIs...`
## Parâmetros de URL
Identificador da transação. Deve ser único e conter entre 26 e 35 caracteres alfanuméricos `[a-zA-Z0-9]`.
Exemplo: `7978c0c97ea847e78e8849634473c1f1`
## Request Body
Informações de controle de tempo da cobrança.
Tempo de vida da cobrança em segundos. Padrão: 86400 (24 horas).
Dados do devedor (pagador). Pode ser Pessoa Física (CPF) ou Jurídica (CNPJ).
CPF do devedor. Apenas números, 11 dígitos.
Nome completo do devedor. Máximo 200 caracteres.
CNPJ do devedor. Apenas números, 14 dígitos.
Razão social do devedor. Máximo 200 caracteres.
Valores monetários da cobrança.
Valor original da cobrança. **String** no formato decimal com 2 casas.
Exemplo: `"123.45"`
Modalidade de alteração do valor:
* `0`: Valor fixo (não pode ser alterado pelo pagador)
* `1`: Valor alterável (pagador pode modificar)
Chave PIX do recebedor. Pode ser telefone, e-mail, CPF/CNPJ ou EVP (chave aleatória). Máximo 77 caracteres.
Texto livre para o pagador. Máximo 140 caracteres.
Lista de informações adicionais ao pagador.
Nome do campo. Máximo 50 caracteres.
Valor do campo. Máximo 200 caracteres.
## Request
```bash cURL theme={null}
curl -X PUT https://api.public.firebanking.com.br/cob/7978c0c97ea847e78e8849634473c1f1 \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"calendario": {
"expiracao": 3600
},
"devedor": {
"cpf": "12345678909",
"nome": "Carlos Oliveira"
},
"valor": {
"original": "123.45",
"modalidadeAlteracao": 0
},
"chave": "7d9f0335-8dcc-4054-9bf9-0dbd61d36906",
"solicitacaoPagador": "Serviço realizado.",
"infoAdicionais": [
{
"nome": "Pedido",
"valor": "#12345"
}
]
}'
```
```typescript Node.js theme={null}
const response = await fetch(
'https://api.public.firebanking.com.br/cob/7978c0c97ea847e78e8849634473c1f1',
{
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
calendario: {
expiracao: 3600,
},
devedor: {
cpf: '12345678909',
nome: 'Carlos Oliveira',
},
valor: {
original: '123.45',
modalidadeAlteracao: 0,
},
chave: '7d9f0335-8dcc-4054-9bf9-0dbd61d36906',
solicitacaoPagador: 'Serviço realizado.',
infoAdicionais: [
{ nome: 'Pedido', valor: '#12345' },
],
}),
}
);
const cobranca = await response.json();
```
```python Python theme={null}
import requests
response = requests.put(
'https://api.public.firebanking.com.br/cob/7978c0c97ea847e78e8849634473c1f1',
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json',
},
json={
'calendario': {
'expiracao': 3600,
},
'devedor': {
'cpf': '12345678909',
'nome': 'Carlos Oliveira',
},
'valor': {
'original': '123.45',
'modalidadeAlteracao': 0,
},
'chave': '7d9f0335-8dcc-4054-9bf9-0dbd61d36906',
'solicitacaoPagador': 'Serviço realizado.',
'infoAdicionais': [
{'nome': 'Pedido', 'valor': '#12345'},
],
}
)
cobranca = response.json()
```
## Response
```json theme={null}
{
"calendario": {
"criacao": "2024-01-15T10:30:00.358Z",
"expiracao": 3600
},
"txid": "7978c0c97ea847e78e8849634473c1f1",
"revisao": 0,
"loc": {
"id": 12345,
"location": "00020126580014br.gov.bcb.pix0136a629532e-7693-4846-852d-1bbff817b5a8520400005303986540512.345802BR5916Tech Solutions Ltda6009Sao Paulo62070503***6304ABCD",
"tipoCob": "cob"
},
"location": "00020126580014br.gov.bcb.pix0136a629532e-7693-4846-852d-1bbff817b5a8520400005303986540512.345802BR5916Tech Solutions Ltda6009Sao Paulo62070503***6304ABCD",
"status": "ATIVA",
"devedor": {
"cpf": "12345678909",
"nome": "Carlos Oliveira"
},
"valor": {
"original": "123.45",
"modalidadeAlteracao": 0
},
"chave": "7d9f0335-8dcc-4054-9bf9-0dbd61d36906",
"solicitacaoPagador": "Serviço realizado.",
"infoAdicionais": [
{
"nome": "Pedido",
"valor": "#12345"
}
]
}
```
```json theme={null}
{
"statusCode": 400,
"message": "CPF deve conter exatamente 11 dígitos numéricos",
"error": "Bad Request"
}
```
```json theme={null}
{
"statusCode": 409,
"message": "Cobrança com este txid já existe",
"error": "Conflict"
}
```
## Campos da Resposta
Data e hora de criação da cobrança (ISO 8601).
Tempo de expiração em segundos.
Identificador da transação informado na requisição.
Número da revisão da cobrança. Sempre `0` na criação.
Informações do payload PIX.
Identificador da transação.
Código PIX copia-e-cola (EMV). Use este valor para gerar o QR Code ou permitir que o pagador copie e cole no aplicativo do banco.
Tipo de cobrança. Sempre `"cob"` para cobranças imediatas.
Código PIX copia-e-cola (mesmo valor de `loc.location`). String no formato EMV que pode ser usada para pagamento.
Status da cobrança:
* `ATIVA`: Cobrança ativa, aguardando pagamento
* `CONCLUIDA`: Pagamento recebido
* `REMOVIDA_PELO_USUARIO_RECEBEDOR`: Cancelada pelo recebedor
* `REMOVIDA_PELO_PSP`: Removida pelo PSP
## Status da Cobrança
```mermaid theme={null}
stateDiagram-v2
[*] --> ATIVA: Cobrança criada
ATIVA --> CONCLUIDA: Pagamento recebido
ATIVA --> REMOVIDA_PELO_USUARIO_RECEBEDOR: Cancelada
ATIVA --> REMOVIDA_PELO_PSP: Expirada/Removida
CONCLUIDA --> [*]
REMOVIDA_PELO_USUARIO_RECEBEDOR --> [*]
REMOVIDA_PELO_PSP --> [*]
```
## Webhook de Pagamento
Quando o pagamento for confirmado, você receberá um webhook V2 do tipo `RECEIVE`:
```json theme={null}
{
"type": "RECEIVE",
"data": {
"id": 123,
"txId": "7978c0c97ea847e78e8849634473c1f1",
"status": "LIQUIDATED",
"payment": {
"amount": "123.45",
"currency": "BRL"
},
"endToEndId": "E12345678901234567890123456789012",
"debtorAccount": {
"name": "Carlos Oliveira",
"document": "123.xxx.xxx-xx"
}
}
}
```
Veja a documentação completa do webhook RECEIVE
## Erros Comuns
| Código | Erro | Solução |
| ------ | ------------------- | -------------------------------------------- |
| 400 | txid fora do padrão | Use 26-35 caracteres alfanuméricos |
| 400 | CPF/CNPJ inválido | Verifique formato (apenas números) |
| 400 | Valor inválido | Use formato "123.45" (string com 2 decimais) |
| 401 | Token inválido | Renove o token de acesso |
| 409 | txid já existe | Use um txid diferente |
## Próximos Passos
Devolva um PIX recebido
Processe notificações de pagamento
# Solicitar Devolução
Source: https://docs.firebanking.dev/pix-bacen/endpoints/devolucao
Solicite a devolução de um PIX recebido seguindo a especificação BACEN
## Visão Geral
O endpoint `PUT /pix/:e2eid/devolucao/:id` solicita a devolução de um PIX recebido. Utiliza o End to End ID (e2eid) da transação original e um identificador de devolução gerado pelo cliente.
A devolução pode ser **total** ou **parcial**. A soma de todas as devoluções não pode ultrapassar o valor original da transação.
## Endpoint
```
PUT /pix/{e2eid}/devolucao/{id}
```
## Autenticação
Token Bearer obtido via `/oauth/token`.
## Parâmetros de URL
End to End ID - identificador único da transação PIX original. Contém exatamente 32 caracteres alfanuméricos.
Exemplo: `E12345678901234567890123456789012`
Identificação gerada pelo cliente para representar a devolução. Entre 1 e 35 caracteres.
Exemplo: `D123456789`
## Request Body
Valor solicitado para devolução. **String** no formato decimal com 2 casas.
A soma dos valores de todas as devoluções não pode ultrapassar o valor total do PIX original.
Exemplo: `"7.89"`
Indica a natureza da devolução solicitada:
* `ORIGINAL`: Devolução de PIX comum ou valor da compra em PIX Troco
* `RETIRADA`: Devolução de PIX Saque ou valor do troco em PIX Troco
Texto a ser apresentado ao pagador contendo informações sobre a devolução.
Máximo: 140 caracteres.
## Request
```bash cURL theme={null}
curl -X PUT https://api.public.firebanking.com.br/pix/E12345678901234567890123456789012/devolucao/D123456789 \
-H "Authorization: Bearer " \
-H "Content-Type: application/json" \
-d '{
"valor": "7.89",
"natureza": "ORIGINAL",
"descricao": "Devolução solicitada pelo recebedor"
}'
```
```typescript Node.js theme={null}
const e2eid = 'E12345678901234567890123456789012';
const devolucaoId = 'D123456789';
const response = await fetch(
`https://api.public.firebanking.com.br/pix/${e2eid}/devolucao/${devolucaoId}`,
{
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
valor: '7.89',
natureza: 'ORIGINAL',
descricao: 'Devolução solicitada pelo recebedor',
}),
}
);
const devolucao = await response.json();
```
```python Python theme={null}
import requests
e2eid = 'E12345678901234567890123456789012'
devolucao_id = 'D123456789'
response = requests.put(
f'https://api.public.firebanking.com.br/pix/{e2eid}/devolucao/{devolucao_id}',
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json',
},
json={
'valor': '7.89',
'natureza': 'ORIGINAL',
'descricao': 'Devolução solicitada pelo recebedor',
}
)
devolucao = response.json()
```
## Response
```json theme={null}
{
"id": "D123456789",
"rtrId": "D12345678901234567890123456789012",
"valor": "7.89",
"natureza": "ORIGINAL",
"descricao": "Devolução solicitada pelo recebedor",
"horario": {
"solicitacao": "2024-01-15T10:30:00.000Z"
},
"status": "EM_PROCESSAMENTO"
}
```
```json theme={null}
{
"statusCode": 400,
"message": "Valor deve estar no formato decimal com 2 casas (ex: 7.89)",
"error": "Bad Request"
}
```
```json theme={null}
{
"statusCode": 404,
"message": "Transação original não encontrada",
"error": "Not Found"
}
```
## Campos da Resposta
Identificação gerada pelo cliente para representar a devolução (mesmo valor enviado na URL).
Identificador único da transação de devolução. Contém 32 caracteres.
Valor da devolução no formato string com 2 casas decimais.
Natureza da devolução:
* `ORIGINAL`: Devolução comum
* `RETIRADA`: Devolução de saque
* `MED_OPERACIONAL`: Devolução MED por falha operacional
* `MED_FRAUDE`: Devolução MED por suspeita de fraude
Mensagem ao pagador relativa à devolução.
Horário no qual a devolução foi solicitada (ISO 8601).
Horário no qual a devolução foi liquidada (ISO 8601). Preenchido apenas quando `status = DEVOLVIDO`.
Status da devolução:
* `EM_PROCESSAMENTO`: Devolução em processamento
* `DEVOLVIDO`: Devolução realizada com sucesso
* `NAO_REALIZADO`: Devolução não realizada (falha)
Campo opcional com detalhes sobre o motivo do status atual. Preenchido principalmente em caso de falha.
## Status da Devolução
```mermaid theme={null}
stateDiagram-v2
[*] --> EM_PROCESSAMENTO: Solicitação enviada
EM_PROCESSAMENTO --> DEVOLVIDO: Sucesso
EM_PROCESSAMENTO --> NAO_REALIZADO: Falha
DEVOLVIDO --> [*]
NAO_REALIZADO --> [*]
```
## Webhook de Devolução
Quando a devolução for processada, você receberá um webhook V2 do tipo `REFUND`:
```json theme={null}
{
"type": "REFUND",
"data": {
"id": 123,
"txId": "original-txid",
"status": "REFUNDED",
"payment": {
"amount": "100.00",
"currency": "BRL"
},
"refunds": [
{
"status": "LIQUIDATED",
"payment": {
"amount": 7.89,
"currency": "BRL"
},
"endToEndId": "D12345678901234567890123456789012",
"eventDate": "2024-01-15T10:30:00.000Z",
"information": "Devolução solicitada pelo recebedor"
}
],
"endToEndId": "E12345678901234567890123456789012",
"creditDebitType": "DEBIT"
}
}
```
Veja a documentação completa do webhook REFUND
## Natureza da Devolução
| Natureza | Descrição |
| ----------------- | ------------------------------- |
| `ORIGINAL` | Devolução de PIX comum |
| `RETIRADA` | Devolução de PIX Saque ou Troco |
| `MED_OPERACIONAL` | MED por falha operacional |
| `MED_FRAUDE` | MED por suspeita de fraude |
Os valores `MED_OPERACIONAL` e `MED_FRAUDE` são retornados apenas na resposta, não podem ser enviados na requisição. São utilizados em casos específicos de Mecanismo Especial de Devolução (MED).
## Prazo para Devolução
Devoluções podem ser solicitadas em até **89 dias** após o recebimento do PIX original, conforme regulamentação do Banco Central.
## Devoluções Parciais
Você pode solicitar múltiplas devoluções parciais:
```typescript theme={null}
// Transação original: R$ 100,00
// Primeira devolução: R$ 30,00
await solicitarDevolucao(e2eid, 'DEV001', '30.00');
// Saldo disponível para devolução: R$ 70,00
// Segunda devolução: R$ 50,00
await solicitarDevolucao(e2eid, 'DEV002', '50.00');
// Saldo disponível para devolução: R$ 20,00
// Terceira devolução: R$ 25,00 - ERRO!
await solicitarDevolucao(e2eid, 'DEV003', '25.00');
// Falha: valor excede saldo disponível (R$ 20,00)
```
## Erros Comuns
| Código | Erro | Solução |
| ------ | ----------------------------------- | ------------------------------------------ |
| 400 | Valor inválido | Use formato "7.89" (string com 2 decimais) |
| 400 | Valor excede disponível | Verifique saldo disponível para devolução |
| 404 | Transação não encontrada | Verifique o e2eid informado |
| 404 | Transação não é do tipo recebimento | Devoluções só para PIX recebidos |
| 422 | Prazo expirado | Devoluções só até 89 dias após recebimento |
## Próximos Passos
Envie um PIX para outra conta
Processe notificações de devolução
# Transferência PIX
Source: https://docs.firebanking.dev/pix-bacen/endpoints/dict-pix
Inicie uma transferência PIX para uma chave DICT
## Visão Geral
O endpoint `POST /dict/pix` inicia uma transferência PIX para a chave informada. A chave pode ser CPF, CNPJ, e-mail, telefone ou EVP (chave aleatória).
Este endpoint segue a especificação do Banco Central para transferências PIX via DICT (Diretório de Identificadores de Contas Transacionais).
## Endpoint
```
POST /dict/pix
```
## Autenticação
Token Bearer obtido via `/oauth/token`.
Identificador único da requisição para suporte a idempotência. Deve ser um UUID v4.
Exemplo: `550e8400-e29b-41d4-a716-446655440000`
## Request Body
Chave PIX de destino. Pode ser:
* **CPF**: 11 dígitos numéricos
* **CNPJ**: 14 dígitos numéricos
* **E-mail**: endereço de e-mail válido
* **Telefone**: +55DDDNUMERO (ex: +5511999999999)
* **EVP**: Chave aleatória (UUID)
Documento do credor (CPF ou CNPJ). Obrigatório quando `priority = HIGH` para validação instantânea.
Prioridade do processamento:
* `HIGH`: Processado instantaneamente (requer `creditorDocument`)
* `NORM`: Processamento normal na fila
Mensagem que acompanha a transferência PIX. Será exibida para o destinatário.
Fluxo de pagamento (opcional). Utilizado para categorização interna.
Tempo máximo em segundos que a operação pode permanecer na fila antes de ser cancelada.
* Mínimo: 1 segundo
* Máximo: 10800 segundos (3 horas)
* Padrão: 600 segundos (10 minutos)
Dados do pagamento.
Moeda da transação. Atualmente apenas `BRL` é suportado.
Valor da transferência. **Número** com até 2 casas decimais.
Exemplo: `100.50`
Diferente do endpoint `/cob`, aqui o valor é **number**, não string.
Lista de códigos ISPB para os quais pagamentos não serão permitidos. Útil para bloquear transferências para instituições específicas.
Exemplo: `["12345678", "87654321"]`
## Request
```bash cURL theme={null}
curl -X POST https://api.public.firebanking.com.br/dict/pix \
-H "Authorization: Bearer " \
-H "x-idempotency-key: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{
"pixKey": "12345678909",
"creditorDocument": "12345678909",
"priority": "NORM",
"description": "Pagamento referente a NF 12345",
"expiration": 600,
"payment": {
"currency": "BRL",
"amount": 100.50
}
}'
```
```typescript Node.js theme={null}
const response = await fetch('https://api.public.firebanking.com.br/dict/pix', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'x-idempotency-key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify({
pixKey: '12345678909',
creditorDocument: '12345678909',
priority: 'NORM',
description: 'Pagamento referente a NF 12345',
expiration: 600,
payment: {
currency: 'BRL',
amount: 100.50,
},
}),
});
const transfer = await response.json();
```
```python Python theme={null}
import requests
import uuid
response = requests.post(
'https://api.public.firebanking.com.br/dict/pix',
headers={
'Authorization': f'Bearer {token}',
'x-idempotency-key': str(uuid.uuid4()),
'Content-Type': 'application/json',
},
json={
'pixKey': '12345678909',
'creditorDocument': '12345678909',
'priority': 'NORM',
'description': 'Pagamento referente a NF 12345',
'expiration': 600,
'payment': {
'currency': 'BRL',
'amount': 100.50,
},
}
)
transfer = response.json()
```
## Response
```json theme={null}
{
"endToEndId": "550e8400-e29b-41d4-a716-446655440000",
"eventDate": "2024-01-15T10:30:00.000Z",
"id": 12345,
"payment": {
"amount": 100.50
},
"type": "PENDING"
}
```
```json theme={null}
{
"statusCode": 400,
"message": "Chave PIX inválida",
"error": "Bad Request"
}
```
```json theme={null}
{
"statusCode": 422,
"message": "Chave PIX não encontrada no DICT",
"error": "Unprocessable Entity"
}
```
## Campos da Resposta
Identificador único da transação PIX. O valor real do E2E será enviado no webhook quando a transação for liquidada.
Data e hora do evento (ISO 8601).
Identificador único da transação.
Valor da transferência.
Tipo/status da transação:
* `PENDING`: Transferência em processamento
* `COMPLETED`: Transferência concluída
* `ERROR`: Falha na transferência
## Idempotência
O header `x-idempotency-key` garante que a mesma requisição não seja processada mais de uma vez:
```typescript theme={null}
// Mesma idempotency key = mesma resposta
const key = '550e8400-e29b-41d4-a716-446655440000';
// Primeira chamada - cria a transferência
const res1 = await createTransfer(key, { amount: 100 });
// { id: 123, type: 'PENDING' }
// Segunda chamada com mesma key - retorna a mesma transferência
const res2 = await createTransfer(key, { amount: 100 });
// { id: 123, type: 'PENDING' } (não cria nova)
```
O `x-idempotency-key` é **obrigatório**. Requisições sem este header serão rejeitadas.
## Webhook de Transferência
Quando a transferência for processada, você receberá um webhook V2 do tipo `TRANSFER`:
```json theme={null}
{
"type": "TRANSFER",
"data": {
"id": 12345,
"txId": null,
"pixKey": "12345678909",
"status": "LIQUIDATED",
"payment": {
"amount": "100.50",
"currency": "BRL"
},
"endToEndId": "E12345678901234567890123456789012",
"creditDebitType": "DEBIT",
"idempotencyKey": "550e8400-e29b-41d4-a716-446655440000",
"creditorAccount": {
"name": "João Silva",
"document": "123.xxx.xxx-xx",
"ispb": "18236120"
},
"remittanceInformation": "Pagamento referente a NF 12345"
}
}
```
Veja a documentação completa do webhook TRANSFER
## Tipos de Chave PIX
| Tipo | Formato | Exemplo |
| -------- | ------------ | -------------------------------------- |
| CPF | 11 dígitos | `12345678909` |
| CNPJ | 14 dígitos | `12345678000195` |
| E-mail | email válido | `joao@email.com` |
| Telefone | +55DDDNUMERO | `+5511999999999` |
| EVP | UUID | `7d9f0335-8dcc-4054-9bf9-0dbd61d36906` |
## Prioridade de Processamento
* Processamento padrão na fila
* Menor custo
* Tempo de processamento variável
* `creditorDocument` opcional
* Processamento instantâneo
* Validação imediata do destinatário
* `creditorDocument` **obrigatório**
* Ideal para pagamentos críticos
## Bloqueio de ISPBs
Use `ispbDeny` para bloquear transferências para instituições específicas:
```json theme={null}
{
"pixKey": "joao@email.com",
"payment": { "amount": 100.00 },
"ispbDeny": [
"12345678", // Bloquear banco X
"87654321" // Bloquear banco Y
]
}
```
Se a chave PIX pertencer a uma instituição bloqueada, a transferência será rejeitada.
## Erros Comuns
| Código | Erro | Solução |
| ------ | ------------------------ | -------------------------------------- |
| 400 | Chave PIX inválida | Verifique o formato da chave |
| 400 | Valor inválido | Use número com até 2 casas decimais |
| 400 | Idempotency key faltando | Inclua header `x-idempotency-key` |
| 401 | Token inválido | Renove o token de acesso |
| 422 | Chave não encontrada | A chave não existe no DICT |
| 422 | Saldo insuficiente | Verifique o saldo disponível |
| 422 | ISPB bloqueado | A instituição está na lista `ispbDeny` |
## Próximos Passos
Verifique o saldo disponível
Processe notificações de transferência
# Introdução
Source: https://docs.firebanking.dev/pix-bacen/introduction
API PIX compatível com a especificação do Banco Central do Brasil
## O que é PIX Bacen?
A **API PIX Bacen** é uma versão da API Fire Banking que segue a especificação oficial do Banco Central do Brasil para o sistema de pagamentos instantâneos PIX. Esta versão foi desenvolvida para atender integradores que precisam de compatibilidade com o formato padrão BACEN.
Esta API é uma alternativa à [API padrão Fire Banking](/api-reference/introduction). Ambas oferecem as mesmas funcionalidades, mas com formatos de requisição e resposta diferentes.
## Quando usar a API PIX Bacen?
Use esta API quando:
* Seu sistema já está integrado com outros PSPs que seguem a especificação BACEN
* Você precisa manter compatibilidade com múltiplos provedores PIX
* Sua aplicação foi construída seguindo a documentação oficial do Banco Central
* Você prefere trabalhar com o formato de webhook V2 (envelope `{type, data}`)
## Principais diferenças
Valores monetários são **strings** com 2 casas decimais (ex: `"123.45"`) ao invés de números.
Webhooks usam formato envelope `{type, data}` com status `LIQUIDATED` ao invés de `CONFIRMED`.
Usa `txid` para cobranças e `e2eid` para devoluções, seguindo nomenclatura BACEN.
Contraparte dividida em `debtorAccount` (pagador) e `creditorAccount` (recebedor).
## Endpoints disponíveis
| Endpoint | Método | Descrição |
| --------------------------- | ------ | -------------------------------------- |
| `/cob/:txid` | PUT | Criar cobrança imediata (QR Code PIX) |
| `/pix/:e2eid/devolucao/:id` | PUT | Solicitar devolução de um PIX recebido |
| `/dict/pix` | POST | Iniciar transferência PIX (Cash-Out) |
| `/accounts/balances` | GET | Consultar saldo da conta |
## Comparação com API padrão
| Operação | API Padrão | API PIX Bacen |
| -------- | ---------------------- | ------------------------------- |
| Cash-In | `POST /pix/cash-in` | `PUT /cob/:txid` |
| Cash-Out | `POST /pix/cash-out` | `POST /dict/pix` |
| Refund | `POST /pix/:id/refund` | `PUT /pix/:e2eid/devolucao/:id` |
| Balance | `GET /balance` | `GET /accounts/balances` |
## Fluxo de integração
```mermaid theme={null}
sequenceDiagram
participant Cliente
participant Fire Banking
participant BACEN
Note over Cliente,BACEN: 1. Autenticação
Cliente->>Fire Banking: POST /oauth/token
Fire Banking-->>Cliente: access_token
Note over Cliente,BACEN: 2. Criar Cobrança
Cliente->>Fire Banking: PUT /cob/{txid}
Fire Banking->>BACEN: Registra cobrança
Fire Banking-->>Cliente: QR Code + dados
Note over Cliente,BACEN: 3. Pagamento (via app bancário)
BACEN->>Fire Banking: Webhook pagamento
Fire Banking->>Cliente: Webhook V2 (type: RECEIVE)
```
## Próximos passos
Configure a autenticação para acessar a API
Saiba como ativar o modo PIX Bacen na sua conta
Gere sua primeira cobrança PIX
Entenda o formato de notificações V2
# Visão Geral
Source: https://docs.firebanking.dev/pix-bacen/webhooks/overview
Entenda o formato de webhooks V2 usado na API PIX Bacen
## O que são Webhooks V2?
Webhooks V2 são notificações enviadas para sua aplicação quando eventos importantes ocorrem em suas transações PIX. O formato V2 usa uma estrutura de envelope `{type, data}` que facilita o processamento e oferece mais detalhes sobre cada evento.
Para receber webhooks V2, sua conta deve estar com a versão de webhook configurada para V2. Veja [como ativar](/pix-bacen/activation).
## Estrutura Base
Todos os webhooks V2 seguem esta estrutura:
```json theme={null}
{
"type": "RECEIVE" | "TRANSFER" | "REFUND",
"data": {
// Dados específicos do evento
}
}
```
## Tipos de Evento
| Type | Descrição | Equivalente V1 |
| ---------- | ---------------------- | ------------------------------------ |
| `RECEIVE` | PIX recebido (Cash-In) | `CashIn` |
| `TRANSFER` | PIX enviado (Cash-Out) | `CashOut` |
| `REFUND` | Devolução (In ou Out) | `CashInReversal` / `CashOutReversal` |
## Estrutura Completa do Data
```typescript theme={null}
interface WebhookV2Data {
// Identificadores
id: number; // ID da transação
txId: string | null; // Identificador da cobrança (txid)
endToEndId: string | null; // End to End ID da transação PIX
// Chave PIX
pixKey: string | null; // Chave PIX utilizada
// Status
status: 'PENDING' | 'LIQUIDATED' | 'REFUNDED' | 'ERROR';
// Pagamento
payment: {
amount: string; // Valor (string com 2 decimais)
currency: string; // Moeda (BRL)
};
// Devoluções
refunds: RefundInfo[]; // Lista de devoluções (vazio se não houver)
// Datas
createdAt: string; // Data de criação (ISO 8601)
// Erro
errorCode: string | null; // Código de erro (se houver)
// Tipo de operação
webhookType: 'RECEIVE' | 'TRANSFER' | 'REFUND';
creditDebitType: 'CREDIT' | 'DEBIT';
transactionType: 'PIX';
localInstrument: 'DICT';
// Contas
debtorAccount: AccountInfo; // Pagador/Remetente
creditorAccount: AccountInfo; // Recebedor/Destinatário
// Idempotência
idempotencyKey: string | null;
// Dados adicionais
ticketData: object;
remittanceInformation: string | null; // Descrição da transação
}
interface AccountInfo {
ispb: string | null; // Código ISPB do banco
name: string | null; // Nome do banco
issuer: string | null; // Código do banco
number: string | null; // Número da conta
document: string | null; // CPF/CNPJ (mascarado)
accountType: string | null; // Tipo de conta
}
interface RefundInfo {
status: 'PENDING' | 'LIQUIDATED' | 'ERROR';
payment: {
amount: number; // Valor do refund (number!)
currency: string;
};
errorCode: string | null;
eventDate: string; // Data do refund
endToEndId: string | null; // E2E ID do refund
information: string | null; // Descrição do refund
}
```
## Diferenças V1 vs V2
| Aspecto | V1 | V2 |
| ----------- | ----------------- | ----------------------- |
| Formato | Campos na raiz | Envelope `{type, data}` |
| Tipo evento | `event: "CashIn"` | `type: "RECEIVE"` |
| Aspecto | V1 | V2 |
| -------------- | ----------- | ------------ |
| Sucesso PIX | `CONFIRMED` | `LIQUIDATED` |
| Sucesso Refund | `CONFIRMED` | `REFUNDED` |
| Erro | `ERROR` | `ERROR` |
| Aspecto | V1 | V2 |
| ------- | --------------- | ----------------------------------- |
| Campo | `counterpart` | `debtorAccount` / `creditorAccount` |
| Banco | `bank.bankName` | `name` |
| ISPB | `bank.bankISPB` | `ispb` |
| Aspecto | V1 | V2 |
| ------- | -------- | ---------- |
| Tipo | `number` | `string` |
| Formato | `100.00` | `"100.00"` |
## Mapeamento de Contas
### PIX Recebido (RECEIVE)
```
debtorAccount = Quem pagou (contraparte)
creditorAccount = Sua conta (recebedor)
creditDebitType = CREDIT
```
### PIX Enviado (TRANSFER)
```
debtorAccount = Sua conta (pagador)
creditorAccount = Quem recebeu (contraparte)
creditDebitType = DEBIT
```
### Devolução de Recebimento (REFUND - CashInReversal)
```
debtorAccount = Sua conta (devolvendo)
creditorAccount = Quem vai receber de volta (contraparte)
creditDebitType = DEBIT
```
### Devolução de Envio (REFUND - CashOutReversal)
```
debtorAccount = Quem está devolvendo (contraparte)
creditorAccount = Sua conta (recebendo de volta)
creditDebitType = CREDIT
```
## Configuração do Endpoint
### Requisitos
* URL HTTPS obrigatória
* Timeout máximo: 10 segundos
* Resposta esperada: HTTP 2xx
### Autenticação
Os webhooks são enviados com Basic Auth:
```
Authorization: Basic base64(username:password)
```
Configure as credenciais no painel ou entre em contato com o suporte.
## Exemplo de Handler
```typescript theme={null}
import express from 'express';
const app = express();
app.use(express.json());
interface WebhookV2 {
type: 'RECEIVE' | 'TRANSFER' | 'REFUND';
data: WebhookV2Data;
}
// Set para idempotência
const processedIds = new Set();
app.post('/webhooks/pix', (req, res) => {
const webhook: WebhookV2 = req.body;
// Responder rapidamente
res.status(200).json({ acknowledged: true });
// Verificar idempotência
if (processedIds.has(webhook.data.id)) {
console.log(`Webhook ${webhook.data.id} já processado`);
return;
}
processedIds.add(webhook.data.id);
// Processar por tipo
switch (webhook.type) {
case 'RECEIVE':
handleReceive(webhook.data);
break;
case 'TRANSFER':
handleTransfer(webhook.data);
break;
case 'REFUND':
handleRefund(webhook.data);
break;
}
});
function handleReceive(data: WebhookV2Data) {
if (data.status === 'LIQUIDATED') {
const amount = parseFloat(data.payment.amount);
console.log(`PIX recebido: R$ ${amount}`);
// Creditar no sistema
}
}
function handleTransfer(data: WebhookV2Data) {
if (data.status === 'LIQUIDATED') {
console.log(`PIX enviado: ${data.endToEndId}`);
// Confirmar transferência
} else if (data.status === 'ERROR') {
console.log(`PIX falhou: ${data.errorCode}`);
// Reverter operação
}
}
function handleRefund(data: WebhookV2Data) {
if (data.status === 'REFUNDED') {
const refund = data.refunds[0];
console.log(`Devolução: R$ ${refund.payment.amount}`);
// Processar devolução
}
}
```
## Retentativas
Se seu endpoint não responder com HTTP 2xx em 10 segundos:
| Tentativa | Intervalo | Acumulado |
| --------- | ---------- | --------- |
| 1ª | Imediato | 0 min |
| 2ª | 5 minutos | 5 min |
| 3ª | 5 minutos | 10 min |
| 4ª | 15 minutos | 25 min |
Após 4 tentativas sem sucesso, o webhook não será mais reenviado automaticamente.
Implemente consulta periódica como fallback para garantir que nenhuma transação seja perdida.
## Próximos Passos
PIX recebido
PIX enviado
Devolução
# RECEIVE
Source: https://docs.firebanking.dev/pix-bacen/webhooks/receive
Webhook enviado quando um PIX é recebido (Cash-In)
## Visão Geral
O webhook `RECEIVE` é enviado quando um PIX é recebido na sua conta. Este evento indica que alguém pagou um QR Code gerado pela sua aplicação ou fez uma transferência direta para sua chave PIX.
## Quando é enviado
* Pagamento de QR Code (cobrança) confirmado
* Transferência direta para chave PIX da conta
## Estrutura do Payload
```json theme={null}
{
"type": "RECEIVE",
"data": {
"id": 123,
"txId": "7978c0c97ea847e78e8849634473c1f1",
"pixKey": "7d9f0335-8dcc-4054-9bf9-0dbd61d36906",
"status": "LIQUIDATED",
"payment": {
"amount": "100.00",
"currency": "BRL"
},
"refunds": [],
"createdAt": "2024-01-15T10:30:00.000Z",
"errorCode": null,
"endToEndId": "E12345678901234567890123456789012",
"ticketData": {},
"webhookType": "RECEIVE",
"debtorAccount": {
"ispb": "18236120",
"name": "NU PAGAMENTOS S.A.",
"issuer": "260",
"number": "12345-6",
"document": "123.xxx.xxx-xx",
"accountType": null
},
"idempotencyKey": null,
"creditDebitType": "CREDIT",
"creditorAccount": {
"ispb": null,
"name": null,
"issuer": null,
"number": null,
"document": null,
"accountType": null
},
"localInstrument": "DICT",
"transactionType": "PIX",
"remittanceInformation": "Pagamento pedido #12345"
}
}
```
## Campos Importantes
Sempre `"RECEIVE"` para PIX recebido.
ID da transação. Use para idempotência.
Identificador da cobrança (txid do endpoint `/cob`). Pode ser `null` para transferências diretas.
End to End ID - identificador único da transação PIX no Banco Central.
Status da transação:
* `LIQUIDATED`: Pagamento confirmado (sucesso)
* `ERROR`: Falha no processamento
Valor recebido. **String** com 2 casas decimais.
Moeda. Sempre `"BRL"`.
Dados de quem pagou (o pagador/remetente).
Código ISPB do banco do pagador.
Nome do banco do pagador.
Código do banco (ex: "260" para Nubank).
Número da conta do pagador.
CPF/CNPJ do pagador (mascarado).
Sempre `"CREDIT"` para recebimentos.
Lista de devoluções. Vazio para transações sem devolução.
Descrição da transferência (se informada pelo pagador).
## Processando o Webhook
### Exemplo Node.js
```typescript theme={null}
interface ReceiveWebhook {
type: 'RECEIVE';
data: {
id: number;
txId: string | null;
status: 'LIQUIDATED' | 'ERROR';
payment: {
amount: string;
currency: string;
};
endToEndId: string;
debtorAccount: {
name: string | null;
document: string | null;
};
remittanceInformation: string | null;
};
}
async function handleReceive(webhook: ReceiveWebhook) {
const { data } = webhook;
if (data.status !== 'LIQUIDATED') {
console.log(`PIX não confirmado: ${data.status}`);
return;
}
// Converter valor de string para number
const amount = parseFloat(data.payment.amount);
// Encontrar pedido pelo txId (se for cobrança)
if (data.txId) {
const order = await findOrderByTxId(data.txId);
if (order) {
await markOrderAsPaid(order.id, {
amount,
endToEndId: data.endToEndId,
payer: data.debtorAccount.name,
});
return;
}
}
// Recebimento sem cobrança associada
await createGenericCredit({
amount,
endToEndId: data.endToEndId,
payer: data.debtorAccount.name,
description: data.remittanceInformation,
});
}
```
### Exemplo Python
```python theme={null}
from decimal import Decimal
def handle_receive(webhook: dict):
data = webhook['data']
if data['status'] != 'LIQUIDATED':
print(f"PIX não confirmado: {data['status']}")
return
# Converter valor
amount = Decimal(data['payment']['amount'])
# Processar por txId se existir
if data.get('txId'):
order = find_order_by_txid(data['txId'])
if order:
mark_order_as_paid(
order_id=order.id,
amount=amount,
e2e_id=data['endToEndId'],
payer=data['debtorAccount'].get('name')
)
return
# Crédito genérico
create_generic_credit(
amount=amount,
e2e_id=data['endToEndId'],
payer=data['debtorAccount'].get('name'),
description=data.get('remittanceInformation')
)
```
## Correlação com Cobrança
Se o PIX foi pago via QR Code gerado pelo endpoint `/cob/:txid`, o campo `txId` conterá o identificador:
```json theme={null}
{
"type": "RECEIVE",
"data": {
"txId": "7978c0c97ea847e78e8849634473c1f1", // Mesmo txid do PUT /cob
// ...
}
}
```
Use este campo para correlacionar com seus registros internos:
```typescript theme={null}
// Criar cobrança
const cobranca = await createCob('meu-txid-123', { valor: '100.00' });
// Salvar associação
await saveOrder({
orderId: 'pedido-456',
txId: 'meu-txid-123',
status: 'PENDING'
});
// No webhook RECEIVE
if (webhook.data.txId === 'meu-txid-123') {
await updateOrder('pedido-456', { status: 'PAID' });
}
```
## Tratamento de Erros
Se `status === 'ERROR'`, verifique o campo `errorCode`:
```typescript theme={null}
if (data.status === 'ERROR') {
console.error(`Erro no PIX: ${data.errorCode}`);
// Notificar sobre falha
await notifyPaymentError({
txId: data.txId,
errorCode: data.errorCode,
});
}
```
## Idempotência
Use `data.id` para evitar processamento duplicado:
```typescript theme={null}
const PROCESSED_KEY = 'processed_webhooks';
async function handleWebhook(webhook: ReceiveWebhook) {
const webhookId = `receive:${webhook.data.id}`;
// Verificar se já processou
const isProcessed = await redis.sismember(PROCESSED_KEY, webhookId);
if (isProcessed) {
console.log(`Webhook ${webhookId} já processado`);
return;
}
// Marcar como processado ANTES de processar
await redis.sadd(PROCESSED_KEY, webhookId);
// Processar
await handleReceive(webhook);
}
```
## Boas Práticas
Retorne HTTP 200 imediatamente e processe de forma assíncrona.
```typescript theme={null}
app.post('/webhook', (req, res) => {
res.status(200).send(); // Responder primeiro
handleWebhook(req.body) // Processar depois
.catch(console.error);
});
```
Sempre verifique se `status === 'LIQUIDATED'` antes de creditar.
Se você criou a cobrança via `/cob`, use o `txId` para encontrar o pedido correspondente.
```typescript theme={null}
console.log({
event: 'PIX_RECEIVED',
id: data.id,
txId: data.txId,
amount: data.payment.amount,
e2eId: data.endToEndId,
payer: data.debtorAccount.name,
});
```
## Próximos Passos
PIX enviado
Devolução
# REFUND
Source: https://docs.firebanking.dev/pix-bacen/webhooks/refund
Webhook enviado quando uma devolução PIX é processada
## Visão Geral
O webhook `REFUND` é enviado quando uma devolução PIX é processada. Existem dois cenários:
1. **CashInReversal**: Você devolveu um PIX recebido (via `/pix/:e2eid/devolucao/:id`)
2. **CashOutReversal**: Alguém devolveu um PIX que você enviou
## Quando é enviado
* Devolução de PIX recebido confirmada (você devolvendo)
* Devolução de PIX enviado recebida (alguém devolvendo para você)
## Estrutura do Payload
```json theme={null}
{
"type": "REFUND",
"data": {
"id": 123,
"txId": "7978c0c97ea847e78e8849634473c1f1",
"pixKey": "7d9f0335-8dcc-4054-9bf9-0dbd61d36906",
"status": "REFUNDED",
"payment": {
"amount": "100.00",
"currency": "BRL"
},
"refunds": [
{
"status": "LIQUIDATED",
"payment": {
"amount": 50.00,
"currency": "BRL"
},
"errorCode": null,
"eventDate": "2024-01-15T10:30:00.000Z",
"endToEndId": "D12345678901234567890123456789012",
"information": "Devolução solicitada pelo recebedor"
}
],
"createdAt": "2024-01-15T09:00:00.000Z",
"errorCode": null,
"endToEndId": "E12345678901234567890123456789012",
"ticketData": {},
"webhookType": "REFUND",
"debtorAccount": {
"ispb": null,
"name": null,
"issuer": null,
"number": null,
"document": null,
"accountType": null
},
"idempotencyKey": "7978c0c97ea847e78e8849634473c1f1",
"creditDebitType": "DEBIT",
"creditorAccount": {
"ispb": "18236120",
"name": "NU PAGAMENTOS S.A.",
"issuer": "260",
"number": "12345-6",
"document": "123.xxx.xxx-xx",
"accountType": null
},
"localInstrument": "DICT",
"transactionType": "PIX",
"remittanceInformation": "Devolução parcial"
}
}
```
## Diferença entre CashInReversal e CashOutReversal
**Você devolveu** um PIX recebido.
```
creditDebitType = DEBIT (saindo da sua conta)
debtorAccount = Sua conta
creditorAccount = Quem vai receber de volta
```
**Exemplo**: Você recebeu R$ 100, depois devolveu R$ 50.
**Alguém devolveu** um PIX que você enviou.
```
creditDebitType = CREDIT (entrando na sua conta)
debtorAccount = Quem está devolvendo
creditorAccount = Sua conta
```
**Exemplo**: Você enviou R$ 100, o destinatário devolveu R$ 30.
## Campos Importantes
Sempre `"REFUND"` para devoluções.
ID da transação **original** (não da devolução).
Status da transação original após a devolução:
* `REFUNDED`: Devolução processada
* `ERROR`: Falha na devolução
Valor da transação **original**, não da devolução.
Valor original. **String** com 2 casas decimais.
Moeda. Sempre `"BRL"`.
Lista de devoluções realizadas. Contém detalhes de cada devolução.
Status da devolução: `LIQUIDATED` ou `ERROR`.
Valor da devolução. **Atenção**: É `number`, não `string`!
E2E ID da devolução (diferente do E2E da transação original).
Data/hora da devolução.
Descrição da devolução.
Direção do dinheiro:
* `DEBIT`: Saindo da sua conta (CashInReversal)
* `CREDIT`: Entrando na sua conta (CashOutReversal)
E2E ID da transação **original**.
## Processando o Webhook
### Exemplo Node.js
```typescript theme={null}
interface RefundWebhook {
type: 'REFUND';
data: {
id: number;
txId: string | null;
status: 'REFUNDED' | 'ERROR';
payment: {
amount: string;
currency: string;
};
refunds: Array<{
status: 'LIQUIDATED' | 'ERROR';
payment: {
amount: number; // number, não string!
currency: string;
};
endToEndId: string;
eventDate: string;
information: string | null;
}>;
endToEndId: string;
creditDebitType: 'CREDIT' | 'DEBIT';
};
}
async function handleRefund(webhook: RefundWebhook) {
const { data } = webhook;
// Identificar tipo de devolução
const isCashInReversal = data.creditDebitType === 'DEBIT';
if (isCashInReversal) {
// Você devolveu um PIX recebido
await handleCashInReversal(data);
} else {
// Alguém devolveu um PIX que você enviou
await handleCashOutReversal(data);
}
}
async function handleCashInReversal(data: RefundWebhook['data']) {
// Encontrar transação original
const original = await findTransactionByE2eId(data.endToEndId);
// Processar cada devolução
for (const refund of data.refunds) {
if (refund.status === 'LIQUIDATED') {
// Devolução confirmada - debitar do saldo
await processRefundOut({
originalId: original.id,
refundAmount: refund.payment.amount, // já é number
refundE2eId: refund.endToEndId,
});
console.log(`Devolvido R$ ${refund.payment.amount} do PIX ${original.id}`);
}
}
}
async function handleCashOutReversal(data: RefundWebhook['data']) {
// Encontrar transferência original
const original = await findTransferByE2eId(data.endToEndId);
// Processar cada devolução recebida
for (const refund of data.refunds) {
if (refund.status === 'LIQUIDATED') {
// Devolução recebida - creditar no saldo
await processRefundIn({
originalId: original.id,
refundAmount: refund.payment.amount,
refundE2eId: refund.endToEndId,
});
console.log(`Recebido R$ ${refund.payment.amount} de devolução`);
}
}
}
```
### Exemplo Python
```python theme={null}
from decimal import Decimal
def handle_refund(webhook: dict):
data = webhook['data']
# Identificar tipo
is_cash_in_reversal = data['creditDebitType'] == 'DEBIT'
if is_cash_in_reversal:
handle_cash_in_reversal(data)
else:
handle_cash_out_reversal(data)
def handle_cash_in_reversal(data: dict):
"""Você devolveu um PIX recebido"""
original = find_transaction_by_e2e(data['endToEndId'])
for refund in data['refunds']:
if refund['status'] == 'LIQUIDATED':
# Já é number, converter para Decimal
amount = Decimal(str(refund['payment']['amount']))
process_refund_out(
original_id=original.id,
refund_amount=amount,
refund_e2e=refund['endToEndId']
)
def handle_cash_out_reversal(data: dict):
"""Alguém devolveu um PIX que você enviou"""
original = find_transfer_by_e2e(data['endToEndId'])
for refund in data['refunds']:
if refund['status'] == 'LIQUIDATED':
amount = Decimal(str(refund['payment']['amount']))
process_refund_in(
original_id=original.id,
refund_amount=amount,
refund_e2e=refund['endToEndId']
)
```
## Devoluções Parciais
Uma transação pode ter múltiplas devoluções parciais. O array `refunds` contém todas:
```json theme={null}
{
"type": "REFUND",
"data": {
"payment": { "amount": "100.00" }, // Valor original: R$ 100
"refunds": [
{
"payment": { "amount": 30.00 }, // 1ª devolução: R$ 30
"eventDate": "2024-01-15T10:00:00Z"
},
{
"payment": { "amount": 50.00 }, // 2ª devolução: R$ 50
"eventDate": "2024-01-15T11:00:00Z"
}
]
}
}
```
**Cálculo do saldo de devolução:**
```typescript theme={null}
const valorOriginal = parseFloat(data.payment.amount); // 100.00
const totalDevolvido = data.refunds
.filter(r => r.status === 'LIQUIDATED')
.reduce((sum, r) => sum + r.payment.amount, 0); // 80.00
const saldoDisponivel = valorOriginal - totalDevolvido; // 20.00
```
## Atenção: amount é number em refunds
Dentro do array `refunds`, o campo `payment.amount` é **number**, não **string**!
```typescript theme={null}
// data.payment.amount → string "100.00"
// data.refunds[0].payment.amount → number 50.00
// CORRETO
const refundAmount = data.refunds[0].payment.amount; // 50.00 (number)
// ERRADO - não precisa de parseFloat
const refundAmount = parseFloat(data.refunds[0].payment.amount);
```
## Idempotência
Use uma combinação de `data.id` e `refunds[].endToEndId` para idempotência:
```typescript theme={null}
async function handleWebhook(webhook: RefundWebhook) {
for (const refund of webhook.data.refunds) {
const key = `refund:${webhook.data.id}:${refund.endToEndId}`;
const isProcessed = await redis.sismember('processed', key);
if (isProcessed) {
continue; // Já processado
}
await redis.sadd('processed', key);
await processRefund(webhook.data, refund);
}
}
```
## Tratamento de Erros
Se `refund.status === 'ERROR'`, a devolução falhou:
```typescript theme={null}
for (const refund of data.refunds) {
if (refund.status === 'ERROR') {
console.error(`Devolução falhou: ${refund.errorCode}`);
// Notificar sobre falha
await notifyRefundFailed({
originalE2eId: data.endToEndId,
refundE2eId: refund.endToEndId,
errorCode: refund.errorCode,
});
}
}
```
## Boas Práticas
Use `creditDebitType` para determinar se é CashInReversal (DEBIT) ou CashOutReversal (CREDIT).
O array `refunds` pode conter múltiplas devoluções parciais. Itere por todas.
* `data.payment.amount` é **string**
* `data.refunds[].payment.amount` é **number**
* CashInReversal: Debita do seu saldo
* CashOutReversal: Credita no seu saldo
## Próximos Passos
PIX recebido
PIX enviado
# TRANSFER
Source: https://docs.firebanking.dev/pix-bacen/webhooks/transfer
Webhook enviado quando um PIX é enviado (Cash-Out)
## Visão Geral
O webhook `TRANSFER` é enviado quando uma transferência PIX iniciada pela sua aplicação é processada. Este evento indica o resultado (sucesso ou falha) de uma chamada ao endpoint `/dict/pix`.
## Quando é enviado
* Transferência PIX processada com sucesso (`LIQUIDATED`)
* Transferência PIX falhou (`ERROR`)
## Estrutura do Payload
```json theme={null}
{
"type": "TRANSFER",
"data": {
"id": 456,
"txId": null,
"pixKey": "destino@email.com",
"status": "LIQUIDATED",
"payment": {
"amount": "100.50",
"currency": "BRL"
},
"refunds": [],
"createdAt": "2024-01-15T10:30:00.000Z",
"errorCode": null,
"endToEndId": "E12345678901234567890123456789012",
"ticketData": {},
"webhookType": "TRANSFER",
"debtorAccount": {
"ispb": null,
"name": null,
"issuer": null,
"number": null,
"document": null,
"accountType": null
},
"idempotencyKey": "550e8400-e29b-41d4-a716-446655440000",
"creditDebitType": "DEBIT",
"creditorAccount": {
"ispb": "18236120",
"name": "NU PAGAMENTOS S.A.",
"issuer": "260",
"number": "12345-6",
"document": "123.xxx.xxx-xx",
"accountType": null
},
"localInstrument": "DICT",
"transactionType": "PIX",
"remittanceInformation": "Pagamento NF 12345"
}
}
```
## Campos Importantes
Sempre `"TRANSFER"` para PIX enviado.
ID da transação. Mesmo valor retornado no `POST /dict/pix`.
End to End ID - identificador único da transação PIX no Banco Central.
Status da transferência:
* `LIQUIDATED`: Transferência confirmada (sucesso)
* `ERROR`: Falha na transferência
Valor transferido. **String** com 2 casas decimais.
Moeda. Sempre `"BRL"`.
Chave de idempotência enviada no header `x-idempotency-key` da requisição original.
Dados de quem recebeu (o destinatário).
Código ISPB do banco do destinatário.
Nome do banco do destinatário.
Código do banco (ex: "260" para Nubank).
Número da conta do destinatário.
CPF/CNPJ do destinatário (mascarado).
Sempre `"DEBIT"` para transferências enviadas.
Código de erro quando `status === 'ERROR'`. Pode ser `null` em caso de sucesso.
Descrição da transferência (campo `description` enviado na requisição).
## Processando o Webhook
### Exemplo Node.js
```typescript theme={null}
interface TransferWebhook {
type: 'TRANSFER';
data: {
id: number;
status: 'LIQUIDATED' | 'ERROR';
payment: {
amount: string;
currency: string;
};
endToEndId: string;
idempotencyKey: string;
creditorAccount: {
name: string | null;
document: string | null;
};
errorCode: string | null;
};
}
async function handleTransfer(webhook: TransferWebhook) {
const { data } = webhook;
// Encontrar transferência pelo idempotencyKey
const transfer = await findTransferByIdempotencyKey(data.idempotencyKey);
if (!transfer) {
console.warn(`Transferência não encontrada: ${data.idempotencyKey}`);
return;
}
if (data.status === 'LIQUIDATED') {
// Sucesso - confirmar transferência
await updateTransfer(transfer.id, {
status: 'COMPLETED',
endToEndId: data.endToEndId,
completedAt: new Date(),
});
// Notificar usuário
await notifyTransferSuccess({
transferId: transfer.id,
amount: parseFloat(data.payment.amount),
recipient: data.creditorAccount.name,
});
} else if (data.status === 'ERROR') {
// Falha - reverter
await updateTransfer(transfer.id, {
status: 'FAILED',
errorCode: data.errorCode,
});
// Notificar usuário
await notifyTransferFailed({
transferId: transfer.id,
errorCode: data.errorCode,
});
// Liberar saldo bloqueado
await releaseBlockedBalance(transfer.id);
}
}
```
### Exemplo Python
```python theme={null}
from decimal import Decimal
def handle_transfer(webhook: dict):
data = webhook['data']
# Encontrar transferência
transfer = find_transfer_by_idempotency_key(data['idempotencyKey'])
if not transfer:
print(f"Transferência não encontrada: {data['idempotencyKey']}")
return
if data['status'] == 'LIQUIDATED':
# Sucesso
update_transfer(
transfer_id=transfer.id,
status='COMPLETED',
e2e_id=data['endToEndId']
)
notify_transfer_success(
transfer_id=transfer.id,
amount=Decimal(data['payment']['amount']),
recipient=data['creditorAccount'].get('name')
)
elif data['status'] == 'ERROR':
# Falha
update_transfer(
transfer_id=transfer.id,
status='FAILED',
error_code=data['errorCode']
)
notify_transfer_failed(
transfer_id=transfer.id,
error_code=data['errorCode']
)
# Liberar saldo
release_blocked_balance(transfer.id)
```
## Correlação com Requisição
Use `idempotencyKey` para correlacionar o webhook com sua requisição original:
```typescript theme={null}
// 1. Criar transferência
const idempotencyKey = crypto.randomUUID();
const transfer = await createTransfer(idempotencyKey, {
pixKey: 'destino@email.com',
amount: 100.50,
});
// 2. Salvar associação
await saveTransfer({
id: transfer.id,
idempotencyKey,
status: 'PENDING',
});
// 3. No webhook TRANSFER
const savedTransfer = await findByIdempotencyKey(webhook.data.idempotencyKey);
// savedTransfer.id corresponde à transferência original
```
## Tratamento de Erros
Códigos de erro comuns:
| Código | Descrição | Ação Recomendada |
| ---------------------- | ---------------------------- | ----------------------------------- |
| `INSUFFICIENT_BALANCE` | Saldo insuficiente | Verificar saldo antes de transferir |
| `INVALID_KEY` | Chave PIX inválida | Verificar chave com usuário |
| `KEY_NOT_FOUND` | Chave não encontrada no DICT | Solicitar chave válida |
| `ACCOUNT_BLOCKED` | Conta bloqueada | Contatar suporte |
| `TIMEOUT` | Timeout no processamento | Tentar novamente |
```typescript theme={null}
if (data.status === 'ERROR') {
switch (data.errorCode) {
case 'INSUFFICIENT_BALANCE':
// Notificar saldo insuficiente
await notifyInsufficientBalance(transfer);
break;
case 'INVALID_KEY':
case 'KEY_NOT_FOUND':
// Solicitar nova chave ao usuário
await requestNewPixKey(transfer);
break;
case 'TIMEOUT':
// Pode tentar novamente com nova idempotency key
await retryTransfer(transfer);
break;
default:
// Erro genérico
await notifyGenericError(transfer, data.errorCode);
}
}
```
## Fluxo de Saldo
```mermaid theme={null}
sequenceDiagram
participant App
participant API
participant Banco
Note over App,Banco: Saldo: available=1000, blocked=0
App->>API: POST /dict/pix (R$ 100)
API-->>App: { type: PENDING }
Note over App,Banco: Saldo: available=900, blocked=100
alt Sucesso
Banco->>API: Confirmação
API->>App: Webhook TRANSFER (LIQUIDATED)
Note over App,Banco: Saldo: available=900, blocked=0
else Falha
Banco->>API: Erro
API->>App: Webhook TRANSFER (ERROR)
Note over App,Banco: Saldo: available=1000, blocked=0
end
```
## Idempotência
Use `data.id` para evitar processamento duplicado:
```typescript theme={null}
async function handleWebhook(webhook: TransferWebhook) {
const webhookId = `transfer:${webhook.data.id}`;
const isProcessed = await redis.sismember('processed', webhookId);
if (isProcessed) {
return; // Já processado
}
await redis.sadd('processed', webhookId);
await handleTransfer(webhook);
}
```
## Boas Práticas
Salve o `idempotencyKey` junto com a transferência para facilitar a correlação no webhook.
Implemente tratamento tanto para `LIQUIDATED` quanto para `ERROR`.
Se a transferência falhar, o saldo bloqueado deve ser liberado. Certifique-se de atualizar seu sistema.
Informe o usuário sobre o resultado da transferência, especialmente em caso de falha.
## Próximos Passos
PIX recebido
Devolução