Códigos de Erro
Todos os códigos de erro que a API devolve, agrupados por tipo, com o que fazer em cada um.
ᚱ
"Quando as forças do caos interferem, os deuses enviam mensagens claras aos mortais. Aprenda os códigos sagrados que revelam a natureza de cada obstáculo no caminho."Os Guardiões do Bifrost
Formato do erro
Todo erro da /v1 tem o mesmo formato, com o status HTTP das tabelas abaixo. Compare error.code, que é snake_case estável. error.message é texto livre em inglês para os seus logs. error.details só aparece quando há dado útil, como campos inválidos, limites ou escopos.
json
{
"error": {
"code": "insufficient_total_balance",
"message": "Insufficient balance to cover all payouts",
"details": {
"crypto_currency": "USDT",
"available": "150.00000000",
"required": "420.50000000"
}
}
}Num lote de POST /v1/payout, cada item recusado traz o mesmo objeto em results[].error, ao lado do seu index e reference_id. A resposta é 200 quando todos os itens deram certo, 207 quando parte deu certo e parte falhou, e 400 quando nenhum deu certo:
json
{
"message": "payment_processing_completed",
"summary": {
"total_requested": 3,
"successful": 1,
"failed": 2
},
"results": [
{
"index": 0,
"reference_id": "order_1001",
"internal_reference": "BIFROST_REF_123456",
"status": "created"
},
{
"index": 1,
"reference_id": "order_1002",
"error": {
"code": "invalid_parameters",
"message": "Invalid parameters provided",
"details": {
"fields": {
"wallet": "Invalid wallet format"
}
}
}
},
{
"index": 2,
"reference_id": "order_1003",
"error": {
"code": "daily_payout_limit_exceeded",
"message": "Daily payout limit for this API key exceeded"
}
}
]
}- As mensagens de validação ficam sempre em error.details.fields, indexadas pelo nome do campo, tanto no topo quanto dentro de um item do lote.
- Um item de payout duplicado conta como falha: traz error.code duplicate_payment_reference mais duplicate, original_status e original_created_at no próprio item. Um lote só de duplicados responde 400.
- Decida por error.code, nunca por error.message: as mensagens podem ser reescritas, os códigos não mudam.
- 4xx significa que o pedido precisa mudar antes de ser reenviado. 5xx significa que a falha foi do nosso lado e o mesmo pedido pode ser repetido; no POST /v1/payout, o reference_id continua protegendo contra saque duplicado.
- Um caminho /v1 desconhecido devolve 404 route_not_found em JSON. Um cliente que pede text/html no Accept recebe uma página HTML.
Erros de Autenticação e Acesso
| Código HTTP | Erro | Descrição |
|---|---|---|
| 401 | api_key_missing | O cabeçalho X-Bifrost-Invoke não foi enviado. |
| 401 | api_key_invalid | API Key desconhecida, revogada ou expirada. Também devolvido enquanto a conta está suspensa. |
| 403 | api_access_denied | O acesso à API está desativado nesta conta. Conferido a cada requisição. |
| 403 | email_not_verified | O e-mail da conta ainda não foi verificado. |
| 403 | ip_not_authorized | O IP de origem não está na whitelist da chave. |
| 400 | invalid_ip | Não foi possível determinar o IP do cliente. |
| 401 | signature_headers_missing | A chave exige assinatura e falta X-Bifrost-Signature, X-Bifrost-Timestamp ou X-Bifrost-Nonce. |
| 401 | signature_timestamp_invalid | X-Bifrost-Timestamp não é composto só de dígitos (segundos Unix). |
| 401 | signature_timestamp_out_of_range | O timestamp difere do horário do servidor em mais de 300 segundos. |
| 401 | signature_nonce_length_invalid | O nonce tem menos de 16 ou mais de 80 caracteres. |
| 401 | signature_nonce_charset_invalid | O nonce tem caracteres fora de A-Z, a-z, 0-9, sublinhado e hífen. |
| 401 | signature_invalid | A assinatura não confere com a string canônica. |
| 401 | signature_replayed | Este nonce já foi usado por esta chave. |
| 401 | signature_secret_unavailable | A chave exige assinatura mas não tem segredo utilizável. Fale com o suporte. |
| 403 | scope_denied | A chave não tem o escopo que este endpoint exige. details.required_scopes lista o que serve e details.granted_scopes o que a chave tem. |
| 429 | rate_limit_exceeded | A cota da chave na janela atual acabou. Retry-After e details.retry_after_seconds dizem quando tentar de novo. |
| 429 | too_many_failed_authentications | Este IP está bloqueado temporariamente após falhas repetidas de autenticação. Vem com Retry-After. |
| 401 | user_not_authenticated | Não foi possível associar a requisição à sua chave de API. Tente de novo; se repetir, fale com o suporte. |
Erros do Pedido
| Código HTTP | Erro | Descrição |
|---|---|---|
| 404 | route_not_found | Nenhuma rota /v1 corresponde ao caminho e ao método. Respondido antes da autenticação. |
| 400 | json_parse_exception | O corpo da requisição não é JSON válido. Qualquer rota /v1. |
| 400 | json_parse_failed | POST /v1/payout: o corpo foi decodificado como null ou false. |
| 400 | invalid_parameters | Parâmetros inválidos ou ausentes. details.fields traz cada campo com sua mensagem (POST /v1/payout com todos os itens inválidos, POST /v1/payin, endereços fixos, filtros de extrato). |
| 400 | invalid_payment_list | POST /v1/payout: o corpo está vazio ou não é um objeto de payout nem uma lista de payouts. |
| 400 | max_payouts_exceeded | POST /v1/payout: mais de 50 payouts em uma requisição. |
| 409 | idempotency_key_in_progress | Uma requisição com esta Idempotency-Key ainda está em andamento. Aguarde e repita com a mesma chave. |
| 400 | crypto_amount_required | POST /v1/payin: process_by é crypto_amount e falta crypto_amount. |
| 400 | fiat_amount_required | POST /v1/payin: process_by é fiat_amount e falta fiat_amount. |
| 400 | fiat_currency_required | POST /v1/payin: process_by é fiat_amount e falta fiat_currency. |
| 400 | invalid_page_number | list-payout / list-payin: page menor que 1. |
| 400 | invalid_per_page_limit | list-payout / list-payin: per_page fora de 1 a 100. |
| 400 | invalid_status_filter | list-payout / list-payin: valor de status desconhecido. |
| 400 | invalid_network_filter | list-payout / list-payin: rede desconhecida. |
| 400 | invalid_date_from | list-payout / list-payin: date_from não é uma data. |
| 400 | invalid_date_to | list-payout / list-payin: date_to não é uma data. |
| 400 | invalid_reference_id | check-payout / check-payin: referência com menos de 10 ou mais de 756 caracteres. Extratos: reference_id com mais de 255. |
| 400 | invalid_pagination | Extratos: page ou per_page fora do intervalo. |
| 400 | invalid_format | Extratos: date_from ou date_to não é uma data (details.field). |
| 400 | invalid_date_range | Extratos: date_from é posterior a date_to. |
| 400 | date_range_too_wide | Extratos: o intervalo é maior que o permitido. details.max_days informa o teto. |
| 400 | invalid_amount | Extratos: amount_from ou amount_to inválido, ou amount_from maior que amount_to. |
| 400 | missing_parameters | get-price: falta currency1 ou currency2. |
| 400 | invalid_currency | get-price: currency1 ou currency2 não é um código de moeda válido. |
| 400 | invalid_webhook_url | PUT /v1/fixed-wallets/config: webhook_url aponta para um destino não permitido. |
Erros de Negócio
| Código HTTP | Erro | Descrição |
|---|---|---|
| 400 | insufficient_total_balance | POST /v1/payout: os itens de uma moeda somam mais que o saldo disponível. Nada é debitado. details traz crypto_currency, available e required. |
| 404 | user_not_found | POST /v1/payout: o cadastro da conta está incompleto. Fale com o suporte. |
| 404 | record_not_found | O payout ou payin consultado por check-payout / check-payin não existe nesta conta. Também devolvido quando falta um dado da conta de que a requisição precisa. |
| 400 | duplicate_reference_id | POST /v1/payin: já existe um payin com este reference_id na conta. |
| 400 | invalid_network_or_deposits_disabled | POST /v1/payin: rede desconhecida, ou depósitos desativados nela. |
| 400 | unsupported_token_or_deposits_disabled | POST /v1/payin: token não suportado, ou depósitos desativados para ele. |
| 400 | token_network_mismatch | POST /v1/payin: o token não está disponível nesta rede. |
| 400 | memo_required | POST /v1/payin: este token exige memo. |
| 400 | amount_out_of_limits | POST /v1/payin: o valor em cripto está fora dos limites do token. details traz min_allowed e max_allowed. |
| 400 | value_below_minimum | POST /v1/payin com fiat_amount: o valor convertido está abaixo do mínimo. |
| 400 | value_above_maximum | POST /v1/payin com fiat_amount: o valor convertido está acima do máximo. |
| 400 | token_not_supported_or_inactive | POST /v1/payin com fiat_amount: o token não é suportado ou está inativo para conversão. |
| 400 | crypto_amount_does_not_correspond_to_fiat_amount_provided | POST /v1/payin: crypto_amount e fiat_amount foram enviados juntos e não batem com a cotação atual. |
| 400 | invalid_parameters_for_process_by | POST /v1/payin: os valores enviados não combinam com o process_by escolhido. |
| 404 | price_not_found | get-price: não há cotação para este par de moedas. |
| 403 | feature_disabled | Endereços fixos não estão habilitados para a conta (config, atualização de config e criação). |
| 422 | type_not_supported | POST /v1/fixed-wallets: este type não está disponível para endereços fixos. |
| 403 | limit_not_allowed | POST /v1/fixed-wallets: o limite da conta para este type é zero. |
| 409 | limit_reached | POST /v1/fixed-wallets: a conta já tem o número máximo de endereços fixos. |
| 409 | no_wallet_available | POST /v1/fixed-wallets: nenhum endereço desse tipo disponível agora. Tente mais tarde. |
Erros por Item do Lote de Payout (results[].error)
| Código HTTP | Erro | Descrição |
|---|---|---|
| 207/400 | invalid_parameters | O item falhou na validação. error.details.fields traz cada campo com sua mensagem. |
| 207/400 | missing_required_fields | Falta um campo obrigatório do item. |
| 207/400 | duplicate_reference_id_in_request | O mesmo reference_id aparece antes no mesmo lote. |
| 207/400 | duplicate_payment_reference | O reference_id já foi usado nesta conta. O item traz duplicate, original_status, original_created_at e o internal_reference original; nenhuma segunda transferência é feita. |
| 207/400 | invalid_crypto_amount | process_by é crypto_amount e crypto_amount está ausente ou inválido. |
| 207/400 | invalid_fiat_amount | process_by é fiat_amount e fiat_amount está ausente ou inválido. |
| 207/400 | fiat_currency_required | process_by é fiat_amount e falta fiat_currency. |
| 207/400 | fiat_currency_not_accepted | A fiat_currency não é aceita. |
| 207/400 | memo_required | Este token exige memo. |
| 207/400 | invalid_network_or_withdrawals_disabled | Rede desconhecida, ou saques desativados nela. |
| 207/400 | unsupported_token_or_withdrawals_disabled | Token não suportado, ou saques desativados para ele. |
| 207/400 | token_network_mismatch | O token não está disponível nesta rede. |
| 207/400 | token_not_active | O token não está ativo para saque ou para conversão. |
| 207/400 | invalid_format | O endereço da wallet não é válido para a rede. |
| 207/400 | limit_exceeded | O valor está fora do mínimo e do máximo do token (também quando o valor fiat convertido cai fora deles). |
| 207/400 | amount_does_not_cover_fees | As taxas consomem todo o valor, e o destinatário não receberia nada. |
| 207/400 | crypto_amount_does_not_correspond_to_fiat_amount_provided | crypto_amount e fiat_amount não batem com a cotação atual. |
| 207/400 | price_unavailable | Sem cotação para converter o valor, ou a chave tem teto diário e o token não tem cotação em USD para contar o saque. |
| 207/400 | insufficient_balance | O saldo disponível não cobre este item. |
| 207/400 | daily_payout_limit_exceeded | O item ultrapassaria o teto diário configurado na chave. |
| 207/400 | daily_payout_limit_unavailable | A chave tem teto diário mas o consumo não pôde ser registrado. Reenvie o item. |
| 207/400 | error_fetching_active_tokens · error_fetching_active_currencies | Não foi possível carregar o catálogo de tokens ou moedas. Reenvie o item. |
| 207/400 | user_not_found · record_not_found | Falta um dado da conta de que o saque precisa. |
| 207/400 | internal_error · database_error · transaction_failed · network_unavailable · duplicate_record | A gravação do saque falhou do nosso lado e foi desfeita. Nada foi debitado por este item; confira com check-payout ou list-payout antes de reenviar. |
Erros de Sistema
| Código HTTP | Erro | Descrição |
|---|---|---|
| 500 | internal_error | Falha inesperada. No POST /v1/payout, uma falha no meio do lote também devolve summary (com not_processed) e results no topo, para você reenviar só o que faltou. |
| 500 | database_error | Extratos: a consulta falhou. |
| 500 | error_fetching_active_currencies | POST /v1/payout e POST /v1/payin: não foi possível carregar o catálogo de tokens ou fiats. Nada foi processado. |
| 500 | token_type_not_found · receipt_creation_failed · database_transaction_failed | POST /v1/payin: o payin não pôde ser criado do nosso lado. |
| 502 | external_processor_error | POST /v1/payin e POST /v1/fixed-wallets: o endereço de depósito não pôde ser criado agora. Nada foi criado; tente mais tarde. |
| 503 | no_processor_configured · no_wallet_available | POST /v1/payin: não há processador ou endereço de depósito disponível para este token e rede agora. Tente mais tarde. |
| 503 | price_not_available_for_conversion · price_not_available_for_validation | POST /v1/payin: não há cotação disponível para converter ou conferir o valor. Tente mais tarde. |
| 503 | service_unavailable | POST /v1/payin e endpoints de summary: uma dependência está temporariamente indisponível. |
| 500 | config_update_failed | PUT /v1/fixed-wallets/config: a configuração não pôde ser gravada. |
| 500 | assign_failed | POST /v1/fixed-wallets: o endereço não pôde ser atribuído. |
Melhores Práticas
ᚱ
"A sabedoria dos anciãos ensina que seguir os caminhos corretos garante uma jornada segura e próspera pelos nove reinos digitais."Mímir, Guardião da Sabedoria Ancestral
Segurança
Faça
- •Armazene sua API Key em variáveis de ambiente
- •Configure IP Whitelist
- •Verifique o X-Bifrost-Signature de todo webhook
- •Use HTTPS em todas as requisições
- •Implemente timeouts nas requisições
Não Faça
- •Compartilhar API Key publicamente
- •Commitar API Keys no Git
- •Usar a mesma API Key em múltiplos ambientes
Performance
- •Os dados de extrato e de resumo podem ter até 5 minutos de atraso; não consulte com frequência maior que isso
- •Implemente paginação adequada
- •Evite requisições excessivas (respeite o rate limit)
- •Processe webhooks de forma assíncrona
Idempotência
- •Use reference_id único e significativo: ele é a trava contra saque duplicado, e reapresentá-lo devolve o pagamento original em vez de criar outro
- •Envie Idempotency-Key nos lotes: se a conexão cair, repetir com a mesma chave devolve a resposta original em vez de reprocessar
- •POST /v1/payin não tem Idempotency-Key: depois de um timeout, reenvie com o mesmo reference_id e, se a resposta for duplicate_reference_id, busque o payin com GET /v1/list-payin?reference_id=…
- •Trate 207 e 500 lendo results: os dois trazem o que já foi criado, para você reenviar apenas o que faltou
- •Trate as retentativas de webhook deduplicando por data.internal_reference junto com event, para não processar o mesmo aviso duas vezes