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"
}
}
| Campo | Significado |
|---|---|
error.code | Código estável no formato <area>.<reason>. Baseie a lógica do seu código nele. |
error.message | Uma explicação legível em inglês. Ela pode ser reescrita a qualquer momento; não a interprete por código. |
error.details | Fatos estruturados sobre o erro, como o parâmetro, um limite ou quando tentar de novo. Os campos dependem do código. |
error.request_id | O ID desta requisição, o mesmo do cabeçalho de resposta X-Request-Id. |
error.doc_url | A página sobre este código neste portal. |
detail | A 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
| Status | Significado |
|---|---|
200, 201, 202, 204 | A requisição deu certo. 202 significa que ela foi aceita e ainda está sendo processada. |
400 | A requisição está malformada: um parâmetro, um cursor, metadata ou Idempotency-Key inválido. Corrija a requisição. |
401 | Não há chave válida. Veja Autenticação. |
402 | Um limite do plano da organização foi atingido, como o número de cartões ou o armazenamento. |
403 | A chave é válida, mas não tem permissão para isso: falta uma função, um escopo ou um recurso do plano. |
404 | O objeto não existe ou a chave não tem permissão para vê-lo. |
409 | A 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. |
410 | O objeto expirou, como um upload depois de 24 horas, ou a rota foi desativada. |
413 | O corpo ou o arquivo é grande demais. |
415 | O tipo de arquivo não é aceito ou o conteúdo não corresponde ao nome dele. |
422 | A requisição está bem formada, mas é inválida: falta um campo ou um valor está fora do intervalo. |
429 | Requisições demais ou uma cota se esgotou. Veja Limites de taxa e cotas. |
500, 502, 503, 504 | Um 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
| Erro | Tentar de novo? |
|---|---|
| Erro de rede ou tempo limite, sem resposta | Sim, com a mesma Idempotency-Key. |
429 | Sim, depois do número de segundos indicado em Retry-After. |
500, 502, 503, 504 | Sim, com backoff exponencial: 1 s, 2 s, 4 s… até um minuto, com jitter aleatório. |
409 api.idempotency_in_progress | Sim, depois do Retry-After: a primeira requisição com esta chave ainda está em execução. |
Qualquer outro 4xx | Nã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.