# Javali Bridge · Simuladores · API de controle

Base: https://api.simulators.javalibridge.com

Os simuladores reproduzem as APIs da Trace Finance e da Bloquo em sandboxes por organização. As APIs simuladas
respondem como as originais, cada uma no seu host (https://trace.simulators.javalibridge.com e https://bloquo.simulators.javalibridge.com).
Esta API de controle faz o que a sua aplicação não consegue fazer pela API original: ler o estado do sandbox, agir
como o outro lado (o compliance que aprova, o banco que liquida, o pagador que paga), esperar webhooks, ligar falhas
controladas e reiniciar os dados.

Use as duas juntas num teste: sua app chama a API simulada e o teste usa a API de controle para empurrar o outro lado
e verificar o resultado.

## Comece aqui (agentes e testes)

1. `GET /v1/me`: a organização e os produtos ativos.
2. `GET /v1/{produto}` (trace ou bloquo): host e credenciais que a sua aplicação usa.
3. A aplicação chama o host simulado; o teste chama `POST /v1/{produto}/actions/{id}` para o outro lado.
4. `GET /v1/{produto}/wait` espera o webhook ou o status (long poll de até 25 s); `POST /v1/{produto}/clock/advance` pula no tempo; `PUT /v1/{produto}/chaos/{key}` injeta falhas.

- Comece por GET /v1/me e GET /v1/{produto}: credenciais, hosts e estado vêm de lá. Nunca invente credenciais nem ids.
- Sua aplicação fala com o host simulado (trace.simulators.javalibridge.com, bloquo.simulators.javalibridge.com); o teste usa a API de controle só para o outro lado e para verificar.
- Use wait com since (horário de antes da ação) em vez de sleep. 408 wait_timeout significa que o evento não aconteceu.
- Comece cada teste com POST /v1/{produto}/reset para não herdar dados.
- Para testar falhas, ligue o caos a 100% e desligue no fim; para testar tempo, avance o relógio.
- Erros vêm como {"error": {"code", "message"}}; 422 action_failed traz o motivo em português. Leia antes de tentar de novo.
- Use uma chave read para só consultar e write para agir. Guarde a chave em SIMULATORS_API_KEY, nunca no código.

Guia com exemplos por produto: https://simulators.javalibridge.com/desenvolvedores · índice para IAs: https://simulators.javalibridge.com/llms.txt

## Autenticação

`Authorization: Bearer mk_…` em toda rota /v1. Crie a chave no console: Configurações da organização → Chaves de API.
Chaves `read` só fazem GET; chaves `write` também agem. A chave vale para todos os sandboxes da organização.

Erros: `{"error": {"code": "…", "message": "…"}}`. Códigos: 400 invalid_json / invalid_parameter, 401 invalid_api_key,
403 read_only_key, 404 not_found / trace_not_active / bloquo_not_active / unknown_action, 408 wait_timeout, 422 action_failed.

Caos (`PUT /v1/{produto}/chaos/{key}`): enabled, apiErrorRate (0–100, % de respostas 500/503/429), apiErrorStatus, apiErrorWhen ("before" não processa, "after" processa e mesmo assim erra), latencyMinMs, latencyMaxMs, opsFailRate (0–100, % de operações recusadas), opsFailCode, webhookFailRate, webhookDuplicateRate. Taxa 100 deixa o teste determinístico; lembre de desligar (`{"enabled": false}`).

## CLI

`node cli/bin/simulators.mjs --help` (no repositório, sem dependências; `SIMULATORS_URL` troca a base): `login mk_…`, `status trace`, `action trace account.create_active`, `wait bloquo delivery order_type=Deposit`, `clock trace +3h`, `get /v1/…`. Saída em JSON e código de saída 1 em erro HTTP.

## Rotas gerais

- `GET /v1/me` — organização, chave e produtos ativos.

## Trace Finance

As APIs FX Account, Payments e Webhooks da Trace ficam em https://trace.simulators.javalibridge.com com os caminhos de api.sandbox.tracefinance.com (`/v1/...`). Token: `POST /v1/oauth/client/token` `{"clientId", "clientSecret"}` → `accessToken` (JWT de 23 h). Todo POST/PUT/PATCH leva `X-Idempotency-Key` (UUID). Os gatilhos do sandbox da Trace valem aqui: addressLine2 com `no-auto`, CPF 12345678909, CNPJ 50676745000143, John Doe, países KP/IR/SY/CU, dictKey `reject@sandbox.trace.finance`, atestado com `reject` no nome, depósito de 8.00 (nunca pago) e 9.00 (expira), saque com centavos .09 (falha). O que a Trace não documenta está em docs/trace/spec.md como [INFERIDO].

- `GET /v1/trace` — host, token_url, credenciais (client_id, client_secret), customer (id = X-Company-Id dos webhooks), chave Pix virtual, cotação, homologação, caos e relógio. `{"active": false}` se não ativado.
- `POST /v1/trace/activate`, `POST /v1/trace/reset` — ativa o sandbox (sem contas: abra pela API ou com a ação account.create_active); reinicia os dados (mantém credenciais, cotação e configurações; o relógio volta à hora real).
- `GET /v1/trace/objects?kind=&status=&id=&parent=&since=&limit=` — kinds: account, ubo (parent = conta), document (parent = conta), quote, operation (parent = conta), beneficiary, instruction (parent = beneficiário), subscription. `GET /v1/trace/objects/{kind}/{id}`.
- `GET /v1/trace/deliveries?resource=ACCOUNT|BENEFICIARY|OPERATION&event=&ref=&status=PENDING|SUCCESS|FAILED&since=&limit=` — execution logs dos webhooks (event = X-Event-Type, ref = id do recurso; `request_headers` traz os headers enviados: X-Message-Id, X-Event-Type, X-Resource-Name, X-Company-Id e X-Message-Signature, para testar a verificação da assinatura). `GET /v1/trace/deliveries/{id}`, `POST /v1/trace/deliveries/{id}/resend` (novo log com o mesmo X-Message-Id).
- `GET /v1/trace/wait?on=object|delivery&…filtros…&timeout=25` — long poll. Ex.: `on=object&kind=account&id={accountId}&status=ACTIVE`, `on=object&kind=operation&id={operationId}&status=COMPLETED`, `on=delivery&event=OPERATION_COMPLETED&ref={operationId}`.
- `GET /v1/trace/clock`, `POST /v1/trace/clock/advance` — relógio do sandbox: cotações de 30 s, QR de 24 h, tokens de 23 h e retentativas de webhook (aceita seconds).
- `POST /v1/trace/sweep` — processa agora o que já venceu. `PUT /v1/trace/homolog` `{"autoApproveAccounts", "autoPayDeposits", "autoSettleOperations", "autoReviewInstructions"}` (booleanos, todos ligados por padrão): desligue para fazer o papel do compliance, do pagador e do trilho com as ações.
- `PUT /v1/trace/rates` `{"USDT": "5.37", "USDC": "5.36"}` — preço de cada stablecoin em BRL usado pelo POST /v1/quotes.
- `GET /v1/trace/chaos`, `PUT /v1/trace/chaos/{key}` — keys: accounts, operations, beneficiaries.
- `GET /v1/trace/actions`, `POST /v1/trace/actions/{id}`:

- `account.create_active` — Cria uma conta já ACTIVE (BRL + USDT + USDC), sem onboarding, no nome do cliente do sandbox. Atalho para testar operações.
  Corpo: `accountType`?: "VIRTUAL" | "NAMED" | "NAMED_NON_RESIDENT" (Tipo da conta BRL (padrão VIRTUAL)); `legalName`?: string (Razão social (padrão: o nome do cliente))
- `account.approve` — O compliance aprova a conta agora: documentos e UBOs aprovados, cripto ACTIVE (OPENING) e, com ativar_fiat, BRL ACTIVE (ACTIVE). Útil com a aprovação automática desligada.
  Corpo: `accountId`: string (id da conta); `activateFiat`?: boolean (Ativa o BRL na hora (padrão true))
- `account.reject_requirement` — O compliance devolve um documento (pelo documentType, ex.: PROOF_OF_ADDRESS) ou um campo do owner (ex.: legalName): a conta volta a ACTION_REQUIRED com o item em requirements.errors (webhook ACCOUNT_ACTION_REQUIRED).
  Corpo: `accountId`: string (id da conta); `requirement`: string (documentType enviado ou nome do campo); `message`?: string (Motivo)
- `account.reject` — O compliance reprova a conta de vez (REJECTED).
  Corpo: `accountId`: string (id da conta); `message`?: string (Motivo)
- `account.fail_asset` — O onboarding de um ativo falha de vez no provedor (FAILED, webhook ACCOUNT_ASSET_FAILED).
  Corpo: `accountId`: string (id da conta); `asset`: "BRL" | "USDT" | "USDC" (Ativo em ONBOARDING)
- `account.receive_pix` — Chega um Pix na chave da conta sem pedido prévio: depósito COMPLETED com quote null e externalId null (webhook OPERATION_COMPLETED). É como se credita saldo em BRL.
  Corpo: `accountId`: string (id da conta (ACTIVE)); `amount`: string (Valor em BRL, ex.: 1000.00); `payerName`?: string (Nome do pagador)
- `account.receive_crypto` — Chegam USDT ou USDC na carteira da conta (ETHEREUM/Sepolia): depósito COMPLETED com quote null.
  Corpo: `accountId`: string (id da conta (ACTIVE)); `asset`: "USDT" | "USDC" (Stablecoin); `amount`: string (Valor, ex.: 250.000000)
- `deposit.pay` — O pagador paga agora o QR Code de um depósito (COMPLETED, CREDIT do valor alvo). Útil com o pagamento automático desligado ou para o valor 8.00, que nunca é pago sozinho.
  Corpo: `operationId`: string (id da operação de depósito)
- `deposit.expire` — O QR Code do depósito expira agora sem pagamento (FAILED, motivo QR_CODE_EXPIRED).
  Corpo: `operationId`: string (id da operação de depósito)
- `operation.settle` — Liquida agora um saque, swap ou transferência (REQUESTED/PROCESSING → COMPLETED), ou um estorno REFUNDING → REFUNDED. force ignora a regra dos centavos .09 e o caos.
  Corpo: `operationId`: string (id da operação); `force`?: boolean (Ignora as regras de falha)
- `operation.fail` — O trilho recusa a operação (FAILED com o motivo; a reserva de saldo é liberada).
  Corpo: `operationId`: string (id da operação); `code`?: string (Código do motivo (padrão PROCESSING_ERROR)); `message`?: string (Mensagem do motivo)
- `operation.require_document` — O compliance pausa a operação pedindo um documento (ACTION_REQUIRED, webhook OPERATION_ACTION_REQUIRED). Enviar o documento em POST /v1/operations/{id}/documents retoma.
  Corpo: `operationId`: string (id da operação); `documentType`?: "FINANCIAL_CAPACITY" | "INVOICE" (Documento pedido (padrão FINANCIAL_CAPACITY))
- `instruction.approve` — O compliance aprova agora uma instrução de pagamento pendente (APPROVED, webhook BENEFICIARY_INSTRUCTION_APPROVED). Use com no-auto no addressLine2.
  Corpo: `instructionId`: string (id da instrução de pagamento)
- `instruction.reject` — O compliance reprova uma instrução de pagamento pendente (REJECTED, COMPLIANCE_REJECTED).
  Corpo: `instructionId`: string (id da instrução de pagamento); `message`?: string (Motivo (padrão An unknown error occurred.))
- `instruction.require_document` — O compliance pede (de novo) o atestado de custódia de uma instrução CRYPTO (ACTION_REQUIRED, webhook BENEFICIARY_INSTRUCTION_ACTION_REQUIRED).
  Corpo: `instructionId`: string (id da instrução CRYPTO); `message`?: string (Motivo)
- `webhook.resend` — Reenvia uma entrega de webhook (execution log) como novo log, com o mesmo X-Message-Id, corpo e assinatura.
  Corpo: `logId`: string (id do execution log); `retry`?: boolean (true = só tenta de novo o mesmo log, sem criar outro)

## Bloquo

O núcleo de integração da Bloquo Platform API fica em https://bloquo.simulators.javalibridge.com com os caminhos de api.bloquo.io (`/api/v1/...`). Token: `POST /api/v1/auth/authenticateServer` `{"clientId", "clientSecret"}` → `result.accessToken` (3600 s). O token é do cliente raiz (Institutional, `root_customer_id`), que só enxerga a própria hierarquia. Webhooks: preferências por orderType em `POST /api/v1/customer/{id}/notification/preference`, assinados com `X-Bloquo-Signature` (RSA-SHA256 de "t.corpo"; chave em `GET /api/v1/notification/webhook/public-key`). O Bloquo não documenta gatilhos de sandbox; os dos simuladores: depósito com centavos ,05 nunca é pago, saque com ,07 falha, nome com KYC pede documentos, com REJEITAR é recusado, endereço com "dead" nunca é aprovado. Usuários humanos, backoffice, grupos e CSVs de template respondem 501. Especificação: docs/bloquo/spec.md.

- `GET /v1/bloquo` — host, token_url, credenciais (client_id, client_secret), root_customer_id, contas do cliente raiz com saldo, cotações, homologação, caos e relógio. `{"active": false}` se não ativado.
- `POST /v1/bloquo/activate`, `POST /v1/bloquo/reset` — ativa (cliente raiz com contas BRL, USD, USDT e USDC zeradas e pares padrão); reinicia (o cliente raiz volta zerado; credenciais, cotações e configurações ficam).
- `GET /v1/bloquo/objects?kind=&status=&id=&parent=&since=&limit=` — kinds: customer, account, counterparty, relation (cliente–contraparte, status de compliance), whitelist, pair, quote, trade (id = orderId uuid), deposit, withdrawal, fx_payment, transaction, preference. parent = id do cliente. `GET /v1/bloquo/objects/{kind}/{id}`.
- `GET /v1/bloquo/deliveries?order_type=Deposit|Withdrawal|Refund|Trade|FxPayment|Compliance&order_id=&event=&status=&since=&limit=` — notification orders (event = status do pedido; cada tentativa traz `request_headers` com o X-Bloquo-Signature enviado e `signature_t`, o t assinado; `last_request_headers` é o da última tentativa enviada). `GET /v1/bloquo/deliveries/{id}`, `POST /v1/bloquo/deliveries/{id}/resend`.
- `GET /v1/bloquo/wait?on=object|delivery&…filtros…&timeout=25` — long poll. Ex.: `on=object&kind=deposit&id={id}&status=COMPLETED`, `on=delivery&order_type=FxPayment&order_id={id}&event=COMPLETED`.
- `GET /v1/bloquo/clock`, `POST /v1/bloquo/clock/advance` — cotações de 30 s, validade dos depósitos, tokens de 1 h e o backoff dos webhooks.
- `POST /v1/bloquo/sweep` — processa agora o que já venceu. `PUT /v1/bloquo/homolog` `{"autoApproveSubaccounts", "autoApproveCounterparties", "autoApproveWallets", "autoPayDeposits", "autoSettleOrders", "autoApproveFxPayments"}` (booleanos, todos ligados por padrão).
- `PUT /v1/bloquo/rates` `{"USD": "5.20", "USDT": "5.21", …}` — preço em BRL de USD, EUR, GBP, USDT, USDC e BTC usado nas cotações (spread de 0,5%).
- `GET /v1/bloquo/chaos`, `PUT /v1/bloquo/chaos/{key}` — keys: customers, orders, trade.
- `GET /v1/bloquo/actions`, `POST /v1/bloquo/actions/{id}`:

- `account.credit` — Credita uma conta como se um depósito externo já tivesse sido liquidado (transação Credit, sem pedido). É o jeito rápido de ter saldo para saques, trades e FX payments.
  Corpo: `accountId`: string (id da conta); `amount`: string (Valor na moeda da conta, ex.: 10000.00)
- `deposit.pay` — O pagador paga o PIX (ou a rede confirma o depósito cripto) agora: COMPLETED com o crédito (valor − taxa) e o webhook de Deposit. Funciona também com os centavos ,05, que nunca são pagos sozinhos.
  Corpo: `depositId`: string (id do pedido de depósito)
- `deposit.expire` — A instrução de depósito expira agora sem pagamento (EXPIRED).
  Corpo: `depositId`: string (id do pedido de depósito)
- `withdrawal.approve` — A tesouraria aprova um saque PENDING_TREASURY (conta com autoExecuteWithdrawal false): ele vai ao conector.
  Corpo: `withdrawalId`: string (id do pedido de saque)
- `withdrawal.complete` — O banco liquida o saque agora (COMPLETED com o débito), ignorando a regra dos centavos ,07.
  Corpo: `withdrawalId`: string (id do pedido de saque)
- `withdrawal.fail` — O banco recusa o saque (FAILED com failureReason; a reserva é liberada).
  Corpo: `withdrawalId`: string (id do pedido de saque); `reason`?: string (failureReason)
- `trade.complete` — O provedor de liquidez confirma e a tesouraria registra o trade agora (COMPLETED com débito e crédito).
  Corpo: `tradeId`: string (orderId (uuid) ou id numérico do trade)
- `trade.fail` — O provedor de liquidez recusa o trade (FAILED; o saldo travado volta).
  Corpo: `tradeId`: string (orderId (uuid) ou id numérico do trade)
- `fxpayment.approve` — O compliance aprova um FX payment PENDING_APPROVAL agora: executa o trade (se cruzado) e credita internamente ou envia o saque ao beneficiário.
  Corpo: `fxPaymentId`: string (id do FX payment)
- `fxpayment.reject` — O compliance recusa um FX payment (REJECTED; o saldo reservado volta).
  Corpo: `fxPaymentId`: string (id do FX payment); `reason`?: string (Motivo)
- `counterparty.approve` — O compliance (KYT) aprova a contraparte para o cliente: Approved e webhook de Compliance.
  Corpo: `customerId`: string (id do cliente); `counterpartyId`: string (id da contraparte)
- `counterparty.reject` — O compliance (KYT) recusa a contraparte: Rejected e webhook de Compliance.
  Corpo: `customerId`: string (id do cliente); `counterpartyId`: string (id da contraparte); `notes`?: string (complianceNotes)
- `wallet.approve` — O compliance aprova um endereço da whitelist (complianceApproved true).
  Corpo: `walletWhitelistId`: string (id da entrada da whitelist)
- `subaccount.approve` — O compliance aprova a sub-account (Active): ela passa a operar.
  Corpo: `customerId`: string (id da sub-account)
- `subaccount.request_kyc` — O compliance pede documentos (AwaitingKyc); enviar em POST /subaccount/{parentId}/{id}/documents volta a Pending.
  Corpo: `customerId`: string (id da sub-account)
- `subaccount.reject` — O compliance recusa a sub-account de vez (Rejected).
  Corpo: `customerId`: string (id da sub-account)
- `notification.resend` — Reenvia agora uma notification order (tentativa manual, como POST /notification/order/{id}/resend).
  Corpo: `notificationId`: string (id da notification order)


## MCP

O mesmo host serve um servidor MCP (Streamable HTTP) em `https://api.simulators.javalibridge.com/mcp`, com a mesma chave. Ferramentas: overview, reference, control (qualquer rota acima), simulated_api (chama a API simulada já autenticada), action, wait e advance_clock.

```sh
claude mcp add --transport http bridge-simulators https://api.simulators.javalibridge.com/mcp --header "Authorization: Bearer $SIMULATORS_API_KEY"
```

Em clientes com configuração JSON (Cursor e outros): `{"mcpServers": {"bridge-simulators": {"url": "https://api.simulators.javalibridge.com/mcp", "headers": {"Authorization": "Bearer mk_…"}}}}`.


## Exemplo: depósito Pix na Trace, do começo ao fim

```sh
K="Authorization: Bearer $SIMULATORS_API_KEY"
curl -s https://api.simulators.javalibridge.com/v1/trace -H "$K"                     # client_id, client_secret e host
# sua app abre o depósito em POST https://trace.simulators.javalibridge.com/v1/operations/deposits e recebe o id
T0=$(date -u +%Y-%m-%dT%H:%M:%SZ)
curl -s "https://api.simulators.javalibridge.com/v1/trace/wait?on=delivery&event=OPERATION_COMPLETED&ref=$OPERATION_ID&since=$T0" -H "$K"   # o webhook que sua app recebeu
```
