Kode Error
Daftar lengkap status HTTP dan pesan galat yang benar-benar dikembalikan endpoint ber-API-key AdForm, apa artinya, dan tindakan yang tepat untuk masing-masing.
Semua galat berbentuk JSON dengan satu field:
{ "error": "Missing scope: products:write" }Sebagian besar endpoint hanya mengirim field error itu: tanpa kode galat mesin, tanpa request_id, tanpa rincian per-field. Yang membedakan adalah status HTTP ditambah teks error.
Kecualinya /api/order_status.php, yang mengirim dua field tambahan pada galat yang berasal dari endpoint itu sendiri:
{ "error": "Pesanan tidak ditemukan.", "code": "order_not_found", "request_id": "9f2c41ab" }code adalah penanda tetap yang aman dijadikan cabang program; request_id untuk penelusuran saat melapor. Daftar lengkapnya di Status Pesanan.
Galat yang berasal dari lapisan bersama di depan endpoint — 401 dan 403 dari pemeriksa kunci API, 429 dari pembatas laju — tetap hanya memuat error, termasuk untuk endpoint tersebut.
Cabangkan program Anda pada status HTTP, bukan pada teks error. Teks bisa berubah kata; status tidak. Pakai teks untuk log dan untuk pesan ke manusia.
Field success tidak dikirim pada response galat. Jangan menguji data.success === false — uji res.ok atau status HTTP.
Daftar di bawah dikumpulkan dari kode yang berjalan di produksi. Cakupannya tiga berkas endpoint yang memeriksa API key: /api/products.php (?action=embed dan ?action=duplicate), /api/customer_lookup.php, serta /api/order_status.php.
#Ringkasan status
| Status | Arti | Ulangi otomatis? |
|---|---|---|
200 | Berhasil, operasi baca. | — |
201 | Berhasil, objek baru dibuat. | — |
400 | Permintaan Anda salah bentuk atau tidak lolos validasi. | Tidak. Perbaiki request. |
401 | Masalah pada API key. | Tidak. Perbaiki konfigurasi. |
403 | Key valid, tapi tidak berhak. | Tidak. Perbaiki scope atau status akun. |
404 | Objek tidak ada di tenant pemilik key. | Tidak. |
405 | Method HTTP salah. Hanya /api/customer_lookup.php, yang menerima POST saja. | Tidak. Perbaiki request. |
413 | Body permintaan lebih dari 4 KB. Hanya /api/customer_lookup.php. | Tidak. Perbaiki request. |
429 | Batas laju terlampaui. | Ya, setelah menunggu sesuai Retry-After. |
500 | Kesalahan di sisi AdForm. | Untuk operasi baca ya, dengan jeda membesar. Untuk operasi tulis, periksa dulu. |
Tidak ada 405 pada /api/products.php: yang menentukan operasi di sana adalah nilai action, bukan method HTTP. /api/customer_lookup.php sebaliknya — ia menolak semua method selain POST dengan 405, supaya nomor telepon tidak pernah masuk query string. Tidak ada 402 pada jalur API key.
/api/customer_lookup.php juga tidak pernah membalas 500 untuk kegagalan basis data. Kalau pencarian pesanan gagal di sisi kami, responsnya tetap 200 dengan meta.degraded bernilai true. Lihat Pencarian Pelanggan.
#Galat autentikasi
Semua diperiksa sebelum request menyentuh data produk, dengan urutan seperti di tabel.
| Status | error | Penyebab | Yang harus dilakukan |
|---|---|---|---|
401 | API key required (X-API-Key header) | Header X-API-Key tidak dikirim, atau nilainya string kosong. | Kirim header. Periksa apakah proxy atau klien HTTP Anda membuang header kustom. |
401 | Malformed API key | Nilai key tidak cocok pola adk_ diikuti 48 karakter heksadesimal huruf kecil. | Periksa key yang tersalin utuh, tanpa spasi/baris baru, dan tidak ter-uppercase. Diperiksa sebelum menyentuh database. |
401 | Invalid API key | Bentuknya benar, tapi tidak ada di database. Salah ketik, atau key sudah dihapus. | Pastikan Anda memakai key untuk lingkungan yang benar. Kalau hilang, minta key baru. |
401 | API key revoked | Key ada, tapi sudah dinonaktifkan. | Key ini tidak akan hidup lagi. Minta key baru. |
401 | API key expired | Key punya tanggal kedaluwarsa dan tanggal itu sudah lewat menurut jam server. | Minta key baru, atau minta key tanpa masa berlaku. |
403 | Tenant inactive | Akun tenant pemilik key sedang nonaktif. | Ini urusan akun, bukan integrasi. Pemilik akun perlu menghubungi AdForm. |
Jangan mengulang otomatis satu pun dari daftar ini. Semuanya kesalahan konfigurasi, bukan gangguan sementara — mengulang hanya menghabiskan jatah batas laju Anda.
Tidak ada header WWW-Authenticate pada response 401.
#Galat scope
| Status | error | Penyebab |
|---|---|---|
403 | Missing scope: products:read | Key valid, tapi tidak memuat scope products:read. Muncul pada action=embed. |
403 | Missing scope: products:write | Key valid, tapi tidak memuat scope products:write. Muncul pada action=duplicate. |
403 | Missing scope: customers:read | Key valid, tapi tidak memuat scope customers:read. Muncul pada POST /api/customer_lookup.php. |
Pencocokan scope adalah kecocokan string persis. Tidak ada pola glob — products:* bukan scope yang sah dan tidak cocok dengan apa pun. Key yang memuat * melewati semua pemeriksaan scope.
Pesan hanya menyebut satu scope pertama yang kurang, bukan daftar lengkap yang dibutuhkan.
403 di sini permanen sampai key diganti. Minta key baru dengan scope yang sesuai; scope key yang sudah ada tidak bisa ditambah lewat API.
#Galat batas laju
| Status | error | Header tambahan |
|---|---|---|
429 | Terlalu banyak permintaan. Coba lagi nanti. | Retry-After: <detik> |
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json; charset=utf-8
{"error":"Terlalu banyak permintaan. Coba lagi nanti."}Ini satu-satunya galat yang layak diulang otomatis. Tunggu selama nilai Retry-After (dalam detik), lalu coba lagi, dengan jeda yang membesar kalau tetap kena.
Pemeriksaan batas laju berjalan sebelum pemeriksaan API key. Request dengan key salah pun tetap memakan jatah. Jangan memakai percobaan berulang untuk menebak key atau ID — Anda hanya akan mengunci IP Anda sendiri.
Tidak ada header sisa kuota (X-RateLimit-Remaining dan sejenisnya). Rinciannya di Batas Penggunaan.
#Galat validasi
Semua berstatus 400.
#Umum
error | Operasi | Penyebab |
|---|---|---|
id required | embed, duplicate | Query id tidak dikirim, bukan angka, atau ≤ 0. |
Unknown action: <nilai> | — | Nilai action bukan embed atau duplicate. Contoh: {"error":"Unknown action: list"}. |
phone wajib diisi | Pencarian Pelanggan | Field phone tidak ada, bukan nilai skalar, atau hanya spasi. |
phone tidak valid | Pencarian Pelanggan | Setelah dibersihkan, nomornya tidak menghasilkan bentuk yang bisa dicocokkan. |
#Khusus pencarian pelanggan
Selain dua baris 400 di atas, endpoint ini punya dua status yang tidak muncul di /api/products.php:
| Status | error | Penyebab |
|---|---|---|
405 | Method not allowed | Method bukan POST. Disengaja: nomor telepon tidak boleh masuk query string. |
413 | Payload too large | Body lebih dari 4 KB, dihitung dari header Content-Length maupun dari byte yang benar-benar dibaca. |
#Khusus duplikat produk
error | Penyebab |
|---|---|
name required | Body tidak memuat name, atau name hanya spasi. Sering kali penyebab sebenarnya adalah body dikirim sebagai form-encoded — server hanya membaca JSON. |
cannot derive slug from name | slug tidak dikirim dan name tidak memuat karakter yang bisa dijadikan slug (misalnya hanya simbol atau hanya karakter non-latin). Kirim slug sendiri. |
#Validasi matriks harga varian
Pemeriksaan ini ada supaya form tidak pernah menagih harga yang salah. Semua dibalas 400. <label> diisi variant_prices atau variant_compare_prices, sesuai bagian mana yang bermasalah.
error | Penyebab |
|---|---|
Harga varian harus daftar angka untuk 1 variasi. | Produk hanya punya satu variasi, tapi variant_prices dikirim sebagai array bersarang. Untuk satu variasi, bentuknya daftar angka datar. |
Setiap variasi minimal punya 1 opsi. | Ada variasi dengan options kosong atau tidak berbentuk array. |
<label> bentuk matriks tidak sesuai jumlah opsi variasi. | Kedalaman atau jumlah elemen matriks tidak persis sama dengan jumlah opsi tiap variasi. Dua variasi berukuran 2×3 menuntut 2 baris berisi 3 angka. |
<label> nilai sel harus angka. | Ada sel daun yang berisi array, bukan angka — biasanya matriks bersarang satu tingkat terlalu dalam. |
<label> tiap kombinasi wajib diisi harga lebih dari 0 (ada sel kosong/0). | Ada sel variant_prices yang kosong, 0, atau bukan angka positif. Berlaku untuk variant_prices saja; pada variant_compare_prices nilai 0 diperbolehkan dan berarti "tanpa harga coret". |
Contoh utuh:
{ "error": "variant_prices bentuk matriks tidak sesuai jumlah opsi variasi." }#Galat objek tidak ditemukan
| Status | error | Operasi | Penyebab |
|---|---|---|---|
404 | Product not found | embed | Tidak ada produk dengan id tersebut di tenant pemilik key. |
404 | Source product not found | duplicate | Tidak ada produk sumber dengan id tersebut di tenant pemilik key. |
Objek milik tenant lain juga dibalas 404, bukan 403. Jadi 404 berarti salah satu dari dua hal: ID-nya memang tidak ada, atau ID itu milik tenant lain. Dari luar keduanya tidak bisa dibedakan, dan itu disengaja.
#Galat sisi AdForm
| Status | error | Penyebab |
|---|---|---|
500 | Terjadi kesalahan sistem. Coba beberapa saat lagi. | Pengecualian yang tidak tertangani di sisi kami. Pesan sengaja umum; rinciannya masuk ke log internal AdForm. |
Kalau Anda menemukan 500 yang berulang, laporkan lewat Pusat Bantuan dan sertakan waktu kejadian (dengan zona waktu), URL yang dipanggil, dan 12 karakter pertama key yang dipakai (adk_0123456). Jangan mengirim key utuh.
Status 502, 503, dan 504 bisa muncul dari lapisan web server atau CDN sebelum kode AdForm dijalankan. Body-nya belum tentu JSON — jangan berasumsi setiap response bisa di-parse sebagai JSON. Perlakukan sebagai gangguan sementara.
#Kesalahan yang tidak menghasilkan galat
Dua hal ini gagal tanpa pesan yang mengarah ke penyebabnya. Kami tulis supaya Anda tidak menghabiskan waktu mencarinya:
- Body form-encoded. Server hanya membaca body sebagai JSON. Kalau Anda mengirim
application/x-www-form-urlencoded, semua field dianggap tidak ada dan Anda melihat400 name requireduntuknameyang jelas-jelas sudah Anda kirim. - Lupa
action. Tanpa queryaction, request tidak masuk ke jalur API key sama sekali, melainkan ke jalur dashboard yang memakai sesi login. HeaderX-API-KeyAnda diabaikan di jalur itu, danPOSTdibalas401 {"error":"Unauthorized"}— pesan yang membingungkan kalau Anda mengira key-nya yang bermasalah. Periksaactionsebelum memeriksa key.
#Pola penanganan yang kami sarankan
async function callAdForm(url, init = {}) {
const res = await fetch(url, {
...init,
headers: {
'X-API-Key': process.env.ADFORM_API_KEY, // hanya di server
Accept: 'application/json',
...init.headers,
},
});
// 502/503/504 bisa membalas HTML, bukan JSON.
const text = await res.text();
let body;
try { body = JSON.parse(text); } catch { body = { error: text.slice(0, 200) }; }
if (res.ok) return body;
const err = new Error(`AdForm ${res.status}: ${body.error ?? 'tanpa pesan'}`);
err.status = res.status;
err.retryAfter = Number(res.headers.get('Retry-After')) || null;
// 429 → tunggu err.retryAfter detik lalu ulangi.
// 5xx pada operasi baca → ulangi dengan jeda membesar.
// 4xx lain → hentikan, ini kesalahan konfigurasi atau data.
err.retryable = res.status === 429 || res.status >= 500;
throw err;
}