Endpoint `/api/products.php` melayani dua operasi programatik yang bisa dipanggil dengan [API key](/docs/authentication). 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`: ```json { "error": "Unknown action: list" } ``` Semua harga adalah **integer rupiah tanpa desimal** (`150000` = Rp150.000). --- ## Ambil snippet embed produk ```http 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 ```bash curl -sS \ -H "X-API-Key: adk_0123456789abcdef0123456789abcdef0123456789abcdef" \ "https://adform.id/api/products.php?action=embed&id=12" ``` ### Contoh response — `200 OK` ```json { "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_with_pixel": "\n" } } ``` ### 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 `" } } ``` 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_prices` harus daftar angka datar, bukan bersarang. - Dua atau tiga variasi → `variant_prices` harus 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. Nilai `0` diperbolehkan (artinya tanpa harga coret). - Setiap variasi minimal punya satu opsi. Kalau tidak lolos, request dibalas `400` dengan pesan yang menyebutkan masalahnya, misalnya: ```json { "error": "variant_prices tiap kombinasi wajib diisi harga lebih dari 0 (ada sel kosong/0)." } ``` atau ```json { "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](https://adform.id/pusat-bantuan.html) — permintaan konkret dari integrator yang menentukan urutan pengerjaan.