Lompat ke konten utama
Dokumentasi AdForm
llms.txt Pusat Bantuan

Konvensi API

Aturan yang berlaku di seluruh endpoint ber-API-key AdForm — base URL, bentuk response sukses dan galat, header, CORS, tanggal, nomor telepon, rupiah, dan kebijakan perubahan.

Halaman ini mengumpulkan aturan yang berlaku sama di semua endpoint AdForm yang bisa dipanggil dengan API key. Kalau satu halaman referensi endpoint tidak menyebut sesuatu, aturan di sini yang berlaku.

Ruang lingkupnya sempit dan kami sebut apa adanya: hari ini ada dua berkas endpoint yang memeriksa API key — /api/products.php (dua operasi, dipilih lewat query action) dan /api/customer_lookup.php (satu operasi, POST saja). Konvensi di bawah ditulis dari perilaku kode yang berjalan di produksi, bukan dari rencana.

Dua pengecualian yang berlaku khusus untuk /api/customer_lookup.php, dan disebutkan lagi di halaman Pencarian Pelanggan:

  • Operasinya tidak dipilih lewat action. Method HTTP-nya yang menentukan, dan hanya POST yang diterima.
  • Batas lajunya dihitung per API key, bukan per alamat IP.

#Base URL

Teks
https://adform.id

Semua endpoint berada di bawah /api/ dan diakses sebagai berkas PHP, misalnya /api/products.php. Tidak ada prefix versi (/v1/, /v2/). Operasi dipilih lewat query parameter, bukan lewat segmen path.

Hanya HTTPS. Tidak ada host terpisah untuk sandbox atau staging yang dibuka untuk pihak ketiga — semua panggilan mengenai data produksi tenant Anda.

#Bentuk request

#Autentikasi

Satu header:

HTTP
X-API-Key: adk_0123456789abcdef0123456789abcdef0123456789abcdef

Tidak ada skema Authorization: Bearer, tidak ada tanda tangan request, tidak ada nonce. Detail format key ada di Autentikasi.

#Parameter

LetakDipakai untuk
Query stringMemilih operasi (action) dan menunjuk objek (id).
Body JSONSemua data yang dikirim (misalnya name, price, variants).

Server membaca body sebagai JSON mentah dari php://input. Konsekuensinya:

  • Body harus JSON yang sah. application/x-www-form-urlencoded dan multipart/form-data tidak dibaca — field-nya akan hilang tanpa pesan galat, dan Anda hanya melihat error "wajib diisi" untuk field yang sebenarnya sudah Anda kirim.
  • Body yang gagal di-parse diperlakukan sebagai objek kosong, bukan sebagai galat tersendiri.
  • Kirim Content-Type: application/json.

Contoh minimum yang benar:

Terminal
curl -sS -X POST \
  -H "X-API-Key: adk_0123456789abcdef0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{"name":"Kaos Polos Hitam - Promo Juli"}' \
  "https://adform.id/api/products.php?action=duplicate&id=12"

#Method HTTP

Gunakan method yang tercantum di halaman referensi tiap operasi (GET untuk baca, POST untuk tulis).

Perilakunya berbeda di dua endpoint, dan bedanya penting:

  • /api/products.php: method tidak diperiksa. Yang menentukan operasi adalah nilai action. Memanggil action=duplicate dengan GET tidak dibalas 405; request itu tetap masuk lalu gagal di validasi body dengan 400 name required. Jangan bergantung pada perilaku ini; ia bisa diperketat kapan saja.
  • /api/customer_lookup.php: method diperiksa keras. Selain POST, semuanya dibalas 405 {"error":"Method not allowed"}. Ini bukan kelonggaran yang bisa berubah — alasannya menjaga nomor telepon supaya tidak pernah masuk query string, dan karena itu tidak akan dilonggarkan.

#Bentuk response

#Sukses

Selalu JSON, selalu objek datar. Tidak ada envelope data, tidak ada meta, tidak ada pagination.

JSON
{
  "success": true,
  "id": 87,
  "slug": "kaos-polos-hitam-promo-juli"
}
  • Field success bernilai true ada di setiap response sukses operasi API key.
  • Status 200 untuk baca, 201 untuk pembuatan objek baru.
  • Field baru bisa ditambahkan sewaktu-waktu. Parser Anda harus mengabaikan field yang tidak dikenal, bukan menolaknya.

#Galat

Selalu JSON dengan satu field:

JSON
{ "error": "Missing scope: products:write" }

Yang perlu Anda ketahui sejak awal:

  • Tidak ada kode galat mesin (code), tidak ada daftar galat per-field, tidak ada request_id. Yang membedakan jenis galat adalah status HTTP ditambah teks error.
  • Teks error bercampur bahasa: sebagian Inggris (berasal dari lapisan autentikasi), sebagian Indonesia (berasal dari validasi). Ini kondisi nyata hari ini.
  • Jangan mencocokkan teks error secara persis untuk logika program. Cabangkan pada status HTTP; pakai teks hanya untuk log dan pesan ke manusia.
  • Field success tidak ikut dikirim pada response galat. Jangan menguji data.success === false; uji status HTTP.

Daftar lengkap ada di Kode Error.

#Header response

Yang dikirim di setiap response:

HeaderNilai
Content-Typeapplication/json; charset=utf-8
X-Content-Type-Optionsnosniff
Cache-Controlno-store, no-cache, must-revalidate, private
Pragmano-cache

Tidak ada ETag, tidak ada Last-Modified, tidak ada dukungan permintaan bersyarat (If-None-Match). Response tidak boleh di-cache oleh perantara.

Header Retry-After hanya muncul pada 429. Tidak ada header sisa kuota — lihat Batas Penggunaan.

#Encoding

Response di-encode dengan JSON_UNESCAPED_UNICODE, jadi karakter non-ASCII muncul apa adanya (Ukuran M, bukan U...). Baca sebagai UTF-8.

#CORS — API key tidak bisa dipanggil dari browser

Ini pembatasan yang paling sering menyita waktu orang, jadi kami tulis eksplisit.

Server mengizinkan header berikut pada request lintas-origin:

Teks
Access-Control-Allow-Headers: Content-Type, X-Admin-Password, Authorization

X-API-Key tidak ada di daftar itu. Artinya browser akan menggagalkan preflight OPTIONS untuk setiap fetch() lintas-origin yang membawa X-API-Key, dan request aslinya tidak pernah terkirim.

Kesimpulannya: panggil API key hanya dari server Anda. Itu memang yang benar dari sisi keamanan — key tidak dibatasi per-domain maupun per-IP, jadi key yang masuk ke kode frontend sama saja dengan key yang dibagikan ke publik.

Untuk konteks, origin yang diperlakukan sebagai tepercaya oleh lapisan CORS adalah domain AdForm sendiri (adform.id, www.adform.id, staging.adform.id, form.adstack.id, subdomain .adform.id dan .adstack.id) serta dua origin pengembangan lokal. Origin lain tetap dilayani, tetapi tanpa kredensial. Request OPTIONS dibalas 200 kosong.

#Tanggal dan zona waktu

Keadaan hari ini, tanpa dibagus-baguskan: dua operasi API key yang tersedia tidak mengembalikan satu pun field tanggal. Jadi belum ada format tanggal response yang perlu Anda parse.

Yang tetap perlu Anda pahami karena memengaruhi perilaku sistem:

  • Waktu disimpan dalam UTC, dan zona waktu default proses PHP adalah UTC.
  • Batas yang berbasis "hari" atau "bulan" dihitung menurut WIB (Asia/Jakarta, UTC+7) lalu dikonversi ke UTC. Misalnya kuota harian paket gratis di-reset pukul 00:00 WIB, yang setara 17:00 UTC hari sebelumnya.
  • Kedaluwarsa API key dievaluasi dengan jam server, bukan jam pemanggil.

Kalau Anda mengirim tanggal (saat ini hanya relevan ketika meminta key dengan masa berlaku), pakai bentuk yang tidak ambigu seperti 2027-01-31 23:59:59 atau ISO 8601 lengkap dengan offset.

#Nomor telepon

AdForm menyimpan nomor Indonesia dalam bentuk kanonik berawalan 62, tanpa +, tanpa spasi, tanpa tanda hubung.

Normalisasi yang dijalankan server:

  1. Semua karakter selain angka dibuang (+, spasi, -, ., kurung).
  2. Kalau hasilnya diawali 0, angka 0 itu diganti 62.
  3. Kalau hasilnya belum diawali 62, 62 ditambahkan di depan.
MasukanTersimpan
0812-3456-78906281234567890
+62 812 3456 78906281234567890
812 3456 78906281234567890

Dua catatan penting supaya tidak salah asumsi:

  • Normalisasi ini tidak memvalidasi panjang. Masukan 1 menghasilkan 621. Kalau Anda punya sumber data sendiri, validasi panjang di sisi Anda sebelum mengirim.
  • Dua operasi API key yang ada sekarang tidak menerima maupun mengembalikan nomor telepon. Konvensi ini berlaku untuk nomor yang masuk lewat form order AdForm dan yang Anda lihat di dashboard.

#Uang dan mata uang

  • Mata uang selalu rupiah (IDR). Tidak ada dukungan multi-mata-uang.
  • Semua nilai uang adalah bilangan bulat rupiah, dikirim dan diterima sebagai angka JSON: 150000 berarti Rp150.000.
  • Tidak ada desimal, tidak ada satuan sen, tidak ada pemisah ribuan, tidak ada simbol mata uang di dalam nilai. "Rp150.000" dan 150000.00 bukan bentuk yang dipakai.
  • Nilai harga yang Anda kirim di-cast ke integer, dan nilai negatif dijadikan 0. -5000 menjadi 0, bukan galat.
  • Harga coret bernilai 0 berarti "tanpa harga coret", bukan "gratis".

#Idempotensi dan pengulangan

Belum ada dukungan idempotency key. Operasi tulis tidak idempoten: memanggil duplikat produk dua kali menghasilkan dua produk.

Karena itu:

  • Jangan mengulang otomatis operasi tulis yang gagal dengan 5xx atau timeout tanpa memeriksa dulu apakah objeknya sudah terbentuk.
  • Yang aman diulang otomatis hanya 429 (setelah menunggu sesuai Retry-After) dan operasi baca.
  • Simpan id dari response sukses di sisi Anda sebagai penanda bahwa operasi sudah selesai.

#Isolasi tenant

Satu API key terikat ke satu tenant. Konteks tenant sepenuhnya ditentukan oleh key — tidak ada parameter tenant yang bisa (atau perlu) Anda kirim pada jalur API key. ID objek bersifat lokal per tenant: id=12 di tenant Anda tidak ada hubungannya dengan id=12 di tenant lain.

Objek yang bukan milik tenant pemilik key dibalas 404, bukan 403.

#Bagaimana perubahan kontrak diumumkan

Karena API ini belum diversikan, kami memakai aturan berikut:

  1. Perubahan aditif bisa terjadi kapan saja tanpa pengumuman. Field baru di response, operasi baru, parameter opsional baru. Integrasi Anda harus tahan terhadap field yang tidak dikenal.
  2. Perubahan yang merusak diumumkan lebih dulu di halaman Changelog situs ini, sebelum berlaku. Termasuk: menghapus atau mengubah nama field, mengubah tipe data, mengubah status HTTP untuk kasus yang sama, memperketat validasi, atau menurunkan batas laju.
  3. Kalau ada di dokumentasi ini, artinya sudah berjalan di produksi hari ini. Kami sengaja tidak menerbitkan kontrak yang belum final — halaman Webhook contohnya: statusnya ditulis "belum ada" alih-alih dikarang.
  4. Belum ada kebijakan deprecation formal dengan tenggat tetap. Kalau integrasi Anda kritis, beri tahu kami lewat Pusat Bantuan supaya Anda masuk daftar pemberitahuan langsung.

Halaman ini sendiri mencantumkan tanggal updated di bagian atas. Kalau berubah, ada yang berubah.

Ketik minimal dua huruf.

↑↓ pindah Enter buka Esc tutup