API Produk
Referensi lengkap dua operasi produk yang bisa dipanggil dengan API key AdForm — ambil snippet embed dan duplikat produk, beserta parameter, contoh, dan daftar error.
/api/products.php
Endpoint /api/products.php melayani dua operasi programatik yang bisa dipanggil dengan API key. Operasi dipilih lewat query parameter action.
| Operasi | Method | URL | Scope |
|---|---|---|---|
| Ambil snippet embed | GET | /api/products.php?action=embed&id={id} | products:read |
| Duplikat produk | POST | /api/products.php?action=duplicate&id={id} | products:write |
action wajib. Tanpa action, request masuk ke jalur dashboard yang butuh sesi login, bukan API key — API key Anda akan diabaikan di jalur itu. action yang tidak dikenal dibalas 400:
{ "error": "Unknown action: list" }Semua harga adalah integer rupiah tanpa desimal (150000 = Rp150.000).
#Ambil snippet embed produk
GET /api/products.php?action=embed&id={id}
X-API-Key: adk_...Scope: products:read.
Mengembalikan harga, varian, dan potongan HTML siap tempel untuk satu produk. Ini cara yang benar untuk mendapatkan URL form dan snippet iframe — jangan merangkainya sendiri, karena bentuk URL bergantung pada tenant_id dan slug produk.
#Parameter
| Nama | Letak | Tipe | Wajib | Keterangan |
|---|---|---|---|---|
action | query | string | ✅ | Harus embed. |
id | query | integer | ✅ | ID produk. Harus > 0. |
#Contoh request
curl -sS \
-H "X-API-Key: adk_0123456789abcdef0123456789abcdef0123456789abcdef" \
"https://adform.id/api/products.php?action=embed&id=12"#Contoh response — 200 OK
{
"success": true,
"id": 12,
"name": "Kaos Polos Hitam",
"slug": "kaos-polos-hitam",
"price": 89000,
"sale_price": 69000,
"variants": [
{ "dimension": "Ukuran", "options": ["M", "L", "XL"] }
],
"variant_prices": [69000, 69000, 79000],
"variant_display": "small",
"embed": {
"product_id": 12,
"tenant_id": 34,
"slug": "kaos-polos-hitam",
"form_url": "https://adform.id/t/34/kaos-polos-hitam",
"iframe": "<iframe src=\"https://adform.id/t/34/kaos-polos-hitam\"\n style=\"width:100%;min-height:700px;border:none;\"\n loading=\"eager\" scrolling=\"no\" frameborder=\"0\"></iframe>",
"iframe_with_pixel": "<iframe src=\"https://adform.id/t/34/kaos-polos-hitam\"\n style=\"width:100%;min-height:700px;border:none;\"\n loading=\"eager\" scrolling=\"no\" frameborder=\"0\"></iframe>\n<script src=\"https://adform.id/embed.js\"></script>"
}
}#Field response
| Field | Tipe | Keterangan |
|---|---|---|
success | boolean | Selalu true pada response sukses. |
id | integer | ID produk. |
name | string | Nama produk. |
slug | string | Slug produk, dipakai di URL form. |
price | integer | Harga dasar (rupiah). |
sale_price | integer | null | Harga coret/promo. null kalau tidak dipakai. |
variants | array | Daftar variasi. Lihat di bawah. |
variant_prices | array | Harga per kombinasi varian. Bentuknya mengikuti variants. |
variant_display | string | Gaya tampilan pilihan varian di form, mis. "small". |
embed.product_id | integer | Sama dengan id. |
embed.tenant_id | integer | ID tenant pemilik key. |
embed.slug | string | Sama dengan slug. |
embed.form_url | string | URL form order produk ini. |
embed.iframe | string | HTML iframe siap tempel. |
embed.iframe_with_pixel | string | Sama seperti iframe, plus <script src=".../embed.js"> untuk auto-resize, penerusan parameter iklan, dan tracking pixel. Ini yang sebaiknya Anda pakai. |
#Bentuk variants dan variant_prices
Produk tanpa varian: variants berupa array kosong dan variant_prices kosong. Harga diambil dari price / sale_price.
Produk dengan satu variasi:
{
"variants": [{ "dimension": "Ukuran", "options": ["M", "L", "XL"] }],
"variant_prices": [69000, 69000, 79000]
}variant_prices adalah daftar angka datar, indeksnya sejajar dengan options.
Produk dengan dua variasi (matriks Variasi 1 × Variasi 2):
{
"variants": [
{ "dimension": "Warna", "options": ["Hitam", "Putih"] },
{ "dimension": "Ukuran", "options": ["M", "L", "XL"] }
],
"variant_prices": [
[69000, 69000, 79000],
[72000, 72000, 82000]
]
}Baris = opsi variasi pertama, kolom = opsi variasi kedua. Jadi Putih/XL = variant_prices[1][2] = 82000.
Produk lama bisa memakai format varian datar tanpa field dimension. Perlakukan keberadaan dimension sebagai penanda format, seperti yang dilakukan form AdForm sendiri.
#Duplikat produk
POST /api/products.php?action=duplicate&id={id}
X-API-Key: adk_...
Content-Type: application/jsonScope: products:write.
Menyalin produk yang sudah ada menjadi produk baru di tenant yang sama, dengan nama baru dan (opsional) harga/varian/gambar yang di-override. Response langsung berisi embed, jadi satu panggilan sudah cukup untuk mendapat produk baru plus snippet siap pasang.
Kirim sebagai POST dengan body JSON — parameter override dibaca dari body, jadi request tanpa body tidak akan berhasil.
#Parameter query
| Nama | Tipe | Wajib | Keterangan |
|---|---|---|---|
action | string | ✅ | Harus duplicate. |
id | integer | ✅ | ID produk sumber yang akan disalin. |
#Parameter body (JSON)
| Nama | Tipe | Wajib | Default | Keterangan |
|---|---|---|---|---|
name | string | ✅ | — | Nama produk baru. Tidak boleh kosong. |
slug | string | ⬜ | diturunkan dari name | Slug produk baru. Kalau kosong, dibuat dari name (huruf kecil, karakter non-alfanumerik jadi -). Kalau slug sudah dipakai, sistem menambahkan -2, -3, dst. |
image_url | string | ⬜ | ikut sumber | Ganti gambar utama produk baru. |
price_mode | string | ⬜ | ikut sumber | "satuan" atau "varian". Menentukan bagaimana harga produk baru dibentuk. Kalau tidak dikirim, seluruh struktur harga disalin apa adanya dari sumber. |
price | integer | ⬜ | ikut sumber | Harga dasar. Nilai negatif dijadikan 0. Dipakai pada price_mode: "satuan", dan sebagai harga dasar pada mode "varian". |
sale_price | integer | null | ⬜ | ikut sumber | Harga coret. Kirim null untuk menghapus. Hanya pada price_mode: "satuan". |
variants | array | ⬜ | ikut sumber | Struktur variasi produk baru. Hanya dipakai pada price_mode: "varian". |
variant_prices | array | ⬜ | ikut sumber | Harga per kombinasi varian. Bentuknya wajib cocok dengan variants (lihat validasi di bawah). |
variant_compare_prices | array | ⬜ | ikut sumber | Harga coret per kombinasi varian. Bentuk sama dengan variant_prices; nilai 0 berarti tanpa coret. |
variant_display | string | ⬜ | ikut sumber | Gaya tampilan pilihan varian, mis. "small". |
Pada price_mode: "satuan", varian produk baru dikosongkan — produk hasilnya jadi produk harga tunggal meskipun sumbernya bervarian.
#Yang ikut tersalin
Selain nama dan slug, produk baru mewarisi konfigurasi produk sumber: deskripsi, berat, metode pembayaran dan channel e-payment, setelan COD, distribusi CS, setelan checkout, order bump, template follow-up, template redirect pelanggan, ID Meta Pixel dan GTM, label form, form_fields, tipe produk (termasuk produk digital), serta penugasan tim (product_team) dari produk sumber.
Status produk baru mengikuti status produk sumber. Kalau sumbernya aktif, hasil duplikat langsung aktif dan formnya bisa diakses publik. Kalau Anda ingin menyiapkan dulu, duplikat dari produk sumber yang nonaktif.
#Contoh — duplikat sederhana
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"#Contoh — jadikan produk harga tunggal
{
"name": "Kaos Polos Hitam - Edisi Promo",
"slug": "kaos-hitam-promo",
"price_mode": "satuan",
"price": 79000,
"sale_price": 59000
}#Contoh — ganti struktur varian
{
"name": "Kaos Polos - Katalog Baru",
"price_mode": "varian",
"price": 69000,
"variants": [
{ "dimension": "Warna", "options": ["Hitam", "Putih"] },
{ "dimension": "Ukuran", "options": ["M", "L"] }
],
"variant_prices": [
[69000, 74000],
[69000, 74000]
],
"variant_compare_prices": [
[89000, 94000],
[89000, 94000]
],
"variant_display": "small"
}#Contoh response — 201 Created
{
"success": true,
"id": 87,
"slug": "kaos-polos-hitam-promo-juli",
"source_id": 12,
"embed": {
"product_id": 87,
"tenant_id": 34,
"slug": "kaos-polos-hitam-promo-juli",
"form_url": "https://adform.id/t/34/kaos-polos-hitam-promo-juli",
"iframe": "<iframe src=\"https://adform.id/t/34/kaos-polos-hitam-promo-juli\"\n style=\"width:100%;min-height:700px;border:none;\"\n loading=\"eager\" scrolling=\"no\" frameborder=\"0\"></iframe>",
"iframe_with_pixel": "<iframe src=\"https://adform.id/t/34/kaos-polos-hitam-promo-juli\"\n style=\"width:100%;min-height:700px;border:none;\"\n loading=\"eager\" scrolling=\"no\" frameborder=\"0\"></iframe>\n<script src=\"https://adform.id/embed.js\"></script>"
}
}Perhatikan slug di response — belum tentu sama dengan yang Anda kirim, karena bisa ditambah akhiran angka bila bentrok. Selalu pakai slug dan embed.form_url dari response, jangan menebak.
#Validasi harga varian
Bentuk variant_prices diperiksa sebelum disimpan supaya form tidak pernah menagih harga yang salah:
- Satu variasi →
variant_pricesharus daftar angka datar, bukan bersarang. - Dua atau tiga variasi →
variant_pricesharus matriks bersarang yang dimensinya persis sama dengan jumlah opsi tiap variasi, dan setiap sel harus diisi angka lebih dari 0. variant_compare_prices, kalau dikirim dan tidak kosong, harus berbentuk sama. Nilai0diperbolehkan (artinya tanpa harga coret).- Setiap variasi minimal punya satu opsi.
Kalau tidak lolos, request dibalas 400 dengan pesan yang menyebutkan masalahnya, misalnya:
{ "error": "variant_prices tiap kombinasi wajib diisi harga lebih dari 0 (ada sel kosong/0)." }atau
{ "error": "variant_prices bentuk matriks tidak sesuai jumlah opsi variasi." }#Daftar error
| Status | Body | Operasi | Penyebab |
|---|---|---|---|
400 | {"error":"id required"} | keduanya | id tidak dikirim atau ≤ 0. |
400 | {"error":"name required"} | duplicate | Body tidak berisi name, atau name kosong. |
400 | {"error":"cannot derive slug from name"} | duplicate | name tidak mengandung karakter yang bisa dijadikan slug (mis. hanya simbol). Kirim slug sendiri. |
400 | {"error":"Unknown action: ..."} | — | Nilai action bukan embed atau duplicate. |
400 | pesan validasi varian | duplicate | Lihat bagian validasi harga varian di atas. |
401 | {"error":"API key required (X-API-Key header)"} | keduanya | Header tidak dikirim. |
401 | {"error":"Malformed API key"} | keduanya | Bentuk key salah. |
401 | {"error":"Invalid API key"} | keduanya | Key tidak dikenal. |
401 | {"error":"API key revoked"} | keduanya | Key sudah dicabut. |
401 | {"error":"API key expired"} | keduanya | Key sudah kedaluwarsa. |
403 | {"error":"Tenant inactive"} | keduanya | Tenant pemilik key nonaktif. |
403 | {"error":"Missing scope: products:read"} | embed | Key tidak punya scope products:read. |
403 | {"error":"Missing scope: products:write"} | duplicate | Key tidak punya scope products:write. |
404 | {"error":"Product not found"} | embed | Produk dengan id tersebut tidak ada di tenant ini. |
404 | {"error":"Source product not found"} | duplicate | Produk sumber tidak ada di tenant ini. |
429 | {"error":"Terlalu banyak permintaan. Coba lagi nanti."} | keduanya | Rate limit 60 request/menit per IP terlampaui. Lihat header Retry-After. |
#Yang tidak tersedia di endpoint ini
Supaya tidak ada waktu terbuang: dengan API key, /api/products.php hanya melayani action=embed dan action=duplicate. Tidak ada list produk, ambil detail lengkap, buat produk dari nol, ubah, atau hapus lewat API key. Operasi itu ada di dashboard dan butuh sesi login dashboard.
Kalau Anda butuh salah satunya untuk integrasi, kabari kami lewat Pusat Bantuan — permintaan konkret dari integrator yang menentukan urutan pengerjaan.