Pular para o conteúdo
Sursa Developers

Erros

O Sursa usa os códigos de status HTTP para dizer se uma requisição deu certo e um código de erro estável para dizer por que ela não deu. Toda resposta de erro tem o mesmo formato:

{
  "detail": "the cursor is malformed or was issued for different filters",
  "error": {
    "code": "api.cursor_invalid",
    "message": "the cursor is malformed or was issued for different filters",
    "details": {"parameter": "cursor"},
    "request_id": "01JB7Q2M4X9V3K8T5N6P0R1S2A",
    "doc_url": "https://developers.sursa.com/errors/api.cursor_invalid"
  }
}
CampoSignificado
error.codeCódigo estável no formato <area>.<reason>. Baseie a lógica do seu código nele.
error.messageUma explicação legível em inglês. Ela pode ser reescrita a qualquer momento; não a interprete por código.
error.detailsFatos estruturados sobre o erro, como o parâmetro, um limite ou quando tentar de novo. Os campos dependem do código.
error.request_idO ID desta requisição, o mesmo do cabeçalho de resposta X-Request-Id.
error.doc_urlA página sobre este código neste portal.
detailA forma anterior do erro, mantida para as integrações existentes. O código novo deve ler error.

Cada código tem uma página própria em Códigos de erro, com o status HTTP, os campos de details e os endpoints que o retornam. A página de cada endpoint na referência da API lista os códigos que esse endpoint pode retornar.

Status HTTP

StatusSignificado
200, 201, 202, 204A requisição deu certo. 202 significa que ela foi aceita e ainda está sendo processada.
400A requisição está malformada: um parâmetro, um cursor, metadata ou Idempotency-Key inválido. Corrija a requisição.
401Não há chave válida. Veja Autenticação.
402Um limite do plano da organização foi atingido, como o número de cartões ou o armazenamento.
403A chave é válida, mas não tem permissão para isso: falta uma função, um escopo ou um recurso do plano.
404O objeto não existe ou a chave não tem permissão para vê-lo.
409A requisição entra em conflito com o estado atual: um external_id duplicado, uma sincronização aberta, uma requisição com a mesma Idempotency-Key ainda em execução.
410O objeto expirou, como um upload depois de 24 horas, ou a rota foi desativada.
413O corpo ou o arquivo é grande demais.
415O tipo de arquivo não é aceito ou o conteúdo não corresponde ao nome dele.
422A requisição está bem formada, mas é inválida: falta um campo ou um valor está fora do intervalo.
429Requisições demais ou uma cota se esgotou. Veja Limites de taxa e cotas.
500, 502, 503, 504Um problema do lado do Sursa. Tente de novo mais tarde.

Erros de validação

Em um 422 com o código validation.failed, detail lista todos os campos que falharam, cada um com a sua localização:

{
  "detail": [
    {"loc": ["body", "title"], "msg": "String should have at least 1 character", "type": "string_too_short"}
  ],
  "error": {"code": "validation.failed", "message": "request validation failed", "request_id": "01JB7…"}
}

Quais erros merecem nova tentativa

ErroTentar de novo?
Erro de rede ou tempo limite, sem respostaSim, com a mesma Idempotency-Key.
429Sim, depois do número de segundos indicado em Retry-After.
500, 502, 503, 504Sim, com backoff exponencial: 1 s, 2 s, 4 s… até um minuto, com jitter aleatório.
409 api.idempotency_in_progressSim, depois do Retry-After: a primeira requisição com esta chave ainda está em execução.
Qualquer outro 4xxNão. A mesma requisição vai falhar de novo; corrija-a primeiro.

Repita requisições POST e PATCH só com uma Idempotency-Key, para que uma nova tentativa depois de uma resposta perdida não possa criar um segundo objeto. GET, PUT e DELETE podem ser repetidos com segurança como estão.

// Retries a request on network errors, 429 and 5xx. The same Idempotency-Key is sent on every attempt.
async function withRetries(send, attempts = 5) {
  for (let attempt = 1; ; attempt++) {
    let response;
    try {
      response = await send();
    } catch (error) {
      if (attempt >= attempts) throw error;
    }
    if (response && response.status !== 429 && response.status < 500) return response;
    if (attempt >= attempts) return response;
    const retryAfter = Number(response?.headers.get("Retry-After"));
    const backoff = Math.min(60, 2 ** (attempt - 1)) * (0.8 + Math.random() * 0.4);
    await new Promise((resolve) => setTimeout(resolve, 1000 * (retryAfter || backoff)));
  }
}

const key = crypto.randomUUID();
const response = await withRetries(() =>
  fetch("https://api.sursa.com/v1/cards/text", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SURSA_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": key,
    },
    body: JSON.stringify({ title: "Refund policy", body_markdown: "Refunds take 14 days." }),
  }),
);

Como relatar um problema

Toda resposta, bem-sucedida ou não, tem um cabeçalho X-Request-Id, e todo erro tem o mesmo valor em error.request_id. Registre-o junto com o seu próprio registro da requisição. Ao entrar em contato com o suporte sobre uma requisição, envie o ID dela: ele leva direto ao registro dessa requisição.

Tratamento de códigos desconhecidos

Novos códigos de erro podem aparecer em qualquer versão da API. Trate um código desconhecido pelo status HTTP: um 4xx desconhecido, como uma requisição que não vai dar certo do jeito que está; um 5xx desconhecido, como um problema temporário.