Pular para o conteúdo principal

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:

  1. Registrar uma carteira da conta (POST /crypto-caas/wallets)
  2. Listar as carteiras já cadastradas (GET /crypto-caas/wallets)
  3. Obter uma cotação BUY ou SELL (POST /crypto-caas/quotes)
  4. Criar a ordem a partir da cotação (POST /crypto-caas/orders)
  5. Acompanhar a ordem (GET /crypto-caas/orders/{id} e GET /crypto-caas/orders)
  6. 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.read e crypto-caas.write. No Finance: Configurações → API Contas → Nova credencial.
  • Token OAuth. Veja Autenticação.
  • Webhook CRYPTO se a aplicação for reagir a cadastro de carteira e a mudanças de ordem. Scope webhook.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.

networkUso
BTCBitcoin
ETHEthereum (envelope também de USDT e USDC)
TRXTron (envelope também de USDT)
USDT, USDC ou qualquer outroNão. Responde 422 antes de qualquer chamada upstream.

Pares de cotação e ordem:

assetnetwork
BTCBTC
ETHETH
USDTETH ou TRX
USDCETH

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 e limits (caps BUY/SELL + 1ª compra)
O parceiro lê data.limits. buyMaxBrl/sellMaxBrl nulos + reason ≠ ilimitado
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).

Limites da carteira​

No POST e no GET /crypto-caas/wallets, o parceiro lê data.limits. Os valores vêm da preferência da instituição e da política do broker — não entram no body do cadastro. Cotação/ordem não passam a rejeitar amountBrl só por 1ª compra.

CampoSignificado
buyMaxBrlTeto BUY aplicável neste destino (decimal BRL em string) ou null.
sellMaxBrlTeto SELL aplicável (auto-aprovação da IF) ou null.
buySourceOrigem do BUY: FIRST_PURCHASE, BUY_AUTO_APPROVE_CEILING ou BUY_AUTO_EXECUTE_CAP. null se o teto não resolveu.
sellSourceOrigem do SELL: SELL_AUTO_APPROVE_CEILING. null se o teto não resolveu.
buyReason / sellReasonSó quando o teto é null: CEILING_DISABLED, CEILING_UNREADABLE ou POLICY_UNRESOLVED.
firstPurchase.activetrue no regime cautelar de 1ª compra (ainda não houve BUY SETTLED limpo nesta carteira+rede).
firstPurchase.enabledToggle institucional da 1ª compra.
firstPurchase.limitBrlTeto cautelar em BRL quando active e enabled; senão null.

1ª compra cautelar (sem BUY SETTLED e toggle ligado): buyMaxBrl é o cautelar (default "300.00", buySource=FIRST_PURCHASE).

Pós-SETTLED (ou toggle off): buyMaxBrl passa ao cap de auto-execução BUY (hard cap "50000.00", buySource=BUY_AUTO_APPROVE_CEILING ou BUY_AUTO_EXECUTE_CAP se o hard cap vencer). firstPurchase.active fica false e dispara crypto.wallet.first_purchase. Releia o GET — não cacheie limits.

buyMaxBrl / sellMaxBrl descrevem o teto de auto. Ordem acima ainda pode ser criada e ir à Mesa.

Fail-closed: nulo não é ilimitado. A carteira continua 201/200.

{
"buyMaxBrl": null,
"sellMaxBrl": null,
"buySource": null,
"sellSource": null,
"buyReason": "CEILING_UNREADABLE",
"sellReason": "CEILING_UNREADABLE",
"firstPurchase": { "active": false, "limitBrl": null, "enabled": true }
}

limits omitido = broker ainda sem o campo. limits: null = política não resolveu no broker. A API de Contas não inventa teto.

Os flags planos isFirstPurchase, firstPurchaseLimitBrl e firstPurchaseLimitEnabled continuam no payload da carteira e da cotação.

Exemplos de curl: Carteiras, cotações e ordens.

Primeira compra​

A carteira e a cotação podem trazer isFirstPurchase, firstPurchaseLimitBrl e firstPurchaseLimitEnabled (os mesmos dados de limits.firstPurchase). Quando o destino está em regime de primeira compra, envie na ordem:

"firstPurchaseAcknowledgment": {
"accepted": true,
"textVersion": "v1"
}

accepted tem de ser true. O teto cautelar (limits.firstPurchase.limitBrl / limits.buyMaxBrl com buySource=FIRST_PURCHASE) não corta o amountBrl nesta API; é o cap aplicável de auto, não recusa dura da criação. Ciência do parceiro: envie firstPurchaseAcknowledgment quando isFirstPurchase for true.

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:

  • Resolução de limits no cadastro/listagem: preferência da IF + política. Teto não resolvido → buyMaxBrl/sellMaxBrl null + buyReason/sellReason (não é ilimitado; a carteira continua 201/200)
  • SELL só com origem VERIFIED e eligibleForSellOrigin=true
  • Atestação de parceiro fail-closed quando a flag estiver ligada (403 permanece 403)
  • 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çãoHTTPEnvelope
Criar carteira / cotação / ordem201{ data }
Detalhe de ordem200{ data }
Listar carteiras200{ meta: { total }, data }
Listar ordens200{ 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"
}
HTTPQuando
400Body ou query inválidos (ex.: SELL sem originWalletId; quoteId que não é UUID; os dois amounts juntos)
401Token inválido ou escopo ausente
403Acesso negado, inclusive atestação de parceiro recusada
404Ordem inexistente ou de outra conta
409Conflito no broker (recurso já existente)
422Rede, ativo, operação ou par não suportados (network=USDT, USDC em TRX, etc.)
503Broker indisponível, timeout ou 401 interno do upstream (não confundir com OAuth da API de Contas)