NueForm

Tratamento de erros

Entenda o formato de resposta de erro da API do NueForm, os códigos de erro e como tratar erros de forma elegante na sua aplicação.

A API do NueForm usa códigos de status HTTP convencionais e retorna respostas de erro em JSON estruturado. Este guia cobre o formato de resposta, todos os códigos de erro possíveis e padrões para tratar erros no seu código.

Formatos de resposta

Respostas de sucesso

Chamadas de API bem-sucedidas retornam um objeto JSON com um campo data. Endpoints de listagem também incluem um campo meta com informações de paginação.

json
{
  "data": {
    "id": "clx1abc2d3e4f5g6h7i8j9k0",
    "title": "Customer Feedback",
    "status": "published",
    "created_at": "2026-01-15T09:30:00.000Z"
  }
}

Com metadados de paginação:

json
{
  "data": [
    {
      "id": "clx1abc2d3e4f5g6h7i8j9k0",
      "title": "Customer Feedback"
    },
    {
      "id": "clx2def3g4h5i6j7k8l9m0n1",
      "title": "Employee Survey"
    }
  ],
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 150,
    "total_pages": 8
  }
}

Respostas de erro

Respostas de erro retornam um objeto JSON com um campo error contendo o code do erro, uma message legível por humanos e o código de status HTTP.

json
{
  "error": {
    "code": "NOT_FOUND",
    "message": "The requested form could not be found.",
    "status": 404
  }
}

Códigos de erro

A tabela a seguir lista todos os códigos de erro retornados pela API:

CódigoStatus HTTPDescrição
BAD_REQUEST400A requisição está malformada, faltam campos obrigatórios ou contém valores inválidos.
UNAUTHORIZED401Falha na autenticação. A chave de API está ausente, malformada, revogada ou expirada.
FORBIDDEN403O usuário autenticado não tem permissão para executar esta ação.
NOT_FOUND404O recurso solicitado não existe ou não está acessível ao usuário autenticado.
CONFLICT409A requisição conflita com o estado atual do recurso (por exemplo, entrada duplicada ou um formulário que ainda não pode ser publicado).
RATE_LIMITED429Requisições em excesso. Veja Limites de taxa para detalhes.
INTERNAL_ERROR500Ocorreu um erro inesperado no servidor.

Exemplos de respostas de erro

400 Bad Request

Retornado quando o corpo da requisição é inválido ou faltam campos obrigatórios.

json
{
  "error": {
    "code": "BAD_REQUEST",
    "message": "Field 'title' is required.",
    "status": 400
  }
}

401 Unauthorized

Retornado quando a autenticação falha por qualquer motivo.

json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired API key.",
    "status": 401
  }
}

403 Forbidden

Retornado quando o usuário está autenticado mas não tem permissão. Causas comuns incluem acessar recursos de outro usuário ou usar um recurso não disponível no plano atual.

json
{
  "error": {
    "code": "FORBIDDEN",
    "message": "API access is not available on your current plan.",
    "status": 403
  }
}

404 Not Found

Retornado quando o recurso solicitado não existe.

json
{
  "error": {
    "code": "NOT_FOUND",
    "message": "The requested form could not be found.",
    "status": 404
  }
}

409 Conflict

Retornado quando a requisição criaria uma duplicata ou conflita com dados existentes.

json
{
  "error": {
    "code": "CONFLICT",
    "message": "A form with this slug already exists.",
    "status": 409
  }
}

409 Conflict — publicação bloqueada

Retornado por POST /api/v1/forms/:id/publish (e por PUT /api/v1/forms/:id com published: true) quando o formulário ainda não pode ir ao ar. Nada é publicado. reason diz o motivo, e question_ids lista as perguntas a corrigir. Quando upgrade_required é true, o plano do dono do formulário precisa de upgrade.

json
{
  "error": {
    "code": "CONFLICT",
    "message": "E-signature contracts need a Pro plan to go live. Upgrade to publish this form, or remove the contract question.",
    "status": 409,
    "reason": "contract_requires_pro",
    "question_ids": ["66a1b2c3d4e5f6a7b8c9d001"],
    "upgrade_required": true
  }
}
reasonO que corrigir
contract_requires_pro, data_node_requires_pro, booking_requires_pro, payment_connect_requires_pro, webhook_requires_proO plano do dono do formulário (o do proprietário da equipe, em formulários de equipe) não inclui esse recurso. Faça upgrade ou remova o recurso: exclua a pergunta, limpe webhook_url ou mude a pergunta de pagamento para a opção padrão.
booking_*Uma pergunta de agendamento ainda não pode receber agendamentos: conecte ou reconecte a agenda dela, ou adicione os horários, o fuso horário, as opções de duração ou o número das ligações de confirmação.
email_node_*Falta a uma etapa de e-mail o destinatário, o nome do remetente, o assunto ou o corpo, ou ela envia para {respondent.email} em um formulário que não exige login.
fuse_*Uma pergunta Fuse não tem formulário Fuse, o formulário Fuse dela não existe ou não está publicado, ou a sessão fixada foi arquivada.
composite_*Um composite usado pelo formulário não está publicado, foi excluído ou está vazio; composite_ids lista quais são.

Se você criar um formulário com published: true e a publicação for bloqueada, o formulário é criado mesmo assim como rascunho: a resposta é 201 Created com um objeto publish_error (reason, message, question_ids, upgrade_required), então uma nova tentativa nunca cria uma duplicata. Quando PUT /api/v1/forms/:id retorna esse erro, suas alterações foram salvas (saved_as_draft: true) e só a publicação falhou.

429 Rate Limited

Retornado quando o limite de taxa foi excedido. Inclui um cabeçalho Retry-After.

json
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Please try again later.",
    "status": 429
  }
}

500 Internal Error

Retornado quando ocorre um erro inesperado no servidor. Se isso persistir, entre em contato com o suporte.

json
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An internal error occurred.",
    "status": 500
  }
}

Erros da API de voz

A API de voz acrescenta estes códigos. Nada que termine em erro é cobrado, e os campos extras ficam ao lado de code dentro de error.

CódigoStatus HTTPDescrição
INSUFFICIENT_CREDITS402O seu saldo não cobre a requisição. credits_required, credits_available e top_up_url dizem quanto falta e onde conseguir mais.
TEXT_TOO_LONG413Mais caracteres falados do que uma requisição aceita (max_characters). Divida o texto em várias requisições.
CONTENT_BLOCKED422O texto não pode ser falado segundo a nossa política de uso (categories).
VOICE_UNAVAILABLE422A voz não existe ou você não pode usá-la (voice).
LANGUAGE_NOT_SUPPORTED_BY_VOICE422A voz não fala esse idioma (voice, language, supported_languages).
LINE_LANGUAGE_NOT_SUPPORTED_BY_VOICE422Algumas linhas estão em um idioma que a própria voz delas não fala. mismatches lista cada uma (speaker, voice, voice_name, language, line_count), com até quatro suggestions de vozes que falam esse idioma. Quando native_voices é false, nenhuma voz fala exatamente esse idioma: as sugestões falam um idioma próximo e podem não soar bem.
INVALID_SCRIPT422O roteiro não pode ser falado do jeito que foi enviado (reason, por exemplo speaker_not_cast).
CONCURRENCY_LIMIT429Gerações demais em andamento ao mesmo tempo na sua conta. Tente de novo depois de Retry-After.
CAPACITY503Sem capacidade no momento. Tente de novo depois de Retry-After.
WARMING_UP503O mecanismo de voz está iniciando. Tente de novo depois de Retry-After.
MODERATION_UNAVAILABLE503Não foi possível verificar o texto agora. Tente de novo depois de Retry-After.
AI_UNAVAILABLE503auto_convert ou auto_cast não pôde ser executado agora. Tente de novo depois de Retry-After.
DELETE_INCOMPLETE503Uma exclusão parou no meio. Envie de novo para concluir.

FORBIDDEN (403) e CONFLICT (409) dizem o motivo em reason: por exemplo key_limit_reached, account_gate ou scope_not_allowed, e quote_exceeds_max_credits, idempotency_conflict ou request_in_progress.

json
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "This request needs 30 credits; 12 are available. Nothing was charged.",
    "status": 402,
    "credits_required": 30,
    "credits_available": 12,
    "top_up_url": "https://nueform.io/wallet?tab=buy"
  }
}

Tratando erros no código

JavaScript

Construa um cliente de API reutilizável que verifica erros e lança exceções tipadas:

javascript
class NueFormApiError extends Error {
  constructor(code, message, status) {
    super(message);
    this.name = "NueFormApiError";
    this.code = code;
    this.status = status;
  }
}

async function nueformRequest(path, options = {}) {
  const response = await fetch(`https://app.nueform.io/api/v1${path}`, {
    ...options,
    headers: {
      "Authorization": `Bearer ${process.env.NUEFORM_API_KEY}`,
      "Content-Type": "application/json",
      ...options.headers,
    },
  });

  const body = await response.json();

  if (!response.ok) {
    const { code, message, status } = body.error;
    throw new NueFormApiError(code, message, status);
  }

  return body;
}

// Usage
async function getForm(formId) {
  try {
    const { data } = await nueformRequest(`/forms/${formId}`);
    return data;
  } catch (error) {
    if (error instanceof NueFormApiError) {
      switch (error.code) {
        case "NOT_FOUND":
          console.error(`Form ${formId} does not exist.`);
          return null;
        case "UNAUTHORIZED":
          console.error("Check your API key configuration.");
          break;
        case "RATE_LIMITED":
          console.warn("Rate limited. Implement backoff and retry.");
          break;
        default:
          console.error(`API error [${error.code}]: ${error.message}`);
      }
    }
    throw error;
  }
}

Python

python
import os
import requests

class NueFormApiError(Exception):
    def __init__(self, code, message, status):
        super().__init__(message)
        self.code = code
        self.status = status

class NueFormClient:
    def __init__(self, api_key=None):
        self.api_key = api_key or os.environ["NUEFORM_API_KEY"]
        self.base_url = "https://app.nueform.io/api/v1"

    def request(self, method, path, **kwargs):
        url = f"{self.base_url}{path}"
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
        }

        response = requests.request(method, url, headers=headers, **kwargs)
        body = response.json()

        if not response.ok:
            error = body["error"]
            raise NueFormApiError(
                error["code"], error["message"], error["status"]
            )

        return body

# Usage
client = NueFormClient()

try:
    result = client.request("GET", f"/forms/{form_id}")
    form = result["data"]
    print(f"Form: {form['title']}")

except NueFormApiError as e:
    if e.code == "NOT_FOUND":
        print(f"Form {form_id} does not exist.")
    elif e.code == "UNAUTHORIZED":
        print("Check your API key configuration.")
    elif e.code == "RATE_LIMITED":
        print("Rate limited. Implement backoff and retry.")
    else:
        print(f"API error [{e.code}]: {e}")

Boas práticas

Sempre verifique o campo error

Nunca presuma que uma resposta foi bem-sucedida apenas pela presença de um corpo. Sempre verifique o campo error ou o código de status HTTP antes de processar a resposta.

Use códigos de erro específicos para controle de fluxo

Faça a correspondência pelo error.code (por exemplo, NOT_FOUND, RATE_LIMITED) em vez do código de status HTTP. Códigos de erro são estáveis, descritivos e mais fáceis de usar na lógica da aplicação.

Registre os detalhes dos erros

Inclua a resposta de erro completa nos seus logs para depuração. Juntos, code, message e status fornecem contexto suficiente para diagnosticar a maioria dos problemas sem reproduzir a requisição.

Trate erros 5xx com novas tentativas

Erros de servidor (500 Internal Error) geralmente são transitórios. Implemente lógica de novas tentativas com backoff exponencial para essas respostas. Limite as tentativas a 2--3 antes de falhar.

Não exponha mensagens de erro aos usuários finais

As mensagens de erro da API são projetadas para desenvolvedores, não para usuários finais. Mapeie os erros da API para mensagens amigáveis na sua aplicação.

Se você receber respostas 500 Internal Error persistentes, entre em contato com o suporte com os detalhes da requisição e os horários. Erros de servidor são registrados do nosso lado e investigados.

Última atualização: 30 de outubro de 2026