Growth Station
DocumentaçãoAPI (Desenvolvedores)ConceitosRespostas e erros

Respostas e erros

Entenda os dois formatos de resposta de sucesso da API e aprenda a tratar cada erro (400, 401, 404, 429) lendo as mensagens em português do backend.

Respostas e erros

Como a API do GS Engage responde quando dá certo — e o que fazer, passo a passo, quando dá errado.

Toda vez que você chama a API, ela devolve duas coisas: um código de status (um número que resume o que aconteceu) e um corpo (os dados ou a explicação do problema). Saber ler esses dois é o que separa uma integração que "às vezes falha misteriosamente" de uma que se recupera sozinha.

Esta página responde três perguntas práticas: como vem uma resposta de sucesso, como vem um erro, e o que fazer diante de cada código. As mensagens de erro do GS Engage já chegam em português — a ideia é você reaproveitá-las direto para quem for corrigir.

A regra de ouro para tratar qualquer erro

Sempre siga o mesmo raciocínio: o que houve → por quê → como corrigir. O código de status responde "o que houve", o corpo (error.message e, no 400, error.errors) responde "por quê", e este guia te dá o "como corrigir".

Aqui está a pegadinha que mais confunde quem começa: nem toda listagem responde no mesmo formato. Existem dois, e você precisa saber qual esperar de cada endpoint, senão seu código procura data onde só tem uma lista solta.

Formato 1 — paginado: { data, meta }

É o formato da maioria das listagens. Os itens vêm dentro de data, e um bloco meta te conta quantos existem no total e quantas páginas há. Você usa isso para saber quando parar de pedir.

{
  "data": [
    { "id": "507f1f77bcf86cd799439011", "name": "Maria Silva" },
    { "id": "507f1f77bcf86cd799439012", "name": "João Pereira" }
  ],
  "meta": {
    "limit": 20,
    "count": 20,
    "total": 137,
    "page": 1,
    "totalPages": 7
  }
}

O que isso significa para você: há 137 leads no total (total), distribuídos em 7 páginas (totalPages). Você está na página 1 e recebeu 20 itens. Peça a página 2 com ?page=2 e siga até chegar em totalPages.

Respondem neste formato paginado, por exemplo: GET /api/v1/leads, GET /api/v1/routines, GET /api/v1/prospections, GET /api/v1/conversations/threads e as demais listagens.

Formato 2 — data sem meta

Um endpoint foge à paginação: GET /api/v1/webhooks devolve a lista em data, mas sem o bloco meta (retorna todos os webhooks do projeto de uma vez, sem paginar).

{
  "data": [
    { "id": "507f1f77bcf86cd799439021", "name": "Webhook CRM", "url": "https://..." }
  ]
}

custom-fields NÃO é exceção

GET /api/v1/custom-fields é paginado normal ({ data, meta }), como a maioria. O único que vem sem meta é o GET /api/v1/webhooks. Em todos os casos os itens ficam em datanunca numa lista solta na raiz. Se o seu código lê a resposta como um array direto, ele quebra.

// Paginado: itens em .data, com .meta
const leadsRes = await fetch(`${BASE}/leads?apiKey=${apiKey}`);
const { data: leads, meta } = await leadsRes.json();
console.log(`${leads.length} de ${meta.total} leads`);

// Webhooks: itens em .data, mas SEM .meta (sem paginação)
const whRes = await fetch(`${BASE}/webhooks?apiKey=${apiKey}`);
const { data: webhooks } = await whRes.json();
console.log(`${webhooks.length} webhooks`);
# Paginado: itens em ["data"], com ["meta"]
leads_res = requests.get(f"{BASE}/leads", params={"apiKey": api_key})
body = leads_res.json()
leads, meta = body["data"], body["meta"]
print(f"{len(leads)} de {meta['total']} leads")

# Webhooks: itens em ["data"], mas SEM ["meta"]
wh_res = requests.get(f"{BASE}/webhooks", params={"apiKey": api_key})
webhooks = wh_res.json()["data"]
print(f"{len(webhooks)} webhooks")

Quando algo dá errado, o código de status não é 200. O corpo muda de cara: em vez de dados, você recebe uma explicação. O caso mais rico é o 400 (Bad Request), que detalha campo por campo o que precisa ser ajustado.

O envelope de erro

Todo erro da API vem embrulhado num objeto error — nunca na raiz do corpo. Dentro dele há sempre um message (o resumo, em português) e, no caso do 400, uma lista errors detalhando campo por campo.

{
  "error": {
    "message": "Não foi possível criar o lead.",
    "errors": [
      { "field": ["emails"], "message": "É obrigatório informar ao menos um contato." },
      { "field": ["customFields", "orcamento"], "message": "Campo personalizado não encontrado neste projeto." }
    ]
  }
}

Entre em error primeiro

O message e o errors ficam dentro de error — ou seja, resposta.error.message, não resposta.message. Se o seu código lê resposta.message direto, vai achar undefined.

Cada item de errors tem duas partes:

fieldobrigatório

O caminho do campo com problema, na forma de lista (ex.: ["emails"], ["content"], ["customFields", "orcamento"]). Junte com ponto para exibir: emails, customFields.orcamento. É por ele que você sabe onde corrigir.

messageobrigatório

A explicação do problema, já em português, pronta para mostrar a quem for arrumar. É o "por quê" do erro.

As mensagens já vêm em português — reaproveite

Você não precisa traduzir nem reescrever nada. O message de cada erro chega em pt-BR direto do GS Engage. Mostre-o como está no seu log, na sua tela ou no seu ticket.

Estes são os quatro códigos de erro que você vai encontrar na prática. Para cada um: o que ele significa, a causa mais comum e o que fazer.

StatusO que houvePor que costuma acontecerComo corrigir
400 Bad RequestO pedido chegou, mas os dados estão inválidos.Falta um contato no lead, uma nota passou de 1000 caracteres, um customField não existe, um enum recebeu valor fora da lista.Leia a lista errors: cada field diz o campo e cada message (em pt-BR) diz o ajuste. Corrija e reenvie.
401 UnauthorizedA API não reconheceu você.A apiKey está ausente, errada ou foi revogada.Confira o ?apiKey= na URL. Gere/copie a chave em Configurações > Configurações de API e teste com um GET /api/v1/custom-fields.
404 Not FoundO recurso pedido não existe.O id na URL está errado, aponta para um item de outro projeto, ou o item foi removido (ex.: leadId, prospectionId, webhookId inexistente).Verifique o identificador. Liste o recurso antes (ex.: GET /api/v1/leads) para pegar um id válido.
429 Too Many RequestsVocê passou do limite de requisições por minuto.Muitas chamadas na janela de 60s: 200/min de leitura ou 100/min de escrita.Espere os segundos do header Retry-After e tente de novo, com backoff exponencial. Veja abaixo.

401 e o alerta de segurança da apiKey

Um 401 costuma vir de uma chave errada — mas cuidado ao investigar: como a apiKey viaja na URL, ela aparece em logs, no histórico do navegador e em links copiados. Nunca cole a URL completa (com a chave) em chats ou tickets para "pedir ajuda". Trate a chave como senha.

O caso do 429: respeite o Retry-After

O 429 é o único erro que não é culpa dos seus dados — é só ritmo. O corpo segue o mesmo envelope ({ "error": { "message": "Limite de requisições excedido..." } }), mas o que importa aqui são os cabeçalhos HTTP que acompanham a resposta:

Retry-After: 12
X-RateLimit-Limit: 200
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1700000000

O que fazer: espere os segundos indicados em Retry-After (aqui, 12s) antes de tentar de novo. Se falhar outra vez, dobre a espera — isso é backoff exponencial (espera crescente a cada tentativa). Nunca fique martelando a API em loop apertado; você só prolonga o bloqueio.

Vamos juntar tudo. O gatilho abaixo é o mais comum: tentar criar um lead sem nenhum contato. A API recusa com 400 e explica exatamente o que faltou. O código lê a lista errors e mostra cada problema já em português.

const res = await fetch(`${BASE}/leads?apiKey=${apiKey}`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Lead sem contato" }), // falta emails/phones/mobiles
});

if (!res.ok) {
  const { error } = await res.json(); // tudo vem dentro de "error"

  if (res.status === 400) {
    console.error(`O que houve: ${error.message}`);
    // Percorre cada campo com problema — a message já vem em pt-BR.
    // field é uma lista (caminho) — junte com ponto para exibir.
    for (const e of error.errors ?? []) {
      console.error(`  • ${e.field.join(".")}: ${e.message}`);
    }
  } else if (res.status === 401) {
    console.error("apiKey ausente ou inválida — confira sua chave.");
  } else if (res.status === 429) {
    const wait = Number(res.headers.get("Retry-After") || 5);
    console.warn(`Limite atingido — aguarde ${wait}s e tente de novo.`);
  }
}
res = requests.post(
    f"{BASE}/leads",
    params={"apiKey": api_key},
    json={"name": "Lead sem contato"},  # falta emails/phones/mobiles
)

if not res.ok:
    error = res.json().get("error", {})  # tudo vem dentro de "error"

    if res.status_code == 400:
        print(f"O que houve: {error.get('message')}")
        # Percorre cada campo com problema — a message já vem em pt-BR.
        # field é uma lista (caminho) — junte com ponto para exibir.
        for e in error.get("errors", []):
            print(f"  • {'.'.join(map(str, e['field']))}: {e['message']}")
    elif res.status_code == 401:
        print("apiKey ausente ou inválida — confira sua chave.")
    elif res.status_code == 429:
        wait = int(res.headers.get("Retry-After", "5"))
        print(f"Limite atingido — aguarde {wait}s e tente de novo.")

Rodando o exemplo acima, a saída seria algo assim:

O que houve: Não foi possível criar o lead.
 emails: É obrigatório informar ao menos um contato.

O que isso significa para o negócio: você sabe na hora que o lead não entrou porque veio sem forma de contato — e a mensagem em português já pode ir direto para quem preencheu a origem dos dados.

Fazer
  • Cheque o código de status antes de tratar o corpo — ele te diz qual caminho seguir.
  • Percorra a lista errors do 400 e mostre cada field + message.
  • Reaproveite as mensagens em pt-BR do backend como estão.
  • Respeite o Retry-After no 429, com backoff exponencial.
Não fazer
  • Não assuma que toda listagem tem metaGET /api/v1/webhooks traz data sem meta. Mas os itens estão sempre em data.
  • Não trate todo erro como "deu ruim" genérico — cada status pede uma ação diferente.
  • Não repita a chamada em loop apertado após um 429 — você prolonga o bloqueio.
Alguma listagem vem sem meta?

Sim, uma: GET /api/v1/webhooks retorna data sem meta (lista completa, sem paginação). Todas as outras — inclusive custom-fields — usam o formato paginado { data, meta }. Em todos os casos os itens ficam em data.

O 400 sempre traz a lista errors?

Sim — dentro de error. O corpo de um 400 sempre tem error.message (resumo) e error.errors (lista onde field é o caminho do campo, como lista, e message é a explicação). É de lá que você tira exatamente o que corrigir.

Recebi 401 mas tenho certeza de que a chave está certa. O que pode ser?

Confira se a chave está mesmo na URL como ?apiKey=SUA_CHAVE (é query param, não header), se não há espaços ou quebras coladas junto, e se a chave não foi revogada. Teste isolado com GET /api/v1/custom-fields: um 200 confirma que a chave é válida.

Qual a diferença entre 404 e 400?

O 404 diz que o recurso não existe (o id da URL aponta para o nada). O 400 diz que o recurso até faz sentido, mas os dados enviados estão inválidos. Um é "não achei isso"; o outro é "o que você mandou está errado".

As mensagens de erro vêm em inglês?

Não. O GS Engage devolve as mensagens de erro já em português. Você pode exibi-las direto para a pessoa que vai corrigir, sem traduzir.

Este artigo foi útil?

Nesta página

Última atualização