Integração com IA

Contexto estruturado completo para LLMs gerarem integrações BSPAY corretas

Cada vez mais empresas usam LLMs para gerar integrações, automatizar pagamentos e construir agentes autônomos. Este guia traz o contexto completo que qualquer modelo (ChatGPT, Claude, Gemini, Llama, etc.) precisa para escrever código correto na primeira tentativa.


Bloco 1 — Contexto Geral (cole sempre)

# BSPAY API v2 — Contexto Completo

## Base
- URL: https://api.bspay.co
- Auth: OAuth2 Client Credentials (Bearer JWT, 1h)
- Formato: JSON (Content-Type: application/json)
- Idempotência: header `external_id` em quase todas operações financeiras

## Resposta padrão
- Sucesso: { "success": true, "data": {...}, "request_id": "...", "timestamp": "..." }
- Erro:    { "success": false, "error": { "code": "...", "message": "...", "group": "...", "retryable": bool, "details": {...} } }

## Status HTTP relevantes
- 200 OK síncrono | 202 Accepted async (cashout, transfer, conversion)
- 400 payload | 401 auth/HMAC | 403 perm/IP/conta | 404 not found
- 409 duplicado/limite | 422 valor/saldo | 423 conta bloqueada | 429 rate limit
- 502/503 provedor (retryable)

## Moedas suportadas
- Fiat: BRL (PIX), MXN (SPEI)
- Crypto: USDT, USDC, BTC, ETH, SOL, BNB

## Chains
- USDT: tron, ethereum, bsc, polygon, solana
- USDC: tron, ethereum, bsc, polygon, solana
- BTC: bitcoin
- ETH: ethereum
- SOL: solana
- BNB: bsc

## chain vs network (ATENÇÃO)
- `chain` (cashin/wallet): blockchain de origem (livro-razão).
- `network` (cashout): rede de transmissão da tx (pode ser uma L2: arbitrum/base/optimism).
- Para Tron/BTC/Solana/BSC ambos coincidem; para ETH/USDC podem diferir.

Bloco 2 — Autenticação (Bearer + HMAC)

## 1) Bearer Token
POST /v2/oauth/token
Header: Authorization: Basic base64(client_id:client_secret)
Body:   { "grant_type": "client_credentials" }
Response: { "access_token": "JWT", "expires_in": 3600, "meta": {...} }
Use:      Authorization: Bearer {access_token}  (todas as outras rotas)
Rate:     30 reqs/min/IP. 5 falhas seguidas → IP_BLOCKED_TEMPORARILY (5 min)

## 2) Assinatura HMAC (rotas financeiras)
Rotas que EXIGEM HMAC (além do Bearer):
- POST /v2/transactions/cashout
- POST /v2/internal_transfers/payment
- POST /v2/conversions/new

Headers extra:
  X-Signature: hex(hmac_sha256(timestamp + "." + nonce + "." + body, signing_key))
  X-Timestamp: <unix_seconds>
  X-Nonce:     <uuid_v4>

Validações servidor:
- Timestamp ±5 min → INVALID_TIMESTAMP (401)
- Nonce único últimos 5 min → REPLAY_DETECTED (401)
- Assinatura não bate → INVALID_SIGNATURE (401)
- Header ausente → MISSING_SIGNATURE / MISSING_TIMESTAMP / MISSING_NONCE (401)

IMPORTANTE: assinar o BODY RAW exato enviado (mesmos bytes — não re-serializar).


Bloco 3 — Catálogo Completo de Endpoints

## Account / Read
GET  /v2/account/balance               → saldos por moeda (available/blocked/pending)
GET  /v2/account/info/profile          → perfil (kyc_level, kyc_status, status)
GET  /v2/account/limits                → limites diários/mensais/por operação
GET  /v2/account/fees                  → taxas configuradas (cashin/cashout/conversion)

## Transactions (core)
POST /v2/transactions/cashin           → cobrança (PIX/cripto/SPEI). Retorna QR/address/CLABE
POST /v2/transactions/cashout          → saque (PIX/cripto/SPEI) — async 202 + HMAC
POST /v2/transactions/wallet           → wallet fixa permanente (BRL/MXN/cripto)

## Transferências e Conversão
POST /v2/internal_transfers/payment    → transferência interna BSPAY — HMAC
POST /v2/conversions/rate              → cotação spot rápida
POST /v2/conversions/simulate          → cotação com fees + spread
POST /v2/conversions/new               → executar conversão — HMAC

## Listagem
POST /v2/account/transactions/list     → extrato com filtros + paginação

## Infrações PIX (MED — disputas BACEN)
GET /v2/account/infractions                   → listar infrações recebidas
GET  /v2/account/infractions/detail    → detalhe + replies
POST /v2/account/infractions/reply     → responder com defesa + evidências (multipart)

Bloco 4 — Bodies por Endpoint

## POST /v2/transactions/cashin
{
  "amount": number,                       // mín: 1.00 BRL · 2.00 USDT · 0.0001 BTC
  "currency": "BRL"|"MXN"|"USDT"|"USDC"|"BTC"|"ETH"|"SOL"|"BNB",
  "chain": string,                        // obrigatório p/ cripto: tron|ethereum|bsc|polygon|solana|bitcoin
  "external_id": string,                  // único — idempotência
  "postback_url": "https://...",          // webhook
  "payer": { "name": string, "document": string, "email"?: string },
  "split"?: [ { "username": string, "percentage": number } | { "username": string, "type": "fixed", "amount": number } ]
}
→ data.payment_info: { qrcode, expires_at } | { address, chain, network, min_confirmations } | { clabe, bank_code }

## POST /v2/transactions/cashout  (HMAC obrigatório)
{
  "external_id": string,
  "amount": number,
  "currency": "BRL"|"MXN"|"USDT"|"USDC"|"BTC"|"ETH"|"SOL"|"BNB",
  "key": string,                          // PIX key | endereço crypto | CLABE 18d
  "key_type": "cpf"|"cnpj"|"email"|"phone"|"random",   // só PIX
  "network": string,                      // só cripto (tron/ethereum/bsc/polygon/...)
  "name"?: string,                        // SPEI: nome recebedor
  "bank_code"?: string,                   // SPEI: auto-detect via CLABE
  "description"?: string,
  "postback_url"?: "https://..."
}
→ 202 { transaction_id, status: "pending" }
→ webhook async: cashout.confirmed | cashout.failed (saldo refundado auto)

## POST /v2/transactions/wallet
{ "currency": string, "chain"?: string }
→ { wallet_id, address|pix_key|clabe, currency, chain?, min_confirmations? }
→ depósitos disparam webhook wallet_deposit

## POST /v2/internal_transfers/payment  (HMAC obrigatório)
{
  "username": string,                     // destinatário BSPAY
  "amount": number,
  "currency": string,
  "description"?: string,
  "external_id"?: string
}
→ { transaction_id, from_user, to_user, status: "confirmed" }
Rate: 10/min (anti-enum)

## POST /v2/conversions/rate
{ "amount": number, "base_currency": string, "destination_currency": string }
→ { rate, estimated_amount, timestamp }

## POST /v2/conversions/simulate
{ "amount": number, "base_currency": string, "destination_currency": string }
→ { from_amount, to_amount, rate, spread, fee, expires_at }

## POST /v2/conversions/new  (HMAC obrigatório)
{
  "amount": number,
  "base_currency": string,
  "destination_currency": string,
  "external_id"?: string
}
→ { conversion_id, amount_from, currency_from, amount_to, currency_to, rate, fee }

## POST /v2/account/transactions/list
{
  "page"?: number, "page_size"?: number,   // padrão 1, 50 (máx 100)
  "status"?: "pending"|"confirmed"|"cancelled"|"failed",
  "type"?: "cashin"|"cashout",
  "source"?: "cashin"|"cashout"|"conversion"|"internal_transfer"|"wallet_deposit",
  "currency"?: string,
  "from_date"?: "YYYY-MM-DD",
  "to_date"?: "YYYY-MM-DD",
  "transaction_id"?: string,
  "external_id"?: string
}

{ "pix_key": "00020126360014BR.GOV.BCB.PIX..." }
→ { receiver_name, receiver_document, city, amount?, txid? }

## GET /v2/account/infractions
{ "page"?: number, "page_size"?: number, "status"?: "open"|"responded"|"resolved", "type"?: "REFUND_REQUEST"|"FRAUD" }

## GET /v2/account/infractions/detail?id=<infraction_id>
→ { infraction_id, type, status, amount, currency, transaction_id, e2e_id, deadline_at, reason, debtor, creditor, replies[] }

## POST /v2/account/infractions/reply  (multipart/form-data)
form: id, message, files[] (PDF/JPG/PNG até 10MB cada, máx 5)
→ { infraction_id, reply_id, status: "responded" }

Bloco 5 — Webhooks (9 eventos)

## Headers enviados
Content-Type: application/json
X-BSPay-Event: <nome do evento>
X-BSPay-Signature: hex(hmac_sha256(rawBody, callback_secret))
X-BSPay-Timestamp: <unix_seconds>

## Validar SEMPRE antes de processar (PHP/Node/Python)
expected = hmac_sha256(rawBody, CALLBACK_SECRET).hex()
if abs(now - X-BSPay-Timestamp) > 300: reject 401
if not constant_time_compare(expected, X-BSPay-Signature): reject 401

## Eventos
- cashin.confirmed       → PIX/cripto/SPEI recebido (saldo creditado)
- cashin.refunded        → cashin estornado (saldo debitado, devolvido ao pagador)
- cashout.confirmed      → saque liquidado (com hash PIX e2e_id ou tx_hash blockchain)
- cashout.failed          → saque falhou — saldo REFUNDADO AUTOMATICAMENTE
- wallet_deposit         → depósito em wallet fixa
- transfer.confirmed     → transferência interna concluída (direction: sent|received)
- conversion.confirmed   → conversão FX completada
- chargeback.opened      → nova infração PIX MED — saldo bloqueado, deadline 7 dias
- chargeback.opened|chargeback.won|chargeback.lost|chargeback.canceled    → infração resolvida (outcome: won|lost)

## error_code (em cashout.failed)
TRON_ENERGY_FAIL | INSUFFICIENT_BALANCE | NO_HOT_WALLET | INVALID_ADDRESS
BLACKLISTED | BLOCKCHAIN_REVERT | BROADCAST_FAIL | PIX_KEY_NOT_FOUND
PROVIDER_TIMEOUT | UNKNOWN

## Política de retry (HTTP 2xx esperado)
Tentativas: 5 | Intervalos: 1min, 5min, 15min, 1h, 6h | Timeout: 10s

## Idempotência
Use transaction_id (ou conversion_id, infraction_id) como chave única.

Bloco 6 — Catálogo de Erros

## Auth (401/403)
MISSING_AUTH_HEADER · INVALID_AUTH_FORMAT · INVALID_CREDENTIALS · INVALID_TOKEN
TOKEN_EXPIRED · UNAUTHORIZED · FORBIDDEN · UNAUTHORIZED_IP · INVALID_OTP
CREDENTIAL_PENDING_ADMIN_ACTIVATION

## HMAC (401)
MISSING_SIGNATURE · MISSING_TIMESTAMP · MISSING_NONCE
INVALID_SIGNATURE · INVALID_TIMESTAMP · REPLAY_DETECTED

## Segurança (403/423/429)
RATE_LIMIT_EXCEEDED (retryable após Retry-After) · IP_BLACKLISTED
IP_BLOCKED_TEMPORARILY · SECURITY_BLOCKED · ACCOUNT_UNDER_REVIEW

## Validação (400/422)
INVALID_PAYLOAD · INVALID_JSON · MISSING_REQUIRED_FIELD (details.field)
INVALID_FORMAT · INVALID_VALUE · INVALID_AMOUNT · INVALID_CURRENCY
INVALID_PIX_KEY · INVALID_SPLIT_CONFIG · SELF_TRANSFER_BLOCKED
UNSUPPORTED_CONTENT_TYPE · UNSUPPORTED_CHAIN (details.supported)
UNSUPPORTED_CURRENCY

## Limites e Saldo (409/422)
INSUFFICIENT_BALANCE · INSUFFICIENT_FUNDS · BELOW_MIN_LIMIT (details.min_amount)
EXCEEDS_MAX_LIMIT (details.max_amount) · LIMIT_EXCEEDED

## Negócio (403/404/409)
PERMISSION_DENIED · KYC_REQUIRED · DUPLICATE_RESOURCE · DUPLICATE_EXTERNAL_ID
PENDING_APPROVAL · MISSING_FEE_CONFIG

## Sistema (500/502/503)
INTERNAL_ERROR · PROVIDER_ERROR (retryable) · SERVICE_UNAVAILABLE (retryable)
INTERNAL_CONFLICT (409, retryable com nonce novo)

Bloco 7 — Mapeamento de Intenção → Endpoint

"Cobrar R$ 100 via PIX"           → POST /v2/transactions/cashin (currency: BRL)
"Receber USDT na rede Tron"       → POST /v2/transactions/cashin (currency: USDT, chain: tron)
"Endereço fixo de doação cripto"  → POST /v2/transactions/wallet (currency: USDT, chain: tron)
"Pagar parceiro via PIX"          → POST /v2/transactions/cashout (currency: BRL, key, key_type) [HMAC]
"Sacar USDC para wallet ETH"      → POST /v2/transactions/cashout (currency: USDC, network: ethereum) [HMAC]
"Saque SPEI México"               → POST /v2/transactions/cashout (currency: MXN, key=CLABE, name, bank_code)
"Transferir entre contas BSPay"   → POST /v2/internal_transfers/payment (username) [HMAC]
"Quanto BRL vale 100 USDT?"       → POST /v2/conversions/rate
"Simular conversão com fee"       → POST /v2/conversions/simulate
"Converter 80 USDT em BRL"        → POST /v2/conversions/new [HMAC]
"Quanto tenho de saldo?"          → GET /v2/account/balance
"Meu perfil/KYC"                  → GET /v2/account/info/profile
"Meus limites"                    → GET /v2/account/limits
"Minhas taxas"                    → GET /v2/account/fees
"Extrato deste mês"               → POST /v2/account/transactions/list (from_date, to_date)
"Recebi infração contra mim"      → GET /v2/account/infractions (status: open)
"Detalhar essa infração"          → GET /v2/account/infractions/detail
"Defender minha transação"        → POST /v2/account/infractions/reply (multipart)

Boas práticas para LLMs

Cache do token

Token vale 1h. Não regere em toda requisição — guarde com TTL e renove 60s antes do expiry.

Confirmação humana em rotas com $$

Cashout, transferência interna e conversão movem dinheiro. Sempre confirme com o usuário antes de chamar — mostre valor, destino e fee.

HMAC só do servidor

signing_key e callback_secret nunca vão pro client/frontend. LLM deve gerar código onde a assinatura acontece no backend.

Idempotência via external_id

Em retries, o mesmo external_id retorna a mesma transação (não duplica). Gere UUID por intenção do usuário, não por tentativa.

Webhook é fonte de verdade

Status final só é definitivo via webhook. A resposta inicial pode ser pending e mudar depois. Reconcile periodicamente via transactions/list.


Guias por modelo

Esta página foi útil?