Lompat ke konten utama
Dokumentasi AdForm
llms.txt Pusat Bantuan

Status Pesanan (server-ke-server)

Referensi API status pesanan AdForm: baca satu pesanan dan pindahkan statusnya lewat kunci API. Scope, penguncian optimistik, idempotensi terikat isi permintaan, dan seluruh kode error.

Endpoint ini dipakai sistem lain untuk membaca satu pesanan dan memindahkan statusnya antara pending dan closing. Contoh pemakaian yang paling umum: aplikasi closing atau CRM tempat CS menutup deal, lalu status di AdForm ikut berpindah tanpa ada yang mengetik ulang.

Semua panggilan memakai kunci API yang diterbitkan penjual sendiri dari dashboard. Anda tidak pernah meminta kunci lewat WhatsApp, email, atau chat.

#Rangkuman satu layar

ItemNilai
Alamathttps://<domain-penjual>/api/order_status.php
MethodGET (baca), PATCH (ubah status)
AuthHeader X-API-Key: adk_…
Scope bacaorders:read (+ customers:read bila butuh data pelanggan)
Scope ubahorders.status:write
Status yang boleh ditulispending, closing — tidak ada yang lain
Batas laju120 permintaan per menit per kunci
IdempotensiHeader Idempotency-Key (opsional, sangat disarankan untuk PATCH)
Content-Typeapplication/json; charset=utf-8

#Cara penjual menerbitkan kunci

Penjual masuk ke dashboard AdForm → Pengaturan → Developer → API Keys → Buat API key:

  1. Nama konektor — isi nama sistem yang akan memakainya, misalnya CRM Closing. Nama ini ikut terkirim ke webhook sebagai penanda siapa yang mengubah status, jadi pakai nama yang mudah dikenali penjual sendiri. Harus berbeda dari key lain yang masih aktif di toko itu — kalau sudah dipakai, penjual menerima penolakan 409 dan diminta memilih nama lain.
  2. Kegunaan — pilih Ubah status pesanan. Kalau sistem Anda juga perlu nama, nomor, atau alamat pembeli, penjual memilih Ubah status pesanan + baca data pelanggan.
  3. Kedaluwarsa — pilih tanggal secara sengaja.

Nilai kunci tampil satu kali setelah dibuat. Penjual menyalinnya lalu memasukkannya ke sistem Anda sendiri.

Penjual bisa mencabut kunci kapan saja dari halaman yang sama. Kolom Terakhir dipakai membantunya memastikan sebuah kunci sudah tidak terpakai sebelum dicabut.

#Izin yang diberikan pilihan "Ubah status pesanan"

Kunci dengan kegunaan ini mendapat tepat dua izin: orders:read dan orders.status:write.

Yang bisa dilakukan:

  • membaca satu pesanan berdasarkan nomornya;
  • memindahkan status pesanan antara pending dan closing.

Yang tidak bisa dilakukan:

  • membaca nama, nomor WhatsApp, surel, atau alamat pembeli — itu izin terpisah, lihat di bawah;
  • menandai pesanan lunas, dikirim, selesai, dibatalkan, atau nomor tidak valid;
  • mengubah harga, jumlah, alamat, atau data pelanggan;
  • menghapus pesanan;
  • membuat pesanan baru;
  • membaca daftar pesanan, produk, atau anggota tim.

#Data pelanggan adalah izin terpisah

orders:read berarti boleh membaca pesanan — produk, jumlah, total, ongkir, kurir, resi, status, waktu dibuat. Blok customer (nama, nomor, surel, alamat lengkap) hanya ikut kalau kunci juga memegang customers:read.

Kalau Anda hanya menyelaraskan status, Anda tidak perlu izin itu. Rekonsiliasi berjalan penuh tanpa satu pun kolom pribadi, dan penjual tidak perlu menyerahkan data pembelinya untuk sesuatu yang tidak memerlukannya.

Jalur tulis tidak pernah mengirim data pelanggan, bahkan untuk kunci yang memegang customers:read. Yang perlu membaca pesanan memakai GET.

orders.status:write sengaja terpisah dari orders:write. Kunci lama yang sudah terbit dengan orders:write tidak ikut mendapat kemampuan ubah-status saat endpoint ini dipasang.

#Arti status di AdForm — baca ini sebelum menulis kode

AdForm punya satu kolom status. Ia mencampur status pembayaran dan tahap penanganan dalam satu nilai. Tidak ada kolom terpisah untuk "sudah dibayar" dan "sudah dikirim".

Ini jebakan yang paling mahal, jadi kami tulis dengan angkanya.

Ketika pembayaran otomatis lewat payment gateway berhasil, AdForm menulis status closing, bukan paid. Status paid hanya pernah ditulis kalau penjual menandainya sendiri dari dashboard.

Sebaran nyata seluruh pesanan di AdForm, diukur 27 Agustus 2026 dari 83 basis data toko, 57.323 baris:

StatusJumlah barisPorsi
pending40.94571,43%
closing13.59723,72%
invalid_no_hp1.3202,30%
completed6771,18%
paid3980,69%
shipped2080,36%
cancelled1780,31%

paid adalah 398 baris dari 57.323. Sistem yang menunggu paid sebagai tanda pembayaran masuk akan menunggu sesuatu yang praktis tidak pernah terjadi.

#Sisi baiknya

95,15% pesanan berada di pending atau closing — dua status yang endpoint ini izinkan Anda tulis. Batasan yang terlihat sempit itu mencakup hampir seluruh pesanan yang benar-benar ada.

#Tujuh nilai yang mungkin

pending, closing, invalid_no_hp, paid, cancelled, shipped, completed.

Anda boleh menulis dua: pending dan closing. Anda akan membaca ketujuhnya lewat GET. Perlakukan lima sisanya sebagai keadaan yang hanya bisa diubah penjual dari dashboard-nya.

#GET — baca satu pesanan

HTTP
GET /api/order_status.php?id=1287
X-API-Key: adk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
JSON
{
  "success": true,
  "order": {
    "id": 1287,
    "status": "pending",
    "product": "Kopi Gayo 250g",
    "variant": "Bubuk",
    "quantity": 2,
    "total": 213000,
    "shipping_fee": 15000,
    "payment_method": "cod",
    "courier": "JNE",
    "resi": null,

    "_catatan": "blok customer di bawah HANYA ikut kalau kunci memegang customers:read",
    "customer": {
      "name": "Rina",
      "phone": "628111000111",
      "email": null,
      "address": "Jl. Merdeka No. 10 RT 02 RW 05",
      "village": "Dago",
      "district": "Coblong",
      "city": "Bandung",
      "province": "Jawa Barat",
      "postal_code": "40135"
    },
    "created_at": "2026-08-26T09:14:00+07:00"
  }
}

Daftar field di atas tetap. Kolom baru yang ditambahkan AdForm di kemudian hari tidak otomatis ikut terkirim.

Field _catatan di contoh itu bukan bagian jawaban sungguhan — hanya penanda di dokumen ini. Jawaban juga membawa request_id di tingkat teratas; sebutkan nilai itu kalau Anda melaporkan masalah.

#PATCH — ubah status

HTTP
PATCH /api/order_status.php
X-API-Key: adk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Idempotency-Key: spc-1287-closing-01
Content-Type: application/json

{
  "id": 1287,
  "status": "closing",
  "expected_previous_status": "pending",
  "external_reference": "SPC-8871"
}
FieldWajibKeterangan
idyaNomor pesanan AdForm
statusyapending atau closing
expected_previous_statustidakStatus yang Anda kira sedang berlaku. Kalau tidak cocok, permintaan ditolak 409 dan tidak ada yang berubah
external_referencetidakNomor rujukan di sistem Anda, maksimal 120 karakter. Diteruskan ke webhook

Jawaban berhasil:

JSON
{
  "order_id": 1287,
  "previous_status": "pending",
  "status": "closing",
  "updated_at": "2026-08-27T14:31:00+07:00",
  "request_id": "9f2c41ab",
  "changed": true
}

Keberhasilan ditandai kode HTTP 200. Jawaban ini tidak membawa data pelanggan — Anda menyatakan tidak memerlukannya pada jalur tulis, dan kami tidak mengirimkannya.

FieldArti
order_idNomor pesanan yang diubah
previous_statusStatus sebelum permintaan ini
statusStatus sesudahnya
updated_atWaktu perubahan diterapkan (ISO 8601, zona server). null kalau changed bernilai false — tidak ada perubahan berarti tidak ada waktu perubahan
request_idPenanda permintaan, 8 karakter heksadesimal. Sebutkan saat melapor masalah
changedfalse berarti pesanan sudah berada di status yang Anda minta. Itu keberhasilan, bukan kegagalan

Tidak semua jawaban gagal membawa request_id. Yang berasal dari endpoint ini membawanya: 400, 404, 405, 409, 422, 500. Yang berasal dari lapisan bersama di depannya tidak, dan hanya memuat field error:

StatusDitangani olehBentuk badan
401, 403pemeriksa kunci API bersama{ "error": "..." }
429pembatas laju bersama{ "error": "..." }

Lapisan itu melayani seluruh endpoint ber-kunci-API, jadi bentuk jawabannya sengaja tidak diubah demi satu endpoint. Untuk kedua kelompok status tersebut, cabangkan program Anda pada status HTTP, bukan pada keberadaan code atau request_id.

#expected_previous_status — kapan dipakai

Kirimkan kalau keputusan Anda didasarkan pada status yang Anda baca sebelumnya.

Contoh: sistem Anda membaca pesanan pukul 10.00 (status pending), lalu CS menekan tombol closing pukul 10.05. Di antara dua waktu itu penjual mungkin sudah menandai pesanan tersebut lunas dari dashboard. Dengan expected_previous_status: "pending", permintaan Anda ditolak alih-alih menimpa keputusan yang lebih baru.

#Idempotensi

Kirim header Idempotency-Key pada setiap PATCH. Isinya bebas, 8–120 karakter dari A-Z a-z 0-9 . _ : -, dan harus unik per permintaan — misalnya spc-<nomor-pesanan>-<status>-<percobaan>.

Kalau permintaan Anda time-out dan Anda tidak tahu apakah ia sampai, kirim ulang dengan kunci dan isi yang sama persis. AdForm mengembalikan jawaban aslinya tanpa mengubah pesanan lagi, disertai header:

HTTP
Idempotency-Replayed: true

Yang diputar ulang adalah jawaban aslinya, jadi request_id dan updated_at yang kembali adalah milik permintaan pertama. Itu memang yang benar: perubahannya terjadi saat itu, bukan saat kiriman ulang tiba.

Aturannya:

  • Kunci sama + isi sama → jawaban asli diputar ulang. Pesanan tidak tersentuh.
  • Kunci sama + isi berbeda → ditolak 422 idempotency_key_reused. Ini melindungi Anda: kunci yang tidak sengaja dipakai ulang untuk pesanan lain akan ketahuan, bukan diam-diam mengembalikan hasil pesanan pertama.
  • Kunci sama, permintaan pertama masih berjalan → 409 request_in_progress. Tunggu beberapa detik lalu coba lagi.

Yang diingat hanyalah keberhasilan. Setiap penolakan melepaskan catatannya, jadi kiriman ulang dengan kunci yang sama dinilai ulang dari awal. Contoh: permintaan Anda ditolak 422 closing_incomplete karena alamat pesanan belum lengkap; penjual melengkapinya dari dashboard; Anda kirim ulang permintaan yang sama persis dan kali ini berhasil. Kalau penolakan ikut diingat, Anda akan menerima penolakan lama itu selama 30 hari meski datanya sudah benar.

Urutan penulisan kunci JSON tidak berpengaruh: {"id":1,"status":"closing"} dan {"status":"closing","id":1} dianggap permintaan yang sama.

Masa simpan catatan idempotensi: 30 hari sejak permintaan pertama. Setelah itu catatannya dihapus, dan kiriman ulang dengan kunci yang sama akan dijalankan sebagai permintaan baru. Ini disengaja — tidak ada pemanggil yang mengulang permintaan sebulan kemudian, dan menyimpannya selamanya berarti tabel yang tumbuh tanpa batas.

#Kode error

Semua kegagalan menjawab JSON dengan field error (kalimat untuk manusia) dan code (penanda tetap untuk program Anda).

HTTPcodeArtiYang harus Anda lakukan
400invalid_jsonBody bukan JSON objekPerbaiki permintaan
400invalid_requestid atau status kosong / bukan angka positifPerbaiki permintaan
401—Kunci tidak ada, salah bentuk, dicabut, atau kedaluwarsaMinta penjual menerbitkan kunci baru
403—Kunci sah tetapi tidak punya scope yang dibutuhkanMinta penjual membuat kunci dengan kegunaan Ubah status pesanan
404order_not_foundPesanan tidak ada di akun penjual iniJangan ulangi
405method_not_allowedMethod selain GET/PATCHPerbaiki permintaan
409status_conflictexpected_previous_status tidak cocok dengan status sekarangBaca ulang pesanannya, putuskan lagi
409order_terminalStatus sekarang sudah final (paid, shipped, completed, cancelled, invalid_no_hp)Jangan ulangi. Status final milik penjual
409request_in_progressPermintaan dengan kunci idempotensi sama sedang berjalanTunggu 5 detik, coba lagi
409update_conflictStatus berubah tepat saat diprosesUlangi permintaan
422status_not_allowedstatus di luar pending/closingJangan ulangi
422expected_status_not_allowedexpected_previous_status di luar pending/closing. Nilai itu tidak akan pernah cocok, jadi ditolak sekarang alih-alih jadi 409 yang membingungkanJangan ulangi
422external_reference_too_longexternal_reference lebih dari 120 karakterPerpendek, lalu ulangi
422closing_incompletePenjual memakai integrasi AdStack dan data pesanan belum lengkap. Field yang kurang ada di missing_fieldsPenjual harus melengkapi data dari dashboard
422idempotency_key_invalidBentuk Idempotency-Key tidak sahPerbaiki permintaan
422idempotency_key_reusedKunci idempotensi dipakai untuk isi berbedaPakai kunci baru
429—Melewati 120 permintaan per menitTunggu sesuai header Retry-After
500internal_errorKegagalan di sisi AdFormCoba lagi dengan kunci idempotensi yang sama

#Jejak di dashboard penjual

Setiap perubahan lewat endpoint ini tercatat di riwayat edit pesanan dengan sumber api dan nama kunci sebagai pelakunya. Penjual bisa melihat bahwa statusnya dipindahkan oleh integrasi, dan integrasi yang mana.

#Kaitan dengan webhook

Perubahan status lewat endpoint ini memicu event order.status_changed seperti perubahan dari dashboard. Payload-nya membawa tiga field tambahan:

JSON
{
  "changed_by": "integration",
  "integration": "CRM Closing",
  "external_reference": "SPC-8871"
}

changed_by bernilai integration di sini. Field ini tidak muncul pada perubahan dari dashboard, sehingga penerima webhook yang sudah berjalan tidak perlu menyesuaikan apa pun. Gunanya: membedakan perubahan yang berasal dari sistem Anda sendiri, supaya dua sistem tidak saling memantulkan status.

Selengkapnya di Webhook Keluar.

#Jangan jadikan webhook satu-satunya sumber kebenaran

Ketiga jalur yang menulis status di AdForm — penjual dari dashboard, konfirmasi pembayaran otomatis, dan endpoint ini — mengantre order.status_changed di dalam transaksi yang sama dengan perubahannya, jadi tidak ada perubahan status yang lolos tanpa event. Tetapi pengantreannya digerbangi kuota kiriman per toko: 3.000 kiriman per jam per toko (WEBHOOK_OUT_HOURLY_CAP).

Kalau kuota itu penuh, perubahan statusnya tetap tersimpan sementara kirimannya tidak pernah masuk antrean. Tidak ada galat, dan tidak ada percobaan ulang — kiriman itu memang tidak pernah dibuat.

Karena itu: pakai webhook sebagai pemberitahuan cepat, dan GET sebagai rekonsiliasi. Toko yang ramai adalah justru toko yang paling mungkin menyentuh kuota, dan juga yang paling merugikan kalau statusnya menyimpang tanpa ketahuan.

#Keamanan

  • Kunci API adalah kredensial. Simpan di penyimpanan rahasia sistem Anda, bukan di kode sumber atau catatan.
  • Jangan pernah meminta atau mengirim kunci lewat WhatsApp, email, atau chat. Penjual menerbitkannya sendiri dari dashboard dan memasukkannya sendiri ke sistem Anda.
  • Kalau kunci diduga bocor, penjual mencabutnya dari dashboard dan menerbitkan yang baru. Pencabutan berlaku seketika.

Ketik minimal dua huruf.

↑↓ pindah Enter buka Esc tutup