# Convert API

> Converta entre criptomoedas diretamente do saldo do seu comércio — obtenha uma cotação em tempo real e execute ao preço de mercado.

A Convert API permite trocar entre as moedas mantidas no saldo do seu comércio ao preço de mercado atual — o mesmo motor que alimenta a aba **Swap** do painel do comércio, agora disponível a partir do seu backend.

> **WARNING:** Os endpoints de Convert são assinados com sua **API key normal** — a mesma usada para requisições da [Payment API](/docs/payments), **não** a Payout API key. Executar uma conversão debita e credita o saldo do seu comércio imediatamente, então trate essa chave com o mesmo cuidado dado a qualquer credencial que movimenta dinheiro.

## Obter cotação de conversão

Retorna uma cotação indicativa para uma conversão ao preço de mercado atual — a taxa efetiva e os valores resultantes. Nada é debitado ou reservado; chame quantas vezes precisar antes de executar.

`POST /v1/convert/price`

### Parâmetros da requisição

| Campo | Tipo | Obrigatório | Descrição | Valor |
|-------|------|-------------|-----------|-------|
| `from_currency` | string | sim | Moeda de origem | `BTC`, `ETH`, `USDT`, `USDC`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `to_currency` | string | sim | Moeda de destino. Deve ser diferente de `from_currency` | `USDT`, `USDC`, `BTC`, `ETH`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `amount` | decimal | sim | Valor a converter, maior que `0` |  |
| `amount_type` | string | sim | A qual lado `amount` se refere | `from`, `to` |

> **INFO:** `amount_type=from` gasta exatamente `amount` de `from_currency`. `amount_type=to` recebe exatamente `amount` de `to_currency`.

**🟢 200 OK** · `application/json`

```json
{
  "state": 0,
  "result": {
    "success": true,
    "from_currency": "BTC",
    "to_currency": "USDT",
    "amount_type": "from",
    "from_amount": "0.01000000",
    "to_amount": "947.86690000",
    "effective_rate": "94786.69000000",
    "from_amount_usd": "947.87",
    "to_amount_usd": "947.87"
  }
}
```

#### Campos da resposta

| Campo | Tipo | Descrição |
|-------|------|-----------|
| `success` | boolean | Se a cotação foi calculada com sucesso |
| `from_currency` | string | Moeda de origem |
| `to_currency` | string | Moeda de destino |
| `amount_type` | string | Repete o `amount_type` da requisição |
| `from_amount` | string | Valor que seria debitado em `from_currency` |
| `to_amount` | string | Valor que seria creditado em `to_currency` |
| `effective_rate` | string | Taxa aplicada a esta cotação — 1 unidade de `from_currency` em `to_currency` (já inclui o preço da plataforma) |
| `from_amount_usd` | string \| null | Equivalente em USD de `from_amount` |
| `to_amount_usd` | string \| null | Equivalente em USD de `to_amount` |

- A cotação é **apenas indicativa** — o preço de mercado pode mudar entre a cotação e a chamada de execução.
- Esta chamada não debita nem reserva nenhum saldo.

> Use your project UUID and the endpoint-appropriate API key from the merchant dashboard.

#### Interactive request: `POST /v1/convert/price`
  - `from_currency` (enum, required): BTC,ETH,USDT,USDC,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `to_currency` (enum, required): USDT,USDC,BTC,ETH,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `amount` (decimal, required)
  - `amount_type` (enum, required): from,to

## Executar conversão

Executa uma conversão ao preço de mercado atual e atualiza o saldo do seu comércio. Não há uma etapa separada de "confirmar cotação" — chame diretamente com o valor que deseja converter.

`POST /v1/convert`

> **INFO:** **Idempotência.** Repetir exatamente a mesma requisição (mesmos `from_currency`, `to_currency`, `amount`, `amount_type`) dentro de cerca de um minuto após a primeira chamada retorna a conversão existente em vez de criar uma segunda. Após essa janela, uma requisição idêntica é tratada como uma nova conversão — não tente novamente às cegas após um timeout sem antes verificar o resultado anterior.

> **WARNING:** Este endpoint é limitado a **10 requisições por minuto** por chamador — mais rígido que o limite geral da API — porque cada chamada movimenta saldo real.

### Parâmetros da requisição

| Campo | Tipo | Obrigatório | Descrição | Valor |
|-------|------|-------------|-----------|-------|
| `from_currency` | string | sim | Moeda de origem | `BTC`, `ETH`, `USDT`, `USDC`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `to_currency` | string | sim | Moeda de destino. Deve ser diferente de `from_currency` | `USDT`, `USDC`, `BTC`, `ETH`, `TRX`, `BNB`, `GRAM`, `SOL`, `POL`, `LTC`, `DASH`, `DOGE`, `ZEC`, `XRP`, `XMR` |
| `amount` | decimal | sim | Valor a converter, maior que `0` |  |
| `amount_type` | string | sim | A qual lado `amount` se refere | `from`, `to` |

**🟢 200 OK** · `application/json`

```json
{
  "state": 0,
  "result": {
    "id": 12345,
    "type": "manual",
    "status": "completed",
    "from_currency": "BTC",
    "to_currency": "USDT",
    "from_amount": "0.01000000",
    "requested_from_amount": "0.01000000",
    "refund_amount": null,
    "to_amount": "947.86690000",
    "exchange_rate": "94786.69000000",
    "fee_amount": "0.00000000",
    "from_amount_usd": "947.87",
    "to_amount_usd": "947.87",
    "processed_at": "2026-01-20T15:30:24Z",
    "created_at": "2026-01-20T15:30:22Z"
  }
}
```

#### Campos da resposta

| Campo | Tipo | Descrição |
|-------|------|-----------|
| `id` | int | ID do pedido de conversão atribuído pelo sistema |
| `type` | string | Sempre `manual` nesta API |
| `status` | string | Status atual (ver «Status de conversão» abaixo) |
| `from_currency` | string | Moeda de origem |
| `to_currency` | string | Moeda de destino |
| `from_amount` | string | Valor debitado em `from_currency` |
| `requested_from_amount` | string \| null | Seu valor de origem originalmente solicitado quando `amount_type = from`. `null` quando `amount_type = to` |
| `refund_amount` | string \| null | Parte do valor pré-debitado devolvida a você após uma execução parcial. `null` se o pedido foi totalmente executado |
| `to_amount` | string | Valor creditado em `to_currency` |
| `exchange_rate` | string | Taxa realmente aplicada a esta conversão — 1 unidade de `from_currency` em `to_currency` (já inclui o preço da plataforma) |
| `fee_amount` | string | Taxa da plataforma cobrada nesta conversão, denominada em `from_currency` ou `to_currency` conforme a direção da operação. Já refletida em `exchange_rate` — exibida para transparência |
| `from_amount_usd` | string \| null | Equivalente em USD de `from_amount` |
| `to_amount_usd` | string \| null | Equivalente em USD de `to_amount` |
| `processed_at` | string (ISO 8601) \| null | Quando a conversão terminou de ser executada. `null` enquanto ainda em processamento |
| `created_at` | string (ISO 8601) | Quando o pedido de conversão foi criado |

#### Status de conversão

| Status | Descrição |
|--------|-----------|
| `pending` | Criado, ainda não enviado ao mercado |
| `processing` | Saldo bloqueado e pedido colocado no mercado |
| `completed` | Totalmente executado — `to_amount` foi creditado ao seu saldo |
| `failed` | Não foi possível executar — qualquer valor pré-debitado foi devolvido automaticamente |
| `partially_completed` | Apenas para pares de moedas sem mercado direto (roteados por uma moeda intermediária): o primeiro trecho foi concluído, mas o segundo falhou. Você recebe a moeda intermediária em vez de `to_currency` — converta novamente a partir dela para alcançar seu objetivo original |

#### Interactive request: `POST /v1/convert`
  - `from_currency` (enum, required): BTC,ETH,USDT,USDC,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `to_currency` (enum, required): USDT,USDC,BTC,ETH,TRX,BNB,GRAM,SOL,POL,LTC,DASH,DOGE,ZEC,XRP,XMR
  - `amount` (decimal, required)
  - `amount_type` (enum, required): from,to

## Erros

Em caso de falha, a resposta tem `state: 1` e um `error_code` — compartilhado por `/v1/convert/price` e `/v1/convert`:

**🔴 422 / 400** · `application/json`

```json
{
  "state": 1,
  "error_code": "amount_too_small",
  "errors": {
    "amount": "Amount is too small for this conversion. Please increase the amount and try again."
  }
}
```

| `error_code` | Status HTTP | Descrição |
|--------------|-------------|-----------|
| `validation_failed` | 422 | Parâmetros inválidos ou ausentes, ou rejeição por regra de negócio (ex.: saldo insuficiente) — veja o campo `errors` para detalhes |
| `amount_too_small` | 422 | `amount` está abaixo do tamanho mínimo negociável para este par de moedas |
| `convert_unavailable` | 400 | Não foi possível executar a conversão agora (dados de mercado indisponíveis ou nenhuma rota entre as duas moedas) — tente novamente em breve |
| `internal_error` | 400 | Erro interno inesperado do servidor ao processar a requisição |

## Conversão automática de pagamentos recebidos

A conversão automática é uma configuração de projeto para faturas recebidas e créditos de carteira estática. Ela é configurada no painel do comerciante, não adicionando campos a `/v1/payment`. Cada regra seleciona uma ou mais moedas de origem e uma moeda de destino.

Quando a conversão é concluída, as informações do pagamento e os webhooks do comerciante podem incluir:

```json
{
  "payment_amount": "0.14800000",
  "merchant_amount": "0.146520000000000000",
  "payer_currency": "XMR",
  "convert": {
    "to_currency": "USDT",
    "commission": "0.09000000",
    "rate": "323.21000000",
    "amount": "47.262015740000000000"
  }
}
```

Os domínios de valores são intencionalmente separados:

- `payment_amount` — o que foi detectado na cadeia na moeda de pagamento de origem;
- `merchant_amount` — o valor líquido de origem atribuível ao comerciante antes da conversão;
- `convert.amount` — o valor creditado em `convert.to_currency`;
- `convert.rate` e `convert.commission` — o resultado da conversão realizada, não um preço que você deve recalcular localmente.

> **WARNING:** A ausência de `convert` é significativa: a conversão pode não ter sido concluída, pode não estar configurada para essa fonte, ou pode ter voltado para crédito na moeda de origem. Nunca invente um valor alvo a partir de `/exchange-rates` ou de um preço de mercado público.

### Falha na conversão automática e fallback

A conversão ocorre após o recebimento do pagamento na blockchain. Disponibilidade de mercado, tamanhos mínimos de pedido, limites de precisão, tempos limites de câmbio e liquidez executável insuficiente podem atrasar ou impedir a conversão.

- Depósitos abaixo do mínimo global/projeto contornam o pipeline de conversão e creditam a moeda de origem.
- Falhas transitórias podem ser re-tentadas de forma assíncrona.
- Depósitos grandes ou não negociáveis podem retornar a um crédito na moeda de origem depois que a política de re-tentativa for esgotada.
- Um pagamento, portanto, pode ser válido mesmo quando a conversão para a moeda desejada não ocorreu.

Sua integração deve persistir o pagamento verificado primeiro, e então reconciliar a moeda efetivamente creditada a partir das informações de pagamento, o bloco opcional `convert` e os saldos do comerciante. Não bloqueie o reconhecimento do webhook de pagamento enquanto espera pelos seus próprios sistemas de análise ou notificação.

### Testes de aceitação de conversão automática

Teste pelo menos: conversão direta bem-sucedida, conversão por ponte/multi-salto, poeira abaixo do mínimo, tentativa transitória, retorno para a moeda de origem, pagamento insuficiente, pagamento excessivo, webhook duplicado, `convert` ausente, e reconciliação após um timeout ambíguo.

## Casos extremos de conversão manual

- `/v1/convert/price` é uma pré-visualização indicativa; o movimento do mercado pode alterar o resultado da execução.
- `amount_type: from` corrige a solicitação do lado da origem, enquanto `amount_type: to` solicita um valor do lado do destino. Não troque o significado ao apresentar a interface de confirmação.
- Um par sem mercado direto pode ser roteado através de uma moeda intermediária. Se apenas uma das etapas for concluída, `partially_completed` relata o crédito intermediário.
- Se uma chamada de execução expirar, reconcilie antes de tentar novamente. Uma ordem de mercado pode ser executada mesmo quando sua resposta HTTP é perdida.
- Trate `failed` como um estado a ser reconciliado, não como permissão para aplicar uma entrada de saldo compensatório local; a plataforma é responsável pela contabilidade de débito/reembolso.