Error
Sursa memakai kode status HTTP untuk menyatakan apakah permintaan berhasil, dan kode error yang stabil untuk menyatakan mengapa permintaan tidak berhasil. Setiap respons error memiliki bentuk yang sama:
{
"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"
}
}
| Field | Artinya |
|---|---|
error.code | Kode stabil dalam bentuk <area>.<reason>. Buat percabangan kode Anda berdasarkan kode ini. |
error.message | Penjelasan bahasa Inggris yang mudah dibaca. Susunan katanya dapat berubah kapan saja; jangan menguraikannya. |
error.details | Fakta terstruktur tentang error, seperti parameter, batas, atau kapan harus mencoba ulang. Field-nya bergantung pada kode. |
error.request_id | ID permintaan ini, sama dengan header respons X-Request-Id. |
error.doc_url | Halaman tentang kode ini di portal ini. |
detail | Bentuk error yang lebih lama, dipertahankan untuk integrasi yang sudah ada. Kode baru sebaiknya membaca error. |
Setiap kode memiliki halamannya sendiri di Kode error, dengan status HTTP-nya, field details, dan
endpoint yang mengembalikannya. Halaman setiap endpoint di referensi API mencantumkan kode yang dapat
dikembalikan endpoint itu.
Status HTTP
| Status | Artinya |
|---|---|
200, 201, 202, 204 | Permintaan berhasil. 202 berarti permintaan diterima dan masih diproses. |
400 | Permintaan salah bentuk: parameter, kursor, metadata, atau Idempotency-Key yang tidak benar. Perbaiki permintaannya. |
401 | Tidak ada kunci yang valid. Lihat Autentikasi. |
402 | Batas paket organisasi tercapai, seperti jumlah kartu atau penyimpanan. |
403 | Kunci valid tetapi tidak diizinkan melakukan hal ini: peran, cakupan, atau fitur paket tidak ada. |
404 | Objek tidak ada, atau kunci tidak boleh melihatnya. |
409 | Permintaan bertentangan dengan keadaan saat ini: external_id duplikat, sinkronisasi yang masih terbuka, permintaan dengan Idempotency-Key yang sama masih berjalan. |
410 | Objek sudah kedaluwarsa, seperti unggahan setelah 24 jam, atau rute sudah dihentikan. |
413 | Isi permintaan atau file terlalu besar. |
415 | Jenis file tidak didukung, atau isinya tidak sesuai dengan namanya. |
422 | Permintaan berbentuk benar tetapi tidak valid: field yang tidak ada atau nilai di luar rentang. |
429 | Terlalu banyak permintaan atau kuota sudah habis. Lihat Batas laju dan kuota. |
500, 502, 503, 504 | Masalah di sisi Sursa. Coba lagi nanti. |
Error validasi
Untuk 422 dengan kode validation.failed, detail mencantumkan setiap field yang
gagal, masing-masing dengan lokasinya:
{
"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…"}
}
Error mana yang perlu dicoba ulang
| Error | Coba ulang? |
|---|---|
| Error jaringan atau timeout, tanpa respons | Ya, dengan Idempotency-Key yang sama. |
429 | Ya, setelah jumlah detik di Retry-After. |
500, 502, 503, 504 | Ya, dengan backoff eksponensial (jeda yang makin panjang): 1 detik, 2 detik, 4 detik… hingga satu menit, dengan jitter (variasi acak). |
409 api.idempotency_in_progress | Ya, setelah Retry-After: permintaan pertama dengan kunci ini masih berjalan. |
4xx lainnya | Tidak. Permintaan yang sama akan gagal lagi; perbaiki dulu. |
Coba ulang permintaan POST dan PATCH hanya dengan Idempotency-Key, agar percobaan
ulang setelah respons yang hilang tidak dapat membuat objek kedua. GET, PUT, dan DELETE aman untuk dicoba ulang
apa adanya.
// 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." }),
}),
);Melaporkan masalah
Setiap respons, berhasil atau tidak, memiliki header X-Request-Id, dan setiap error memiliki nilai yang sama di
error.request_id. Catat nilai itu bersama catatan permintaan Anda sendiri. Saat Anda menghubungi dukungan tentang
sebuah permintaan, kirim ID permintaannya: ID itu langsung mengarah ke catatan permintaan tersebut.
Menangani kode yang tidak Anda kenal
Kode error baru dapat muncul di versi API mana pun. Tangani kode yang tidak dikenal menurut status HTTP-nya: 4xx
yang tidak dikenal sebagai permintaan yang tidak akan berhasil apa adanya, 5xx yang tidak dikenal sebagai masalah
sementara.