Crypto-as-a-Service (CaaS) na API de Contas
Esta jornada usa a API de Contas (secureapi.<domínio>/api/v2) para o parceiro BaaS ou a conta operarem compra e venda de criptoativos: registrar carteiras, cotar, criar ordens e receber eventos.
O contrato público é /crypto-caas/* no prefixo /api/v2 da API de Contas. Não é um produto avulso: a autenticação, o envelope e os erros são os mesmos dos demais módulos de Contas (CryptoRisk, cobranças, webhooks).
Referência OpenAPI: API de Contas — CryptoCaas.
CaaS não substitui o CryptoRisk. CryptoRisk analisa risco de uma carteira. CaaS registra destino/origem e executa BUY/SELL.
Quando usar
Use esta API quando a sua aplicação precisa:
- Registrar uma carteira da conta (
POST /crypto-caas/wallets) - Listar as carteiras já cadastradas (
GET /crypto-caas/wallets) - Obter uma cotação BUY ou SELL (
POST /crypto-caas/quotes) - Criar a ordem a partir da cotação (
POST /crypto-caas/orders) - Acompanhar a ordem (
GET /crypto-caas/orders/{id}eGET /crypto-caas/orders) - Receber os eventos no webhook
CRYPTO
O que não entra neste contrato: ids internos de catálogo (assetNetworkId), titular da carteira (ownerType, ownerDocument, ownerName), accountId no body, rotas de mesa/staff nem o canal App Finance.
Pré-requisitos
- mTLS configurado. Veja certificado mTLS.
- Credencial da API de Contas (Finance ou AppMobile) com os escopos
crypto-caas.readecrypto-caas.write. No Finance: Configurações → API Contas → Nova credencial. - Token OAuth. Veja Autenticação.
- Webhook
CRYPTOse a aplicação for reagir a cadastro de carteira e a mudanças de ordem. Scopewebhook.write. Veja Configurando Webhook.
Credenciais já emitidas não recebem os novos escopos automaticamente. Crie uma nova credencial ou atualize a existente incluindo crypto-caas.read e crypto-caas.write.
Credencial BaaS (clientId com prefixo baas_) opera contas filhas. Envie x-account-id com o id interno da conta alvo. Credencial de conta opera a própria conta e não precisa do cabeçalho. O x-account-id do cliente não vai no body: a API resolve a conta da credencial e não reenvia esse header ao broker.
Redes e ativos
network é a rede. USDT e USDC não são rede.
network | Uso |
|---|---|
BTC | Bitcoin |
ETH | Ethereum (envelope também de USDT e USDC) |
TRX | Tron (envelope também de USDT) |
USDT, USDC ou qualquer outro | Não. Responde 422 antes de qualquer chamada upstream. |
Pares de cotação e ordem:
asset | network |
|---|---|
BTC | BTC |
ETH | ETH |
USDT | ETH ou TRX |
USDC | ETH |
USDC em TRX e qualquer outro par respondem 422.
Body do POST de carteira: { "network": "BTC" | "ETH" | "TRX", "address": "...", "alias": "..." }. Sem accountId no body.
Fluxo BUY
1. POST /crypto-caas/wallets { network, address, alias? }
→ 201 { data } com id da carteira
2. POST /crypto-caas/quotes { operation: BUY, network, asset, amountBrl?, destinationWalletId? }
→ 201 { data } com quoteId e validUntil
3. POST /crypto-caas/orders { operation: BUY, quoteId, amountBrl|amountAsset, destinationWalletId }
→ 201 { data } com id da ordem (UUID)
4. Acompanhe GET /crypto-caas/orders/{id} e os eventos crypto.order.updated / crypto.order.settled
BUY exige destinationWalletId na criação da ordem.
Fluxo SELL
1. A origem precisa estar cadastrada e elegível (VERIFIED na mesma rede, eligibleForSellOrigin=true)
2. POST /crypto-caas/quotes { operation: SELL, network, asset, originWalletId, amountBrl? }
→ 201 { data }
3. POST /crypto-caas/orders { operation: SELL, quoteId, amountBrl|amountAsset, originWalletId }
→ 201 { data }
SELL exige originWalletId na cotação e na ordem. Sem origem, a API de Contas responde 400 antes do broker. Origem cadastrada mas inelegível (pendente, rejeitada ou só auto-verificada como destino de BUY) é recusada pelo broker (4xx).
Primeira compra
A carteira e a cotação podem trazer isFirstPurchase, firstPurchaseLimitBrl e firstPurchaseLimitEnabled. Quando o destino está em regime de primeira compra, envie na ordem:
"firstPurchaseAcknowledgment": {
"accepted": true,
"textVersion": "v1"
}
accepted tem de ser true. O limite de primeira compra não corta o amountBrl nesta API; é sinalização (ciência do parceiro), não teto de valor.
O primeiro BUY SETTLED daquela carteira dispara crypto.wallet.first_purchase.
Idempotência
Nos POSTs, a API resolve um request id nesta ordem: Idempotency-Key → x-request-id → x-correlation-id → UUID gerado. O valor é truncado em 128 caracteres. Não envie accountId no body.
Política (fail-closed)
A API de Contas não reimplementa política. O broker aplica:
- SELL só com origem VERIFIED e
eligibleForSellOrigin=true - Atestação de parceiro fail-closed quando a flag estiver ligada (
403permanece403) - Criação de ordem é fail-closed (movimento de dinheiro não “passa quieto”)
- Webhook/notify são fail-open: falha de publicação não desfaz a ordem nem a carteira
Envelopes
| Operação | HTTP | Envelope |
|---|---|---|
| Criar carteira / cotação / ordem | 201 | { data } |
| Detalhe de ordem | 200 | { data } |
| Listar carteiras | 200 | { meta: { total }, data } |
| Listar ordens | 200 | { meta: { total, page, limit }, data } |
Ordem de outra conta responde 404.
Erros
Os endpoints /crypto-caas/* respondem no envelope da API de Contas:
{
"detail": "network must be BTC, ETH or TRX. Received: USDT. USDT and USDC are assets on the ETH envelope (USDT also on TRX), not networks.",
"title": "Unprocessable entity",
"type": "onz-0026",
"instance": "/api/v2/crypto-caas/wallets"
}
| HTTP | Quando |
|---|---|
400 | Body ou query inválidos (ex.: SELL sem originWalletId; quoteId que não é UUID; os dois amounts juntos) |
401 | Token inválido ou escopo ausente |
403 | Acesso negado, inclusive atestação de parceiro recusada |
404 | Ordem inexistente ou de outra conta |
409 | Conflito no broker (recurso já existente) |
422 | Rede, ativo, operação ou par não suportados (network=USDT, USDC em TRX, etc.) |
503 | Broker indisponível, timeout ou 401 interno do upstream (não confundir com OAuth da API de Contas) |