Logo
Daftar Isi

Bab 13 · Developer API

Konvensi Respons & Paginasi

Aturan yang berlaku di semua endpoint: bentuk envelope, snake_case, tanggal UTC, paginasi cursor, sinkronisasi updated_since, dan penanda data terhapus.

Aturan di halaman ini berlaku untuk semua endpoint. Kalau Anda hanya sempat membaca satu halaman teknis, baca yang ini.

Format dasar

  • Semua nama field snake_casesales_order_number, bukan salesOrderNumber.
  • Semua tanggal/waktu ISO 8601 UTC, contoh 2026-08-13T03:42:08.000Z. Selalu berakhiran Z.
  • Semua nilai uang berupa angka Rupiah bulat (integer), tanpa desimal dan tanpa pemisah ribuan. 1350000 berarti Rp 1.350.000.
  • Field yang kosong bernilai null, bukan string kosong.
  • Semua respons JSON, dengan Content-Type: application/json.

Tiga bentuk respons

BentukDipakai olehStruktur
List berpaginasi/sales, /sales/payments, /sales/facets, /sales/{id}/facets, /members, /members/{id}/sales, /members/{id}/point-logs, /warehouse/items, /warehouse/stocks, /warehouse/stock-movements{ "data": [...], "pagination": {...} }
Objek tunggal/sales/{id}, /members/{id}, /warehouse/items/{id}{ "data": { ... } }
List pendek tanpa paginasi/branches, /sales/{id}/items, /sales/{id}/payments, /warehouse/items/{id}/stocks{ "data": [...] }

Satu pengecualian: /ping membalas { "ok": true, "key_name": "..." } tanpa pembungkus data.

Contoh list berpaginasi:

{
  "data": [ { "id": 18234, "...": "..." } ],
  "pagination": {
    "next_cursor": "eyJ1IjoiMjAyNi0wOC0xM1QwNTozMDoxMi4wMDBaIiwiaSI6MTgyMzR9",
    "has_more": true
  }
}

Paginasi dengan cursor

  • limit — jumlah baris per halaman. Default 100, maksimal 500, harus bilangan bulat minimal 1.
  • cursor — penanda posisi, diambil dari pagination.next_cursor halaman sebelumnya.

Polanya:

halaman 1: GET /sales?limit=200
  → has_more = true, next_cursor = "AAA"
halaman 2: GET /sales?limit=200&cursor=AAA
  → has_more = true, next_cursor = "BBB"
halaman 3: GET /sales?limit=200&cursor=BBB
  → has_more = false, next_cursor = null   ← berhenti di sini

Aturan penting:

  • Bawa serta semua filter di setiap halaman. Cursor hanya menyimpan posisi, bukan filter. Kalau halaman 1 memakai updated_since, halaman 2 harus memakainya juga.
  • Cursor bersifat opaque — jangan diurai atau dibuat sendiri, cukup diteruskan kembali. Isinya bisa berubah sewaktu-waktu.
  • Tidak ada paginasi offset (page= / offset=). Cursor aman terhadap data yang bertambah di tengah proses tarik.
  • Cursor tidak punya masa kedaluwarsa, tapi tidak dimaksudkan disimpan lama. Untuk melanjutkan di lain waktu, simpan updated_at terbesar dan mulai lagi dengan updated_since.

Urutan data

EndpointUrutan
Sebagian besar listupdated_at menaik, lalu id menaik
/warehouse/stocksitem_id menaik, lalu branch_id menaik
/warehouse/stock-movementsoccurred_at menaik, lalu type, lalu id sumber

Karena updated_at hanya bergerak maju, baris yang berubah di tengah proses paginasi akan pindah ke belakang — jadi Anda mungkin menerimanya dua kali, tapi tidak akan pernah terlewat. Selama penyimpanan Anda melakukan upsert, ini tidak jadi masalah.

Sinkronisasi inkremental: updated_since

Hampir semua list mendukung updated_since=<ISO datetime>: hanya kirim baris yang berubah sejak waktu tersebut.

  1. Simpan nilai updated_at terbesar yang pernah Anda terima.
  2. Pada polling berikutnya, kirim nilai itu sebagai updated_since.
  3. Upsert hasilnya berdasarkan id — jangan insert saja.
GET /sales?updated_since=2026-08-13T05:30:12.000Z&include_deleted=true&limit=200

Catatan penting:

  • Filternya inklusif (>=). Baris tepat di batas waktu akan terkirim ulang setiap polling. Ini disengaja supaya tidak ada yang terlewat — upsert membuatnya tidak berbahaya.
  • Pola ini otomatis menangkap perubahan pada transaksi lama: pembayaran yang menyusul, faktur yang terbit belakangan, atau pembatalan. Filter berdasarkan tanggal transaksi saja akan melewatkan semua itu.
  • Endpoint yang tidak mendukung updated_since: /warehouse/stocks dan /warehouse/stock-movements. Alasannya dijelaskan di halaman API Gudang.

Data terhapus (tombstone)

  • Setiap objek yang bisa dihapus punya field deleted_atnull untuk data aktif.
  • Secara default data terhapus tidak ikut. Tambahkan include_deleted=true untuk menerimanya.
  • Penghapusan tetap mengubah updated_at, jadi kombinasi updated_since + include_deleted=true mengantarkan penghapusan lewat loop polling biasa.

Kalau Anda menyimpan salinan data, selalu poll dengan include_deleted=true. Tanpa itu, transaksi yang dihapus berhenti terkirim tanpa pemberitahuan, dan salinan Anda diam-diam jadi lebih besar dari kenyataan. Baris dengan deleted_at terisi artinya: hapus, atau tandai batal, di sisi Anda.

Transaksi yang dibatalkan

Berbeda dengan penghapusan: transaksi batal tetap ada dan ikut terkirim secara default (include_canceled=true), dengan status: "CANCELED" dan alasan di canceled_reason. Ini disengaja supaya pembatalan terlihat oleh sistem Anda. Kirim include_canceled=false kalau Anda hanya mau transaksi yang masih berlaku.

Zona waktu

Semua tanggal keluar dalam UTC. WIB = UTC + 7 jam. Jadi "1 Agustus 2026 waktu Indonesia bagian barat" berarti:

transaction_date_from=2026-07-31T17:00:00Z
transaction_date_to=2026-08-01T16:59:59Z

Ini penyebab paling sering angka rekap Anda meleset dibanding laporan di aplikasi, yang memakai waktu lokal. Untuk WITA geser 8 jam, WIT 9 jam.

Tipe dan validasi parameter

JenisContohAturanError kalau salah
Bilangan bulatbranch_id, item_id, category_id, brand_idbulat, minimal 0400 invalid_parameter
limitlimit=200bulat, 1–500400 invalid_limit
Tanggalupdated_since, paid_fromISO 8601, sebaiknya berakhiran Z400 invalid_date
Booleaninclude_deletedaktif hanya bila persis true— (nilai lain dianggap false)
include_canceledinclude_canceled=falsenonaktif hanya bila persis false— (default true)
Enumtype, statusharus salah satu nilai yang diizinkan400 invalid_parameter
cursordari next_cursoropaque400 invalid_cursor
{id} di path/sales/18234bulat, minimal 1400 invalid_id

Parameter yang tidak dikenal diabaikan diam-diam — salah ketik nama parameter tidak menimbulkan error, tapi juga tidak menyaring apa pun. Periksa lagi kalau hasil filter terasa aneh.

Janji kompatibilitas

Selama masih v1:

  • Field baru bisa muncul kapan saja tanpa pemberitahuan. Parser Anda harus mengabaikan field yang tidak dikenal.
  • Field yang sudah ada tidak akan diganti nama, dihapus, atau berubah tipe.
  • Perubahan yang merusak kompatibilitas akan terbit sebagai /api/v2, dan /api/v1 tetap jalan.

Ringkasan kode status

HTTPArti
200Berhasil
400Parameter tidak valid
401Masalah API key
404Objek tidak ditemukan atau sudah dihapus
405Metode selain GET
429Melebihi batas pemakaian
500Kesalahan server — coba ulang dengan jeda bertingkat

Rincian setiap error.code ada di halaman Kode Error & Pemecahan Masalah.

Solusi lengkap untuk toko optik modern dengan teknologi terdepan dan dukungan terbaik.

Fitur

  • Penjualan Optik
  • Faset & QC
  • Inventory & Gudang
  • Membership & After-Service
  • Multi-Cabang & Roles
  • Financial Statement

Solusi

Perusahaan

©Lensiro. All Rights Reserved.

Kebijakan Privasi