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 HTTPErroDescrição
401api_key_missingO cabeçalho X-Bifrost-Invoke não foi enviado.
401api_key_invalidAPI Key desconhecida, revogada ou expirada. Também devolvido enquanto a conta está suspensa.
403api_access_deniedO acesso à API está desativado nesta conta. Conferido a cada requisição.
403email_not_verifiedO e-mail da conta ainda não foi verificado.
403ip_not_authorizedO IP de origem não está na whitelist da chave.
400invalid_ipNão foi possível determinar o IP do cliente.
401signature_headers_missingA chave exige assinatura e falta X-Bifrost-Signature, X-Bifrost-Timestamp ou X-Bifrost-Nonce.
401signature_timestamp_invalidX-Bifrost-Timestamp não é composto só de dígitos (segundos Unix).
401signature_timestamp_out_of_rangeO timestamp difere do horário do servidor em mais de 300 segundos.
401signature_nonce_length_invalidO nonce tem menos de 16 ou mais de 80 caracteres.
401signature_nonce_charset_invalidO nonce tem caracteres fora de A-Z, a-z, 0-9, sublinhado e hífen.
401signature_invalidA assinatura não confere com a string canônica.
401signature_replayedEste nonce já foi usado por esta chave.
401signature_secret_unavailableA chave exige assinatura mas não tem segredo utilizável. Fale com o suporte.
403scope_deniedA 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.
429rate_limit_exceededA cota da chave na janela atual acabou. Retry-After e details.retry_after_seconds dizem quando tentar de novo.
429too_many_failed_authenticationsEste IP está bloqueado temporariamente após falhas repetidas de autenticação. Vem com Retry-After.
401user_not_authenticatedNã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 HTTPErroDescrição
404route_not_foundNenhuma rota /v1 corresponde ao caminho e ao método. Respondido antes da autenticação.
400json_parse_exceptionO corpo da requisição não é JSON válido. Qualquer rota /v1.
400json_parse_failedPOST /v1/payout: o corpo foi decodificado como null ou false.
400invalid_parametersParâ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).
400invalid_payment_listPOST /v1/payout: o corpo está vazio ou não é um objeto de payout nem uma lista de payouts.
400max_payouts_exceededPOST /v1/payout: mais de 50 payouts em uma requisição.
409idempotency_key_in_progressUma requisição com esta Idempotency-Key ainda está em andamento. Aguarde e repita com a mesma chave.
400crypto_amount_requiredPOST /v1/payin: process_by é crypto_amount e falta crypto_amount.
400fiat_amount_requiredPOST /v1/payin: process_by é fiat_amount e falta fiat_amount.
400fiat_currency_requiredPOST /v1/payin: process_by é fiat_amount e falta fiat_currency.
400invalid_page_numberlist-payout / list-payin: page menor que 1.
400invalid_per_page_limitlist-payout / list-payin: per_page fora de 1 a 100.
400invalid_status_filterlist-payout / list-payin: valor de status desconhecido.
400invalid_network_filterlist-payout / list-payin: rede desconhecida.
400invalid_date_fromlist-payout / list-payin: date_from não é uma data.
400invalid_date_tolist-payout / list-payin: date_to não é uma data.
400invalid_reference_idcheck-payout / check-payin: referência com menos de 10 ou mais de 756 caracteres. Extratos: reference_id com mais de 255.
400invalid_paginationExtratos: page ou per_page fora do intervalo.
400invalid_formatExtratos: date_from ou date_to não é uma data (details.field).
400invalid_date_rangeExtratos: date_from é posterior a date_to.
400date_range_too_wideExtratos: o intervalo é maior que o permitido. details.max_days informa o teto.
400invalid_amountExtratos: amount_from ou amount_to inválido, ou amount_from maior que amount_to.
400missing_parametersget-price: falta currency1 ou currency2.
400invalid_currencyget-price: currency1 ou currency2 não é um código de moeda válido.
400invalid_webhook_urlPUT /v1/fixed-wallets/config: webhook_url aponta para um destino não permitido.

Erros de Negócio

Código HTTPErroDescrição
400insufficient_total_balancePOST /v1/payout: os itens de uma moeda somam mais que o saldo disponível. Nada é debitado. details traz crypto_currency, available e required.
404user_not_foundPOST /v1/payout: o cadastro da conta está incompleto. Fale com o suporte.
404record_not_foundO 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.
400duplicate_reference_idPOST /v1/payin: já existe um payin com este reference_id na conta.
400invalid_network_or_deposits_disabledPOST /v1/payin: rede desconhecida, ou depósitos desativados nela.
400unsupported_token_or_deposits_disabledPOST /v1/payin: token não suportado, ou depósitos desativados para ele.
400token_network_mismatchPOST /v1/payin: o token não está disponível nesta rede.
400memo_requiredPOST /v1/payin: este token exige memo.
400amount_out_of_limitsPOST /v1/payin: o valor em cripto está fora dos limites do token. details traz min_allowed e max_allowed.
400value_below_minimumPOST /v1/payin com fiat_amount: o valor convertido está abaixo do mínimo.
400value_above_maximumPOST /v1/payin com fiat_amount: o valor convertido está acima do máximo.
400token_not_supported_or_inactivePOST /v1/payin com fiat_amount: o token não é suportado ou está inativo para conversão.
400crypto_amount_does_not_correspond_to_fiat_amount_providedPOST /v1/payin: crypto_amount e fiat_amount foram enviados juntos e não batem com a cotação atual.
400invalid_parameters_for_process_byPOST /v1/payin: os valores enviados não combinam com o process_by escolhido.
404price_not_foundget-price: não há cotação para este par de moedas.
403feature_disabledEndereços fixos não estão habilitados para a conta (config, atualização de config e criação).
422type_not_supportedPOST /v1/fixed-wallets: este type não está disponível para endereços fixos.
403limit_not_allowedPOST /v1/fixed-wallets: o limite da conta para este type é zero.
409limit_reachedPOST /v1/fixed-wallets: a conta já tem o número máximo de endereços fixos.
409no_wallet_availablePOST /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 HTTPErroDescrição
207/400invalid_parametersO item falhou na validação. error.details.fields traz cada campo com sua mensagem.
207/400missing_required_fieldsFalta um campo obrigatório do item.
207/400duplicate_reference_id_in_requestO mesmo reference_id aparece antes no mesmo lote.
207/400duplicate_payment_referenceO 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/400invalid_crypto_amountprocess_by é crypto_amount e crypto_amount está ausente ou inválido.
207/400invalid_fiat_amountprocess_by é fiat_amount e fiat_amount está ausente ou inválido.
207/400fiat_currency_requiredprocess_by é fiat_amount e falta fiat_currency.
207/400fiat_currency_not_acceptedA fiat_currency não é aceita.
207/400memo_requiredEste token exige memo.
207/400invalid_network_or_withdrawals_disabledRede desconhecida, ou saques desativados nela.
207/400unsupported_token_or_withdrawals_disabledToken não suportado, ou saques desativados para ele.
207/400token_network_mismatchO token não está disponível nesta rede.
207/400token_not_activeO token não está ativo para saque ou para conversão.
207/400invalid_formatO endereço da wallet não é válido para a rede.
207/400limit_exceededO valor está fora do mínimo e do máximo do token (também quando o valor fiat convertido cai fora deles).
207/400amount_does_not_cover_feesAs taxas consomem todo o valor, e o destinatário não receberia nada.
207/400crypto_amount_does_not_correspond_to_fiat_amount_providedcrypto_amount e fiat_amount não batem com a cotação atual.
207/400price_unavailableSem 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/400insufficient_balanceO saldo disponível não cobre este item.
207/400daily_payout_limit_exceededO item ultrapassaria o teto diário configurado na chave.
207/400daily_payout_limit_unavailableA chave tem teto diário mas o consumo não pôde ser registrado. Reenvie o item.
207/400error_fetching_active_tokens · error_fetching_active_currenciesNão foi possível carregar o catálogo de tokens ou moedas. Reenvie o item.
207/400user_not_found · record_not_foundFalta um dado da conta de que o saque precisa.
207/400internal_error · database_error · transaction_failed · network_unavailable · duplicate_recordA 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 HTTPErroDescrição
500internal_errorFalha 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.
500database_errorExtratos: a consulta falhou.
500error_fetching_active_currenciesPOST /v1/payout e POST /v1/payin: não foi possível carregar o catálogo de tokens ou fiats. Nada foi processado.
500token_type_not_found · receipt_creation_failed · database_transaction_failedPOST /v1/payin: o payin não pôde ser criado do nosso lado.
502external_processor_errorPOST /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.
503no_processor_configured · no_wallet_availablePOST /v1/payin: não há processador ou endereço de depósito disponível para este token e rede agora. Tente mais tarde.
503price_not_available_for_conversion · price_not_available_for_validationPOST /v1/payin: não há cotação disponível para converter ou conferir o valor. Tente mais tarde.
503service_unavailablePOST /v1/payin e endpoints de summary: uma dependência está temporariamente indisponível.
500config_update_failedPUT /v1/fixed-wallets/config: a configuração não pôde ser gravada.
500assign_failedPOST /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