Logistivo CLI: jalankan operasi logistik Anda dari terminal dan dari ERP Anda

Satu katalog perintah melayani tiga antarmuka: klien terminal lg, asisten AI di dalam dashboard, dan kanal yang digunakan asisten AI eksternal untuk terhubung. Sebuah kemampuan ditulis sekali dan muncul di ketiganya — dan kini setiap tindakan tulis di panel memiliki perintah, sehingga skrip Anda dan agen AI Anda sendiri dapat mengerjakan pekerjaan yang dilakukan tim Anda di layar. Pembayaran, persetujuan hukum, dan penutupan akun sengaja tetap berada di browser. Halaman ini adalah sebuah kontrak. Tabel perintah di bawah tidak ditulis tangan; tabel itu dibuat dari registri perintah yang aktif pada setiap permintaan.

Apa itu Logistivo CLI?

Logistivo CLI (lg) adalah klien baris perintah yang menjalankan pekerjaan akun Logistivo Anda — muatan dan perjalanan, permintaan angkutan, penawaran dan tender, armada dan pengemudi, dokumen, kontak dan rekening berjalan, faktur dan akuntansi, gudang dan stok, anggota tim, serta tarif bea cukai — dari terminal, skrip, atau agen AI Anda sendiri. Perintah-perintahnya tidak tertanam di dalam klien: klien mengambil katalog perintah dari server dan membangun tampilannya sendiri dari katalog itu. Perintah yang sama dapat dipanggil melalui HTTP tanpa memasang apa pun — itulah jalur yang digunakan integrasi ERP dan CRM — dan melalui kanal MCP yang digunakan asisten AI eksternal untuk terhubung.

Apa yang sudah aktif hari ini, dan apa yang sedang disiapkan

Bagian ini sengaja diletakkan di bagian atas halaman. Kesalahan paling mahal yang dapat dibuat oleh sebuah dokumen integrasi adalah mendeskripsikan endpoint yang belum dibuka seolah-olah sudah dibuka: integrator menulis kodenya, endpoint mengembalikan 404, dan kepercayaan tidak pernah kembali.

Karena kontraknya dibekukan, kode integrasi yang Anda tulis hari ini tetap berfungsi saat bagian-bagian yang tersisa dibuka. Field yang tidak Anda kenal harus diabaikan; aturan itulah yang memungkinkan kami menambahkan hal baru tanpa merusak yang lama.

Apa yang membuatnya berbeda: klien tidak mengenal satu pun perintah, klien menampilkannya dari katalog

Inilah satu keputusan yang membuat seluruh isi halaman ini bermakna. Pada CLI biasa, perintah berada di dalam klien; di Logistivo, perintah berada di server.

Dalam susunan klasik, ketika server memperoleh kemampuan baru, klien memerlukan rilis baru dan pengguna perlu memasangnya. Di antara kedua momen itu, kedua sisi mengetahui hal yang berbeda — dan itulah mode kegagalan paling senyap dalam integrasi: skrip berjalan, kode keluarnya 0, dan perintah itu begitu saja tidak menggambarkan apa yang dapat dilakukan server.

Di Logistivo, definisi sebuah perintah adalah satu rekaman: namanya, apa yang dilakukannya, parameter JSON Schema-nya, peran mana yang boleh melihatnya, apakah perintah itu memerlukan konfirmasi, dan apakah efeknya dapat dibatalkan. Saat lg dijalankan, ia mengambil katalog tersebut dan menghasilkan teks bantuan, flag, serta validasi input-nya dari katalog itu. Begitu sebuah perintah ditambahkan di server, lg langsung mengenalnya — tanpa pembaruan klien.

Katalog yang sama melayani asisten AI di dashboard dan kanal yang digunakan asisten AI eksternal untuk terhubung. Sebuah kemampuan ditulis sekali dan muncul di tiga tempat — dan ketiganya melewati gerbang peran yang sama, gerbang konfirmasi yang sama, dan log audit yang sama. Tidak ada inventaris perintah CLI yang terpisah; jika ada, ketiga antarmuka itu akan diam-diam saling menyimpang dalam enam bulan.

Aturan penamaan — bagaimana nama perintah bertambah

Katalog akan terus bertambah. Perintah baru tidak diberi nama secara asal; ada enam aturan yang diterapkan. Kami memublikasikannya di sini karena inilah nama-nama yang akan ditulis oleh integrasi Anda selama bertahun-tahun, dan keterdugaannya bernilai bagi Anda.

Cakupan: semua yang ada di panel, dari sebuah perintah

Banyak tim menghubungkan Logistivo ke agen AI atau harness otomasi milik mereka sendiri, dan sebuah agen hanya berguna jika dapat menyelesaikan pekerjaannya. Karena itu, aturannya sederhana: apa pun yang dapat diubah oleh pengguna kantor di panel, dapat diubah juga oleh sebuah perintah — di balik gerbang peran yang sama, gerbang konfirmasi yang sama, dan log audit yang sama.

Hal ini diukur, bukan sekadar diklaim. Setiap endpoint tulis di balik panel diperiksa terhadap registri perintah, dan sebuah endpoint baru dihitung tercakup jika ada perintah yang menyatakannya sebagai cerminan HTTP-nya atau memanggil layanan bersama yang sama. Dari endpoint tulis yang termasuk dalam cakupan, 100% sudah tercakup dan tidak ada satu pun yang belum terbukti. Setiap endpoint yang tidak disertakan memiliki alasan tertulis — langkah identitas dan pembayaran, control plane integrasi, endpoint milik aplikasi pengemudi sendiri, telemetri klien, salinan mati tanpa pemanggil — dan endpoint yang penting bagi integrator tercantum di bawah ini.

Perintah yang menerima file tunduk pada aturan yang sama: 33 perintah di tabel menerima dokumen atau foto, dan semuanya berfungsi dari lg, melalui HTTP, dan melalui MCP (lihat Perintah yang menerima file).

Pekerjaan manajemen juga tercakup, di balik kunci yang lebih kuat: 62 perintah di tabel — anggota tim dan peran, pengaturan perusahaan, rekening bank, kartu tersimpan, kunci integrasi — memerlukan kunci integrasi tingkat manager. Kunci tingkat employee, yang merupakan bawaan, menjalankan operasi sehari-hari dan ditolak untuk perintah-perintah tersebut dengan INSUFFICIENT_KEY_LEVEL.

Akun afiliasi (mitra rujukan) memiliki katalognya sendiri: 55 perintah di 20 domain — rujukan, prospek (lead), catatan dan tugas CRM, materi presentasi penjualan, pendapatan dan pembayaran, ditambah perintah akun dan tim yang dimiliki setiap peran. Perintah khusus afiliasi tidak ada di tabel publik di bawah, yang mencantumkan tiga peran panel; akun afiliasi melihatnya di GET /api/common/commands dan di lg commands. Perusahaan yang sekaligus merupakan afiliasi dan pengirim, pengangkut, atau agen kepabeanan melihat kedua set tersebut.

Sebuah agen sebaiknya memulai dengan lg users get (nama API get_current_user): perintah ini mengembalikan peran, tingkat akses kunci yang sedang digunakan, izin yang efektif, dan apakah ada persetujuan hukum yang tertunda atau nomor telepon yang belum diverifikasi yang saat ini memblokir penulisan — sehingga agen dapat memprediksi penolakan alih-alih mengumpulkannya.

Cakupan diukur oleh sebuah laporan yang membandingkan registri perintah dengan endpoint tulis panel; endpoint panel yang ditambahkan tanpa perintah akan muncul di laporan itu sebagai belum terbukti. Jika Anda menemukan tindakan di panel yang tidak dapat Anda jangkau dari sebuah perintah, laporkan kepada tim dukungan.

Instalasi dan identitas

lg adalah skrip Node satu file tanpa dependensi (Node 18+), dipublikasikan di npm sebagai @lgstv/lg. Paket ini memasang dua perintah yang identik, lg dan logistivo — gunakan logistivo jika lg sudah menjadi alias di mesin Anda. Karena hanya satu file, menyalin lg.mjs ke server ERP pelanggan juga bisa dilakukan. Anda tidak memerlukan klien ini untuk membangun integrasi ERP/CRM — perintah yang sama tersedia melalui HTTP.

1. Instalasi — Yang Anda butuhkan hanyalah Node 18 atau yang lebih baru; tidak ada dependensi lain. Pasang secara global atau jalankan tanpa memasang melalui npx.

2. Masuk dengan kode persetujuan — lg login menampilkan kode persetujuan yang pendek dan membuka logistivo.com/connect di browser Anda; melalui SSH, di CI, atau dengan --no-browser, perintah ini hanya mencetak alamatnya, yang dapat Anda buka di perangkat apa pun, termasuk ponsel Anda. Masuklah di sana seperti biasa, periksa bahwa kode di halaman itu sama dengan kode di terminal Anda, pilih akses hanya-baca atau baca-dan-bertindak, lalu setujui. Tidak ada hal rahasia yang diketik di terminal: tidak ada kata sandi, tidak ada kunci, tidak ada kode sekali pakai. Setelah itu sesi tetap dalam keadaan masuk di komputer tersebut — menutup terminal tidak membuat Anda keluar, dan lg memperbarui access token dengan sendirinya sebelum kedaluwarsa. lg logout juga mengakhiri sesi di server, dan Anda dapat menghapusnya di panel melalui Settings › Integration keys › AI and CLI connections. lg login --password (email dan kata sandi) tetap tersedia sebagai cadangan. Memerlukan lg 0.11 atau yang lebih baru.

3. Untuk server dan CI: kunci, bukan kode persetujuan — Server atau job CI tidak memiliki siapa pun yang dapat menyetujui kode di browser. Terbitkan kunci di panel (Settings › Integration keys) atau dengan lg keys create — secara opsional atas nama anggota tim khusus dengan cakupan sempit — lalu taruh di LOGISTIVO_TOKEN, atau simpan dengan lg login --api-key. Selama variabel itu diset, file konfigurasi tidak pernah dibaca. Detail: bagian "Terhubung dengan kunci API".

4. Verifikasi dan jelajahi katalog — lg commands menampilkan setiap perintah yang dapat dilihat oleh peran Anda, dikelompokkan per domain; lg help mencetak verb dari domain tersebut, dan lg help mencetak daftar parameter lengkap satu perintah beserta tipenya dan mana yang wajib. Tidak satu pun teks itu berada di dalam klien — semuanya berasal dari katalog. Ingat bahwa daftar ini bergantung pada peran Anda: verb dari satu domain tidak sama untuk pengangkut dan untuk pengirim (loads set-status, misalnya, hanya ada di katalog pengangkut). Jika sebuah verb tidak ada, periksa peran Anda sebelum memeriksa instalasi Anda.

Jangan pernah menaruh kunci di version control, log CI, atau jendela chat. Jika Anda masuk dengan kode persetujuan atau kunci integrasi, lg logout juga mengakhirinya di server; pada sesi kata sandi (lg login --password), perintah itu hanya menghapus salinan LOKAL dan menyatakannya secara eksplisit. Log audit menyimpan argumen Anda, tetapi menyamarkan setiap field yang namanya mengandung token, password, IBAN, card, atau OTP sebagai ***, serta setiap parameter yang dinyatakan rahasia oleh sebuah perintah.

Ergonomi klien

Fitur-fitur ini memudahkan penggunaan klien `lg` sehari-hari. Tidak satu pun yang mengubah kontrak HTTP — lewati bagian ini jika Anda berintegrasi langsung melalui HTTP.

Terhubung dengan kunci API

ERP, CRM, skrip, atau agen AI terhubung ke Logistivo dengan kunci integrasi yang diterbitkan di panel atau dengan lg keys create. Kunci bertindak atas nama anggota tim yang nyata: peran orang itu menentukan daftar perintah, tingkat akses kunci menentukan seberapa berat pekerjaan yang boleh dilakukan, dan ketika orang itu keluar dari tim, kuncinya ikut mati bersamanya. Tidak ada "akun layanan" sintetis dan tidak akan pernah ada — isolasi multi-tenant terikat pada pengguna, dan jembatan tanpa pemilik adalah jenis jembatan yang diam-diam terus menulis selama bertahun-tahun.

1. Terbitkan kunci di panel — Settings › Integration keys › New key. Beri nama ("ERP nightly sync"), pilih scope dan tingkat aksesnya, dan secara opsional persingkat masa berlakunya. Kunci ditampilkan satu kali, hanya di layar itu; server tidak menyimpannya. Jika Anda kehilangannya, Rotate akan menerbitkan kunci baru — kunci lama langsung menjadi tidak valid.

2. Hubungkan dari terminal — lg login --api-key memvalidasi kunci, mencetak identitas dan scope, lalu mengunduh katalog perintah. lg whoami menanyakan kepada server sebagai pengguna, perusahaan, peran, dan scope apa Anda sedang berjalan. lg logout juga mencabut kunci di server, sama seperti perintah itu mengakhiri sesi kode persetujuan di sana (hanya lg logout pada sesi kata sandi yang tidak dapat melakukan itu).

3. Atau HTTP biasa — Kunci adalah access token yang dibawa dalam header Authorization: Bearer dan berfungsi langsung terhadap ketiga endpoint perintah. Di server atau di CI, taruh kunci itu di LOGISTIVO_TOKEN; selama variabel itu diset, file konfigurasi tidak pernah dibaca.

4. Atau kelola kunci dari terminal: lg keys — lg keys adalah kembaran terminal dari layar panel: endpoint yang sama, gerbang yang sama (izin pengelolaan tim, dan kunci tingkat manager jika Anda memanggilnya dengan sebuah kunci). create, rotate, dan transfer mengembalikan token baru satu kali: dengan --save-profile=NAME, token langsung masuk ke profil lokal dan tidak pernah dicetak; jika tidak, lg mencetaknya satu kali. Dalam mode --json, token hanya disertakan jika Anda menambahkan --show-secret, dan panggilan --json tanpa salah satu dari kedua flag itu ditolak sebelum permintaan dikirim, sehingga token tidak dapat diam-diam masuk ke output skrip. rotate, transfer, dan revoke meminta konfirmasi (--yes); sebuah kunci dirujuk dengan nama persisnya atau uuid-nya, tidak pernah dengan tebakan. Tingkat akses terbawa tanpa perubahan ke kunci yang dirotasi atau ditransfer.

Scope ditegakkan di seluruh permukaan API, tidak hanya di katalog perintah: kunci read-only meloloskan permintaan baca (GET/HEAD/OPTIONS) dan setiap permintaan tulis lainnya menjawab 403 forbidden. Ada dua pengecualian yang disengaja dan keduanya dijaga oleh gerbang yang lebih halus: endpoint eksekusi perintah (keputusan diambil dari penanda read_only milik perintah itu sendiri, karena perintah yang tidak menulis juga dipanggil dengan POST) dan kunci yang mencabut dirinya sendiri (lg logout, juga diizinkan pada kunci read-only). Scope, pemberian akses domain, dan tingkat akses adalah tiga sumbu yang independen dan semuanya harus lolos: kunci read tidak dapat menulis bahkan pada tingkat manager, kunci write tingkat employee tidak dapat mengelola tim, dan pemberian akses domain loads:write tidak membuka pekerjaan manajemen. Terbitkan kunci atas nama anggota dengan hak paling sedikit yang mencukupi kebutuhan Anda: peran menentukan rekaman mana yang dapat Anda lihat sama sekali.

Sepuluh menit pertama: dari panel ke panggilan pertama Anda

Menit-menit paling mahal dalam sebuah integrasi adalah sepuluh menit pertama. Empat langkah di bawah ini menguraikan jalur tersebut secara berurutan; urutan yang sama tersedia di panel sebagai wizard (Settings › Integration › Setup wizard), dan wizard itu memanggil persis endpoint yang dijelaskan di sini. Pengembang yang tidak memiliki akses ke wizard mencapai tempat yang sama dengan membaca bagian ini.

Tidak satu pun dari keempat langkah itu berakhir dengan "saya sudah mengirimnya". Apakah kunci benar-benar berfungsi dijawab oleh ledger permintaan; apakah webhook benar-benar sampai dijawab oleh ledger pengiriman. Keduanya terbuka di panel dan di lg. Penyiapan yang tidak pernah diukur adalah penyiapan yang hanya dipercaya berfungsi.

Sandbox: berlatih menulis tanpa menyentuh produksi

Preflight (dry_run) memberi tahu Anda apa yang AKAN dilakukan sebuah perintah; tetapi apakah idempotency benar-benar memutar ulang, apakah webhook benar-benar terkirim, seperti apa cabang-cabang error-nya, dan dari mana kredit diambil hanya dapat dilihat dalam penulisan SUNGGUHAN. Untuk itulah sandbox ada: tenant terpisah yang mencerminkan perusahaan Anda — kode yang sama, katalog perintah yang sama — dan tidak pernah menyentuh ledger live Anda.

Sandbox bukan penanda permintaan, melainkan sebuah TENANT. Perusahaan Logistivo terpisah yang dibuka untuk perusahaan Anda: dengan pengguna sendiri, paket sendiri, ledger kredit sendiri, dan langganan webhook sendiri. Lingkungan DITURUNKAN dari perusahaan pemilik kunci dan tidak dapat diberikan oleh pemanggil, sehingga meskipun Anda memilih profil yang salah, permintaan akan berhenti di sisi server dengan 403 ENVIRONMENT_MISMATCH. "Saya kira saya menulis ke sandbox, ternyata menulis ke produksi" secara struktural tidak mungkin terjadi.

Permukaannya identik — terukur: katalog yang diperoleh kunci sandbox sama dengan yang diperoleh kunci live Anda (pada kunci pengirim, 448 perintah di kedua sisi). Jembatan yang berfungsi di sandbox juga berfungsi di produksi; tidak ada yang perlu Anda pelajari dua kali.

Sandbox tidak dibuka dalam keadaan kosong: sandbox berisi data awal yang dihasilkan (BUKAN disalin) — kontak, muatan di kedua arah, dan permintaan angkutan. Nama-namanya terang-terangan rekaan dan alamat e-mail-nya berada di domain @sandbox.invalid; tidak ada baris milik tenant sungguhan yang pernah dibaca, dikloning, atau dipakai sebagai templat.

Sengaja TIDAK ada tombol reset, dan tidak ada rekaman, file, atau dokumen yang pernah dihapus. Jika Anda menginginkan awal yang bersih, pembaruan membuka generasi baru dan generasi lama tetap ada dalam keadaan pensiun. Beberapa ratus baris latihan yang menumpuk tidak ada artinya dibandingkan rutinitas penghapusan yang tersambung ke tenant yang salah.

Model perintah

Hanya ada satu tata bahasa dan tidak ada pengecualian. Untuk mengetahui cara memanggil sebuah perintah, satu-satunya yang Anda butuhkan adalah apa yang dikatakan katalog tentang perintah itu.

lg loads list --status=in_transport --limit=20 --json

Nama flag adalah nama parameter dalam schema, dan lg menerima kedua ejaan: --load_code dan --load-code mencapai field yang sama (lg help mencetak bentuk kebab-case). Jadi, dengan melihat sebuah flag Anda langsung tahu ke field JSON mana nilainya masuk, dan berpindah dari CLI ke HTTP tidak memerlukan penerjemahan nama. Parameter boolean yang ditulis tanpa nilai bernilai true (--only_open); kebalikannya adalah --no-only_open. Tulis argumen yang nilainya diawali tanda hubung sebagai --flag=value; jika tidak, flag yang lupa diberi nilai akan diam-diam menelan flag berikutnya sebagai nilainya. Pada perintah yang menerima file, tambahkan @ di depan path lokal (--attachment_uuid=@./cmr.pdf) — lihat Perintah yang menerima file.

Contoh nyata

Setiap perintah di bawah ini ada di katalog hari ini; tidak ada nama atau parameter yang dikarang di sini. Output dipersingkat. Output menampilkan tingkat `data.result`; melalui HTTP, objek yang sama tiba di dalam envelope `{success, data:{…}}` (lihat API perintah HTTP).

Tanpa klien: API perintah HTTP

lg adalah kemudahan, bukan satu-satunya pintu. Katalog dan eksekusi sama-sama melewati tiga endpoint HTTP (untuk file ada endpoint keempat, yaitu endpoint unggah — lihat Perintah yang menerima file); integrasi ERP atau CRM terhubung langsung ke sini. Identitas berupa personal access token yang dibawa dalam Authorization: Bearer. SETIAP respons kembali dalam envelope yang sama: `{ "success": bool, "data": { … }, "code": "…", "message": { … } }`. Seluruh kontrak berada di dalam `data` — `contract_version`, `command`, `status` dan, sebagai output perintah, `result`; jika gagal, `error_code`, `error_key`, dan `error`. Contoh respons di halaman ini menampilkan objek `data`, bukan envelope-nya, untuk menghemat tempat; jangan lupa membuka `data` di kode Anda sendiri. `lg --json` juga mencetak envelope SECARA UTUH, itulah sebabnya path jq diawali dengan `.data.result…`. Satu-satunya pengecualian adalah `/api/public/status`: endpoint itu tidak memakai envelope agar watchdog dapat memberi peringatan tanpa perlu mem-parsing-nya.

Meminta konfirmasi dengan 200 + ok:false dilarang dan tidak akan pernah terjadi. Alasannya sederhana: klien menganggap 200 sebagai sukses, sehingga permintaan konfirmasi yang dikembalikan sebagai 200 akan mematikan alur secara diam-diam dan tidak ada yang menyadarinya. Konfirmasi selalu 409.

Perintah yang menerima file

33 perintah di tabel menerima dokumen atau foto: melampirkan CMR ke sebuah muatan, membaca kode HS dari faktur, membuat kendaraan dari dokumen registrasinya, memperkirakan palet dari foto, mengunggah dokumen perusahaan, mencocokkan faktur dengan packing list. Di browser, Anda cukup menjatuhkan file ke dalam formulir; di tempat lain, prosesnya dua langkah — unggah file, lalu teruskan id-nya ke perintah.

Sebuah parameter menerima file jika namanya diakhiri dengan attachment_uuid atau attachment_uuids — photos[].attachment_uuid di dalam daftar, front_attachment_uuid pada cek. Anda tidak perlu mengingatnya: setiap baris katalog membawa file_params dengan path yang persis, schema menandai field tersebut (format: uuid, x-logistivo-file: true), dan respons katalog membawa blok attachments dengan alamat unggah, tipe yang diterima, batas ukuran, dan masa berlaku. lg help juga menampilkannya.

Mengunggah adalah penulisan, sehingga kunci dengan scope baca tidak dapat mengunggah; setiap kunci dengan scope tulis dapat melakukannya, pada tingkat akses mana pun. Unggahan adalah milik pengguna, bukan milik kunci: dua kunci dari pengguna yang sama melihat unggahan yang sama, dan tidak ada yang dapat menggunakan unggahan milik orang lain. Id yang tidak dikenal, sudah kedaluwarsa, atau bukan milik Anda dianggap tidak ditemukan, dan alasannya tidak diungkapkan.

Lampiran chat dashboard dan unggahan perintah adalah ruang yang terpisah: file yang dilampirkan di chat asisten tidak dapat diteruskan ke perintah berdasarkan id, dan unggahan perintah tidak pernah muncul di thread chat.

Unggahan bukanlah perintah dan tidak memiliki efek bisnis dengan sendirinya: tidak ada yang terjadi pada data Anda sampai sebuah perintah menggunakan id tersebut. Itulah juga sebabnya mengunggah tidak pernah meminta konfirmasi — perintah yang menggunakan file itulah yang membawa gerbangnya.

Gerbang konfirmasi dan perintah yang tidak dapat dibatalkan

Setiap perintah di katalog membawa dua penanda: confirmation_needed (apakah perintah itu memerlukan persetujuan eksplisit sebelum berjalan) dan irreversible (apakah efeknya dapat dibatalkan). Keduanya adalah pertanyaan yang terpisah, dan keduanya ditegakkan di server, bukan diserahkan pada sopan santun klien.

Gerbang ini menyangkut tanggung jawab, bukan aturan bisnis. Mengajukan penawaran adalah langkah pertama menuju kontrak angkutan; menerbitkan faktur menciptakan entri akuntansi; pergerakan stok mengubah sebuah buku besar. Tidak satu pun dari hal-hal ini boleh menjadi sesuatu yang dapat Anda jalankan dua kali secara tidak sengaja.

Gerbang dibaca dari perintah yang EFEKTIF. Ketika satu perintah membungkus perintah lain — seperti yang dilakukan executor generik di chat — gerbang diambil dari penanda milik perintah yang benar-benar akan berjalan, bukan dari pembungkusnya. Dengan penanda yang tetap, salah satu dari dua kegagalan tidak akan terhindarkan: perintah faktur berjalan tanpa konfirmasi, atau setiap pencarian negara memunculkan kartu konfirmasi.

read_only dan tanpa gerbang bukanlah hal yang sama. Ada tiga keadaan yang berbeda: invoices preview tidak dijaga gerbang dan tidak menulis apa pun; loads create dijaga gerbang dan menulis; invoices create tidak dijaga gerbang tetapi menulis — sebuah draf tercipta. Satu uji menentukannya: ketika perintah selesai, apakah ada baris di database yang berubah?

Beberapa tindakan tidak berada di balik gerbang ini karena memang sama sekali bukan perintah: membeli paket, menambahkan kartu dengan 3D Secure, menyetujui teks hukum, dan menutup akun terjadi di browser milik orang itu sendiri. Untuk tindakan-tindakan tersebut, lg handoff-links generate mengembalikan tautan logistivo.com — gratis, tidak menulis apa pun, tidak membawa secret — dan orang itu menyelesaikan langkahnya di sana. Tidak ada flag, bagaimanapun disetelnya, yang mengubah pembayaran atau persetujuan menjadi panggilan API.

Secara teknis, sebuah integrasi dapat melewati gerbang secara otomatis pada setiap panggilan, tetapi hal itu tidak disarankan. Pola yang benar adalah mengotomatiskan gerbang hanya untuk perintah yang Anda ketahui dapat dibatalkan, dan mengarahkan perintah yang tidak dapat dibatalkan ke antrean manusia.

Preflight: lihat apa yang akan dilakukan perintah tulis, tanpa menjalankannya

Langkah paling mahal dalam menyambungkan jembatan ERP adalah mencari tahu apakah body Anda sudah benar dengan BENAR-BENAR menjalankan perintahnya: draf faktur di buku besar yang aktif, kredit yang terbakar, pembersihan manual. dry_run mengakhiri hal itu — Anda menambahkan satu field ke permintaan, perintah tidak dijalankan, dan Anda membaca apa yang akan terjadi.

Registri saat ini memuat 519 perintah; 233 di antaranya hanya membaca dan tidak menulis apa pun. Dari 286 perintah tulis sisanya, 251 dijaga gerbang: jika dipanggil tanpa confirm, perintah-perintah itu sudah menjawab 409 dan menjelaskan apa yang akan dilakukannya. Celahnya ada pada 35 sisanya — draf faktur, draf dokumen ekspor, baris item: tepat rantai yang pertama kali disambungkan oleh sebuah jembatan. Untuk perintah-perintah itu, memanggil berarti mengeksekusi.

Ada celah kedua bahkan pada perintah yang dijaga gerbang: validasi schema berjalan SETELAH gerbang. Jadi, panggilan dengan argumen yang kurang mendapat kartu konfirmasi lebih dulu, mengonfirmasinya, dan baru kemudian mendengar "field ini tidak ada". Preflight menjawab keduanya dalam satu kali bolak-balik.

Dan bug jembatan yang paling umum — sekaligus yang paling terlambat ditemukan — adalah ini: Anda mengirim field yang tidak ada dalam schema (customer_id padahal yang diharapkan recipient_contact_id), server diam-diam membuangnya, perintah mengembalikan 200, dan rekaman berakhir di tempat yang salah. Respons preflight mencantumkan field-field itu berdasarkan nama di bawah arguments.ignored; tidak ada pesan error yang akan pernah melakukannya.

Preflight tidak menulis apa pun, dan hal itu bersifat struktural sebelum menjadi hasil pengukuran: jalur preflight tidak pernah masuk ke handler perintah, sehingga berlaku untuk setiap perintah, termasuk yang ditambahkan kemudian. Hal ini juga diukur saat fitur dirilis: setiap pernyataan SQL yang dijalankan selama preflight terhadap setiap perintah tulis di katalog saat itu (dua set argumen, dua peran, 178 preflight) direkam. Tidak ada satu pun INSERT/UPDATE/DELETE, tidak ada satu pun job dalam antrean, tidak ada satu pun email; jumlah baris di 31 tabel dan saldo kredit tidak berubah.

Kode keluarnya sama dengan yang akan dihasilkan oleh panggilan sungguhan — akan berjalan 0, gerbang konfirmasi 4, konflik idempotency 2, selain itu 1 — sehingga "lg … --dry-run || exit" merupakan gerbang yang bermakna dalam langkah CI.

Idempotency: mengapa mencoba ulang tidak menghasilkan duplikat

ERP tidak membaca pesan error Anda, tidak melihat layar Anda, dan akan mengirim ulang permintaan yang sama setelah timeout. Bagian ini ada justru untuk perilaku tersebut.

Panggilan yang membawa idempotency_key dijalankan paling banyak satu kali. Panggilan kedua tidak mengeksekusi ulang perintah; panggilan itu mengembalikan hasil yang tercatat dengan status replayed. Ketika jaringan terputus, ketika permintaan mengalami timeout, atau ketika antrean mengambil job yang sama dua kali, Anda mendapatkan hasil pertama alih-alih faktur ganda.

Gunakan kunci pada setiap perintah tulis. Field read_only di katalog memberi Anda pembedaan itu secara cuma-cuma: setiap perintah dengan read_only: false layak mendapat idempotency key.

Strategi percobaan ulang Anda sebaiknya menggunakan exponential backoff dan membawa kunci yang SAMA pada setiap percobaan. Membuat kunci baru untuk setiap percobaan sama saja dengan tidak menggunakan idempotency sama sekali. Jangan mengirim kunci yang sama secara paralel: at-most-once (paling banyak satu kali) dijamin untuk percobaan ulang yang berurutan, bukan untuk dua permintaan yang saling berlomba.

Sinkronisasi inkremental dan paginasi

Jembatan ERP/CRM sebaiknya mengambil apa yang berubah, bukan mengambil semuanya setiap jam. Setiap perintah daftar — loads list, demands list-own, contacts list, invoices list, teammates list, load-groups list, reservations list, drivers list, tenders list, products search, warehouses search — menerima dua parameter bersama dan mengembalikan satu field bersama.

Mengapa tidak ada offset: pada tabel yang aktif, offset bergeser. Baris yang disisipkan di antara dua halaman membuat Anda melewatkan atau menghitung ganda sebuah rekaman, secara diam-diam. Cursor hanya bergerak maju, sehingga rekaman yang disisipkan atau diperbarui di tengah penelusuran akan muncul di akhir daftar: tidak ada yang hilang, tidak ada yang terduplikasi.

Muatan awal yang lengkap memakai mekanisme yang sama: mulai dengan updated_since=1970-01-01T00:00:00Z dan ikuti next_cursor hingga nilainya null. Tidak ada endpoint “ekspor” tersendiri karena memang tidak diperlukan.

Hal yang sama dengan lg: lg loads list --updated_since=2026-09-01T00:00:00Z --limit=100 --json, lalu lg loads list --cursor= --json.

Untuk sinkronisasi berikutnya, setel updated_since ke nilai updated_at terbesar yang Anda lihat. Timestamp memiliki presisi satu detik, sehingga rekaman yang tepat berada di batas dapat datang dua kali — tidak berbahaya selama Anda melakukan upsert berdasarkan external_ref atau kode di sisi Anda. Tanpa updated_since atau cursor, perintah berjalan dalam mode chat: urutan untuk manusia, paling banyak 20 baris, next_cursor bernilai null, dan truncated yang menyatakan “masih ada lagi”. Jangan gunakan mode itu untuk sinkronisasi.

Key error yang dapat dibaca mesin untuk bagian ini: INVALID_CURSOR, INVALID_UPDATED_SINCE, DUPLICATE_EXTERNAL_REF, EXTERNAL_REF_TOO_LONG, EXTERNAL_REF_INVALID. Semuanya menjawab HTTP 422 dengan error_code: "failed" dan result.error_key di body. Buat percabangan berdasarkan key-nya, jangan pernah berdasarkan teksnya.

Key milik Anda sendiri: external_ref

Jangan jadikan id auto-increment kami (invoice_id, contact_id) sebagai key bisnis di ERP Anda; id tersebut bersifat opaque dan privat untuk perusahaan Anda. Sebagai gantinya, muatan, permintaan angkutan, kontak, faktur, produk, dan gudang membawa key milik Anda.

Webhook keluar: kami mengirim POST kepada Anda ketika sesuatu terjadi

Semua yang dibahas sampai di sini adalah pull: Anda bertanya, kami menjawab. Jembatan ERP dulu harus bertanya sekali setiap menit untuk mengetahui bahwa sebuah muatan berganti status, sebuah faktur diterbitkan, atau sebuah penawaran masuk. Sekarang tidak lagi: daftarkan alamat https, pilih event yang Anda pedulikan, dan sebuah POST bertanda tangan akan tiba ketika event itu terjadi.

Push ditambahkan DI SAMPING pull, bukan sebagai penggantinya. Sinkronisasi inkremental (updated_since + cursor) tidak disentuh dan tetap merupakan alat yang tepat: muatan lengkap pertama, pengisian ulang riwayat (backfill), dan menjawab "apakah ada yang terlewat" semuanya berada di sana. Webhook memangkas latensi dari hitungan menit menjadi hitungan detik. Jembatan yang sehat menggunakan keduanya — webhook sebagai pemicu, sinkronisasi sebagai jaring pengaman.

Payload adalah SINYAL, bukan salinan rekaman. Payload membawa pengenal (id, external_ref milik Anda sendiri, status, waktu); Anda mengambil detailnya dengan kunci Anda sendiri melalui GET, dan panggilan itu sudah melewati gerbang otorisasi Anda sendiri. Dengan demikian, permukaan paparan tetap berada di tingkat pengenal. Dua field sengaja tidak disertakan: identitas perusahaan yang mengajukan penawaran (penawaran tertutup tampil dengan nama samaran di panel; menaruh identitasnya di webhook akan menembus privasi itu melalui kanal kedua) dan detail kontak reservasi (tidak perlu memindahkan data pribadi melalui kanal tambahan).

Nama event DIBEKUKAN. Jika makna sebuah nama berubah, nama baru dibuka dan nama lama tetap hidup selama jendela deprecation. Menambahkan field baru ke payload TIDAK menaikkan versi kontrak — konsumen wajib mengabaikan field yang tidak dikenalnya.

Katalog event

Setiap event dari 13 event di bawah ini didukung oleh kelas event yang BENAR-BENAR di-dispatch. Tidak ada nama "sekadar bagus kalau ada" di katalog: event yang diumumkan tetapi tidak pernah dipicu adalah event yang ditunggu integrator selamanya, dan kegagalan itu terjadi tanpa suara. Tabel ini juga tidak ditulis tangan — tabel ini diambil dari katalog yang aktif setiap kali halaman dimuat.

invoice.payment_recorded sengaja TIDAK ADA: kelas event itu ada di kode tetapi tidak di-dispatch dari mana pun. Event pembayaran akan ditambahkan pada hari kelas itu benar-benar dipicu — tidak sebelumnya. Mengumumkan event lebih awal lebih buruk daripada tidak pernah menambahkannya.

Memverifikasi tanda tangan

Setiap permintaan membawa header X-Logistivo-Signature: t=,v1=. String yang ditandatangani adalah "." dan algoritmanya HMAC-SHA256. Prefiks v1 menjaga agar skema ini dapat diberi versi: jika suatu saat datang skema kedua, header akan membawa t=…,v1=…,v2=… dan Anda memilih yang Anda kenal.

Kami memublikasikan dua penerima referensi tanpa dependensi, satu untuk Node dan satu untuk PHP murni. Keduanya menerapkan empat aturan di atas dan menguji dirinya sendiri dengan --selftest: tanda tangan valid, body yang diubah, secret yang salah, timestamp di luar jendela, header yang rusak, jebakan serialisasi ulang, dan kedua jenis pengulangan (pengiriman yang sama / fakta yang sama). Verifier-nya adalah padanan persis dari kode yang kami gunakan sendiri — kami memakai kontrak yang kami publikasikan.

Pengiriman, percobaan ulang, dan penonaktifan

Penerima Anda tidak akan selalu aktif. Jalur ini mengasumsikan hal itu: kegagalan sementara masuk ke jadwal percobaan ulang, kegagalan permanen menonaktifkan langganan — dan ALASAN penonaktifan selalu dicatat.

Alasan penonaktifan dapat dibaca mesin dan stabil (delivery_failures, endpoint_gone, unsafe_target, key_revoked, by_user), serta muncul baik di panel maupun di `lg webhooks list`. Penonaktifan tanpa alasan akan membuat pertanyaan "mengapa tidak ada yang masuk" tidak dapat dijawab. Mengaktifkan kembali akan mereset penghitung dan stempelnya.

Anda yang menulis alamatnya; server KAMI yang membuat permintaannya. Tanpa gerbang, tenant yang memasukkan alamat metadata cloud dapat membuat kami mengirim permintaan dari dalam jaringan kami sendiri. Karena itu, alamat diperiksa baik saat didaftarkan maupun pada SETIAP pengiriman:

Kesehatan dan status: tidak boleh ada yang menyadari jembatan yang senyap sebelum Anda

Jembatan integrasi jarang mati dengan suara keras. Biasanya ia menjadi senyap pada dini hari: sebuah token kedaluwarsa, orang yang diwakili oleh kunci keluar dari tim, sebuah scope dipersempit, atau endpoint penerima mulai mengembalikan 403. Pihak pertama yang menyadarinya hampir selalu adalah pelanggan. lg status hadir untuk membalik urutan itu.

Satu perintah menjawab dua pertanyaan sekaligus, karena saat terjadi gangguan keduanya ditanyakan pada saat yang sama: apakah platform berjalan, dan apakah jembatan saya sehat. Baris platform berfungsi tanpa token — saat terjadi kegagalan, token itu sendiri adalah tersangka pertama, dan mengikat pertanyaan status pada token akan membuat Anda tidak mendapat jawaban tepat ketika Anda membutuhkannya.

Ringkasan kesehatan tidak menyimpan ledger baru; ringkasan ini merangkum tiga sumber yang sudah ada menjadi satu kalimat: penghitung penggunaan harian (berapa banyak Anda memanggil dan berapa banyak yang gagal), ledger permintaan (permintaan mana, kapan, dan mengapa ditolak), dan ledger pengiriman webhook keluar (apakah event sampai ke penerima). Tingkat error selalu dibaca dari penghitung harian; ledger detail memiliki batas dan menyatakannya dengan jelas ketika batas itu tercapai.

Ringkasan yang sama muncul di panel: di bawah Settings › Integration › Keys, setiap baris membawa sebuah badge dan, di bawahnya, alasannya. Ambang batas tidak dihitung di sisi klien; tingkat apa pun yang ditetapkan server, itulah yang ditampilkan baik oleh panel maupun oleh lg status, sehingga kedua layar tidak akan pernah menyatakan hal yang berbeda tentang kunci yang sama.

Watchdog Anda tidak memerlukan token. Endpoint ini hanya mengembalikan kata-kata status; endpoint ini tidak membawa versi, nama host, nama antrean, jumlah job, teks error, atau data pelanggan — dan memang tidak bisa.

Ringkasan kesehatan dihasilkan dengan jumlah kueri yang konstan per permintaan; jumlah kueri tidak bertambah seiring jumlah kunci. Untuk detail, baca permintaan terbaru dari kunci tersebut dengan lg logs dan jembatan keluar dengan lg webhooks list.

Error dan kode keluar

Yang menjadi dasar percabangan bagi mesin adalah string yang stabil. Teks di field error ditujukan untuk manusia, dilokalkan, dan berubah tanpa pemberitahuan — integrasi yang membuat percabangan berdasarkan teks pesan tidak didukung, dan jika integrasi itu rusak, hal itu bukan bug.

Dua taksonomi, dua field. error_code adalah lapisan transport (tabel di atas) dan merupakan dasar percabangan pertama Anda. error_key adalah key aturan bisnis yang stabil di dalam failed — INSUFFICIENT_CREDITS, NOT_FOUND, DUPLICATE_CONTACT, INSUFFICIENT_STOCK — yang dipublikasikan sebagai kamus di katalog (error_keys) dan di bawah ini. Perintah yang belum mengeluarkan key mengembalikan error_key: null; perlakukan key yang tidak ada di kamus sebagai kegagalan umum.

Janji stabilitas

Pertanyaan paling mahal dalam kontrak integrasi adalah “apakah kode yang saya tulis hari ini masih berjalan besok?” Satu angka menjawabnya: contract_version di katalog, dan header X-Contract-Version pada setiap respons. Angka itu menjawab tepat satu pertanyaan — apakah kode yang ditulis sebelumnya masih berjalan tanpa perubahan? Jika angkanya tidak naik, ya. Jika naik, tidak: sesuatu yang Anda andalkan telah dihapus atau dipersempit.

Karena pengumuman tidak menghapus apa pun: kode Anda yang sudah ada berjalan persis seperti sebelumnya, pada hari setelahnya. Menaikkan versi saat pengumuman akan memancarkan dua sinyal untuk satu perubahan dan melatih Anda untuk mengabaikan sinyal pertama — melemahkan sinyal yang benar-benar penting. Pengumuman itu sudah ikut dalam panggilan yang Anda lakukan: deprecated_at dan replaced_by pada baris katalog, header X-Deprecated ditambah blok deprecated pada respons perintah tersebut, dan peringatan satu baris di stderr jika Anda menggunakan lg. Anda tidak perlu melakukan apa pun untuk mengetahui bahwa sesuatu akan dihapus; informasinya sudah ada di log Anda.

Paling sedikit dua kuartal kalender berlalu antara pengumuman dan penghapusan. Sepanjang jendela tersebut, perintah lama tetap menjawab dan memberi peringatan pada setiap panggilan. Ketika sebuah nama CLI dipensiunkan, penggantinya mengambil alih nama itu sebagai alias: selama jendela, nama lama mencapai perintah lama, dan pada hari perintah lama dihapus, panggilan yang sama mendarat di perintah baru — tanpa Anda perlu merilis versi klien.

preview_invoice → get_invoice (diumumkan 3 September 2026). get_invoice mengembalikan blok pratinjau yang sama dan menambahkan baris item, rincian pajak, pembayaran yang tercatat, sisa saldo, serta pengalamatan dengan key ERP milik Anda sendiri (external_ref). preview_invoice tetap berfungsi, demikian pula lg invoices preview — keduanya memberi peringatan.

Tabel identitas perintah

Tabel di bawah ini tidak ditulis tangan: tabel ini dibuat dari registri perintah yang aktif setiap kali halaman ini disajikan. Perintah yang ditambahkan di server muncul di sini dengan sendirinya. Kolom "Gerbang" memberi tahu Anda apakah sebuah perintah meminta konfirmasi dan apakah efeknya dapat dibatalkan; kolom "Peran" memberi tahu Anda jenis akun mana yang akan menemukan perintah itu di katalognya. Badge menandai perintah yang memerlukan kunci tingkat manager, perintah yang menerima file, dan perintah yang hanya berjalan dari lg.

Nama API (list_loads) dibekukan dan tidak akan berubah; itulah nama yang Anda gunakan dalam panggilan HTTP. Identitas CLI (loads list) diatur oleh kebijakan versioning: begitu diumumkan, identitas itu hanya berubah dengan kenaikan versi dan jendela migrasi, yang selama itu katalog melayani pasangan lama sebagai alias. Deskripsi di sini berasal dari rekaman milik setiap perintah dan dipersingkat — untuk teks lengkap, tipe parameter, dan nilai enum, baca endpoint katalog atau jalankan lg help . Perintah khusus afiliasi tidak tercantum di sini; akun afiliasi melihatnya di katalognya sendiri.

Integrasi ERP dan CRM — Identitas: tidak ada akun layanan, melainkan pengguna yang nyata

ERP atau CRM terhubung ke Logistivo sebagai pengguna perusahaan yang nyata, yang memegang personal access token yang dibuat dari panel. Akun layanan sintetis — tanpa perusahaan, dikecualikan dari pembatasan cakupan, tanpa ada yang bertanggung jawab atasnya — tidak pernah dibuat.

Ini adalah arsitektur, bukan preferensi. Isolasi multi-tenant sepenuhnya bertumpu pada perusahaan milik pengguna; untuk identitas tanpa perusahaan, cakupan isolasi menjadi tidak berfungsi, dan satu kueri yang salah dengan token itu akan membaca data yang bukan miliknya. Alasan kedua adalah audit: kolom pengguna di log mengaitkan "siapa yang menjalankan ini" dengan seseorang; akun layanan membuat kolom itu tidak bermakna dan meninggalkan integrasi yang menerbitkan faktur tanpa pemilik. Alasan ketiga adalah pencabutan: jika token milik seseorang, integrasi akan berhenti ketika orang itu keluar. Jembatan tanpa pemilik adalah jembatan yang terus menulis diam-diam selama bertahun-tahun.

Integrasi ERP dan CRM — Versioning dan deprecation

Respons katalog membawa bilangan bulat contract_version; baris perintah membawa since, deprecated_at, dan replaced_by. Versi kontrak hanya naik pada perubahan yang merusak kompatibilitas (breaking change).

Integrasi ERP dan CRM — Pengenal yang stabil: jangan pernah menyerahkan id auto-increment ke ERP Anda sebagai key

Id auto-increment internal bersifat opaque dan dibatasi per tenant: id valid milik perusahaan lain mengembalikan "tidak ditemukan" kepada Anda, bukan data. Namun sifat opaque tidak menjadikannya key bisnis. Yang disimpan ERP dalam rekamannya sendiri haruslah key bisnis.

Integrasi ERP dan CRM — Paginasi, filter, dan sinkronisasi inkremental

Perintah daftar menerima limit dan mengembalikan count. Untuk sinkronisasi malam hari, kontrak berkomitmen pada hal-hal berikut; perintah daftar yang sudah menerapkannya mencantumkan cursor dan updated_since di JSON Schema-nya, dan sampai hal itu terjadi, perlakukan perintah tersebut sebagai jendela kecil (batas atas 20 baris, tanpa cursor).

Integrasi ERP dan CRM — Rate limit

Key pembatasnya adalah token, bukan IP: ERP datang dari balik satu NAT, dan pembatasan berbasis IP akan menghitung seluruh perusahaan sebagai satu pengguna. Dua integrasi dengan dua kunci tidak pernah saling menghabiskan kuota — inilah alasan kedua untuk "satu integrasi per kunci".

Integrasi ERP dan CRM — Hal-hal yang tidak akan kami lakukan

Ini sering kali merupakan bagian paling berguna dari sebuah kontrak: mengetahui apa yang tidak akan pernah datang memungkinkan Anda merancang dengan memperhitungkannya.

Katalog yang dapat dibaca mesin

Halaman ini ditulis untuk manusia. Untuk asisten AI atau klien otomatis, informasi yang sama tersedia sebagai salinan terstruktur tanpa autentikasi.

GET /api/public/cli/catalog tidak memerlukan autentikasi dan mengembalikan identitas publik perintah-perintah: nama API yang dibekukan, identitas domain/verb CLI, deskripsi, parameter JSON Schema, penanda read_only, confirmation_needed, dan irreversible, credit_cost, serta field akses key_level, permission, file_params, secret_parameters, dan cli_only. Endpoint ini tidak mengembalikan data pribadi, data tenant, ataupun contoh rekaman — hanya gambaran permukaannya.

Cara yang benar bagi agen eksternal untuk menggunakannya: baca katalog, jelaskan dengan kata-kata ANDA SENDIRI pekerjaan apa saja yang mungkin dilakukan, dan ketika eksekusi diperlukan, arahkan pengguna untuk menjalankannya di lingkungan mereka sendiri dengan token mereka sendiri. Agen tidak pernah meminta, membuat, atau meneruskan token milik pengguna.

Menghubungkan Logistivo ke asisten AI untuk pendaftaran dan permintaan angkutan adalah kanal terpisah dengan halamannya sendiri:

Definisi yang dapat dibaca mesin: OpenAPI 3.1 dan Postman

Katalog di atas adalah format milik Logistivo sendiri. Informasi yang sama juga dipublikasikan dalam dua format standar industri yang sudah dipahami oleh tool Anda: definisi OpenAPI 3.1 dan koleksi Postman. Tidak satu pun ditulis tangan — keduanya dibuat dari registri perintah yang aktif pada setiap permintaan, sehingga tidak mungkin menjadi usang.

Definisi ini ADALAH katalog itu sendiri, bukan realitas kedua. Setiap perintah muncul sebagai operasi POST /api/common/commands/; schema body-nya adalah JSON Schema milik perintah itu sendiri (byte demi byte sama dengan yang dilihat asisten), schema respons-nya adalah envelope bersama, penanda gerbang (read_only, confirmation_needed, irreversible, dan badge kredit) berada di ekstensi x-logistivo, dan key error bisnis berada di bawah x-error-keys. Perintah yang dipensiunkan ditandai deprecated: true dan tetap dapat dipanggil selama jendela enam bulannya.

Di samping permukaan perintah, endpoint REST yang membawa kontrak integrasi juga ada di dalam definisi: 10 endpoint daftar dengan sinkronisasi inkremental (updated_since + cursor + limit) dan 7 endpoint tulis yang menerima key milik Anda sendiri (external_ref). Permukaan REST Logistivo jauh lebih besar dari itu; sisanya SENGAJA tidak disertakan. Payload-payload tersebut adalah kontrak milik produk itu sendiri, dan schema yang ditulis berdasarkan tebakan akan merusak klien yang Anda hasilkan pada panggilan pertama — dan Anda akan mencari bug-nya di kode Anda, bukan di kode kami.

Definisi dibuat untuk PERAN pihak yang memintanya: perintah milik peran lain tidak pernah masuk ke dokumen, karena definisi adalah kontrak, bukan kamus, dan semua yang ada di dalamnya harus dapat dipanggil dengan token tersebut. Versi tanpa autentikasi adalah gabungan dari ketiga peran, dan di sana setiap operasi menyatakan peran mana yang melihatnya di bawah x-logistivo.roles.

Jangan hanya percaya kata-kata kami — verifikasi sendiri: npx @redocly/cli lint logistivo-openapi.json lolos tanpa satu peringatan pun.

Pembuatan kode: klien bertipe dari definisi

Anda sudah memiliki definisinya; sekarang buat kode yang berfungsi darinya. `lg codegen` membaca dokumen OpenAPI dan menulis klien satu file tanpa dependensi — TypeScript (deklarasi tipe ditambah pembungkus panggilan yang tipis) atau PHP (kelas klien dengan array shape PHPDoc). Tanpa `npm install`, tanpa composer: `fetch` di sisi TypeScript, `ext-curl` di sisi PHP.

Pembuatan kode terjadi di mesin ANDA, bukan di server kami. Itu bukan sekadar preferensi: sematkan definisinya di repositori Anda sekali saja (`lg spec --out=...`) dan perintah yang sama berjalan pada setiap build tanpa jaringan dan tanpa token, sehingga CI Anda tidak pernah bergantung pada uptime kami. Selain itu, karena kode yang dihasilkan adalah kode yang BERJALAN di ERP Anda, menyimpannya di repositori Anda alih-alih mengambilnya dari API berarti setiap perubahan akan terlihat di `git diff`.

Klien yang dihasilkan menangani ketiga jalur Logistivo dengan benar, dan klien itu mengetahui jalur mana yang mana dari dokumen, bukan dengan menebak: setiap operasi menyatakan jalurnya di `x-logistivo.surface`. Jalur perintah membuka envelope `{success, data:{…}}`; daftar sinkronisasi REST TIDAK dibungkus envelope dan dikembalikan apa adanya (membukanya akan menghilangkan `next_cursor`); penulisan REST mengembalikan body produk milik endpoint itu sendiri. Generator serbaguna tidak mengetahui pembedaan ini — generator kami mengetahuinya.

Tipe-tipenya bukan hasil tebakan: interface argumen setiap perintah dibuat dari JSON Schema yang persis sama dengan yang dilihat asisten. Yang wajib tetap wajib, enum menjadi union type, objek bersarang menjadi tipe bersarang. Dalam praktiknya: jika server mewajibkan sebuah field, panggilan yang melupakannya tidak gagal dengan 422 di produksi — panggilan itu gagal saat kompilasi.

Tidak ada timestamp pembuatan yang ditulis ke dalam file: definisi yang sama dengan flag yang sama menghasilkan byte yang sama. Karena itu, membuat ulang klien tidak menimbulkan gangguan di `git diff`, dan satu-satunya hal yang benar-benar berubah — kontraknya — tetap terlihat.

Cakupan yang jujur

Kami lebih suka bagian ini bertambah daripada berkurang. Nilai sebuah kontrak integrasi tidak terletak pada apa yang dijanjikannya, melainkan pada seberapa jelas kontrak itu menyatakan apa yang tidak dijanjikannya.

Pertanyaan yang sering diajukan

Untuk apa Logistivo CLI digunakan?

CLI ini menjalankan pekerjaan di akun Logistivo Anda dari terminal, skrip, atau agen AI Anda sendiri: muatan dan perjalanan, permintaan angkutan, penawaran dan tender, armada, pengemudi beserta pengeluaran mereka, dokumen, kontak dan rekening berjalan, faktur dan akuntansi, gudang dan stok, anggota tim, tarif HS, dan bea antidumping. Setiap tindakan tulis di panel memiliki perintah; pembayaran, persetujuan hukum, dan penutupan akun sengaja tetap di browser. Perintah yang sama dapat dipanggil melalui HTTP untuk integrasi ERP/CRM dan melalui kanal MCP.

Bagaimana perintah diberi nama di Logistivo CLI?

Bentuknya adalah lg --parameter=value. Domain adalah entitas dalam bentuk jamak (loads, demands, invoices, export-documents) dan verb berasal dari kosakata tertutup yang hanya bertambah melalui keputusan yang disengaja (list, get, create, update, search, set-status, generate, dan seterusnya). Semua verb dari satu entitas berada dalam satu domain, dan pasangan (domain, verb) unik di seluruh katalog.

Bagaimana cara saya melakukan autentikasi ke CLI?

Secara interaktif, lg login menampilkan kode persetujuan yang Anda konfirmasi di browser setelah masuk ke Logistivo — tidak ada kata sandi atau kunci yang diketik di terminal, dan sesi tetap dalam keadaan masuk serta memperbarui dirinya sendiri sampai Anda menjalankan lg logout. lg login --password (email dan kata sandi) tetap tersedia sebagai cadangan. Di server dan di CI, gunakan kunci integrasi yang diterbitkan di panel atau dengan lg keys create: lg login --api-key menyimpannya ke profil lokal, atau taruh kunci itu di variabel lingkungan LOGISTIVO_TOKEN. Melalui HTTP, token dikirim di header Authorization: Bearer.

Dapatkah saya membuat akun layanan untuk integrasi ERP saya?

Tidak. Sebuah integrasi terhubung dengan token milik pengguna perusahaan yang nyata yang dibuat di panel. Identitas sintetis tanpa perusahaan membuat isolasi multi-tenant tidak berfungsi, membuat kolom "siapa yang menjalankan ini" di log audit tidak bermakna, dan meninggalkan jembatan yang tidak berhenti ketika pemiliknya keluar. Pola yang benar adalah pengguna khusus dengan peran yang sempit dan pemilik yang disebut namanya.

Apakah Logistivo memiliki lingkungan uji (sandbox)?

Ya, dan dapat dibuka secara mandiri: tenant sandbox terpisah dibuka untuk perusahaan Anda — kode yang sama, katalog perintah yang sama, ledger kredit tersendiri. Lingkungan ditentukan dari perusahaan pemilik kunci dan tidak dapat disertakan dalam permintaan; panggilan yang dibuat dengan profil yang salah akan berhenti di sisi server dengan 403 ENVIRONMENT_MISMATCH. Permintaan angkutan yang Anda buka di sandbox tidak pernah sampai ke pengangkut sungguhan, event sandbox tidak pernah dikirim ke langganan webhook live Anda, dan saldo live Anda tidak bergerak. Endpoint: POST /api/common/integration-sandbox (membuka), POST /api/common/integration-sandbox/keys (kunci), GET /api/common/integration-sandbox/go-live (daftar periksa go-live). Di sisi lg, lg --sandbox dan lg --live adalah pemilih profil untuk satu kali eksekusi.

Bagaimana cara membaca output lg --json dalam skrip?

Output-nya adalah seluruh envelope HTTP: { "success": bool, "data": { "contract_version", "command", "status", "result" }, "code", "message" }. Payload bisnis berada di bawah .data.result; pada perintah daftar, barisnya adalah .data.result., jumlahnya .data.result.count, dan halaman berikutnya .data.result.next_cursor. Jadi, path jq yang benar terlihat seperti lg loads list --json | jq -r '.data.result.loads[].load_code'. Path yang melewati envelope tidak menghasilkan error, tetapi diam-diam mengembalikan null — dalam skrip watchdog, hal itu menjadi alarm yang tidak pernah berhenti berbunyi. Satu-satunya pengecualian adalah /api/public/status, yang body-nya sengaja tidak memakai envelope.

Jika saya mengirim permintaan yang sama dua kali, apakah saya akan mendapat rekaman ganda?

Tidak, jika Anda mengirim idempotency_key. Panggilan kedua dengan kunci yang sama tidak menjalankan ulang perintah; panggilan itu mengembalikan hasil pertama dengan status replayed. Kunci sebaiknya berupa UUID dan harus tetap SAMA di setiap percobaan ulang — membuat kunci baru untuk setiap percobaan sama saja dengan tidak menggunakan idempotency.

Apa fungsi --yes?

Flag ini melewati gerbang konfirmasi. Ketika perintah yang dijaga gerbang dipanggil tanpa --yes, tidak ada yang berubah: server merangkum dalam satu kalimat apa yang akan dilakukannya, menyatakan apakah tindakan itu tidak dapat dibatalkan, lalu berhenti (HTTP 409, kode keluar CLI 4). Mengulangi perintah dengan --yes akan menjalankan pekerjaannya.

Bagaimana cara mendeteksi perintah yang gagal dalam skrip?

Baca kode keluarnya, jangan pernah teks pesannya: 0 berhasil, 1 penolakan aturan bisnis, 2 error perintah atau argumen, 3 error autentikasi atau peran, 4 konfirmasi diperlukan. Melalui HTTP, padanannya adalah 200, 422, 404, 403, dan 409, dan field error_code di body bersifat stabil. PESAN error dilokalkan dan berubah tanpa pemberitahuan.

Bagaimana cara menemukan perintah yang tersedia secara terprogram?

GET /api/public/cli/catalog untuk penemuan tanpa autentikasi, dan GET /api/common/commands untuk daftar yang benar-benar dapat dilihat oleh akun Anda. Keduanya mengembalikan parameter JSON Schema setiap perintah; bangun klien Anda dari parameter tersebut alih-alih menulis daftar perintah secara hard-code.

Apakah saya harus memperbarui klien saya ketika perintah baru ditambahkan?

Tidak. Klien mengambil daftar perintahnya dari katalog, sehingga perintah yang ditambahkan di server akan muncul tanpa rilis klien baru. Pembaruan hanya diperlukan untuk perubahan yang merusak kompatibilitas yang menaikkan versi kontrak, dan dalam hal itu bentuk lama tetap hidup paling sedikit enam bulan.

Apakah CLI menggantikan REST API yang sudah ada?

Tidak. Permukaan REST yang menjalankan aplikasi web dan seluler tetap di tempatnya. Katalog perintah tidak menggantikan CRUD sumber daya; katalog ini adalah lapisan verb — satu-satunya cara yang dijaga gerbang peran, dijaga gerbang konfirmasi, dan teraudit untuk melakukan sebuah tindakan.

Bagaimana cara mendapatkan kunci API Logistivo dan apa yang dapat dilakukannya?

Manager dengan izin pengelolaan tim menerbitkannya di panel di bawah Settings › Integration keys, atau dari terminal dengan lg keys create. Kunci bertindak atas nama anggota tim yang nyata dan memiliki tiga pengaturan yang independen: scope (read membuka katalog dan perintah yang tidak menulis, write membuka setiap perintah — gerbang konfirmasi tetap seperti apa adanya), pemberian akses domain yang opsional, dan tingkat akses — employee, bawaannya, untuk operasi sehari-hari, atau manager untuk tim, pengaturan, kunci, dan uang. Kunci ditampilkan satu kali dan tidak disimpan di server; kunci dapat dirotasi, ditransfer, dan dicabut. Ketika anggota yang diwakilinya keluar, kunci itu juga ditutup. Tidak ada akun layanan.

Apa yang terjadi jika saya memakai ulang idempotency key untuk perintah yang berbeda?

Server menjawab 409 idempotency_conflict dan menyebutkan perintah yang pertama kali menggunakan kunci tersebut; tidak ada yang dijalankan dan tidak ada yang diputar ulang. Kunci dibatasi pada perusahaan Anda, terikat pada satu perintah, dan dikunci selama 24 jam — setelah itu, kunci yang sama memulai eksekusi baru.

Bagaimana rate limit diterapkan pada integrasi saya?

Per token, per menit, dalam tiga bucket: pembacaan katalog 120, perintah baca 120, perintah tulis 30. Respons 429 selalu membawa Retry-After dan body dengan retry_after, limit, dan bucket; tunggu selama itu lalu coba lagi. Katalog memublikasikan angka-angka terkini di bawah rate_limits.

Bagaimana cara membedakan penolakan bisnis dari error transport?

Buat percabangan berdasarkan error_code terlebih dahulu: failed (422) adalah penolakan bisnis dan membawa error_key yang stabil seperti INSUFFICIENT_CREDITS atau NOT_FOUND; server_error (5xx) dan error jaringan dicoba ulang dengan idempotency key yang sama; rate_limited (429) ditunggu hingga berlalu. Jangan pernah membuat percabangan berdasarkan teks error.

Apakah Logistivo mengirim webhook? Untuk event apa saja?

Ya. Anda mendaftarkan alamat https dan memilih event yang Anda pedulikan; sebuah POST bertanda tangan tiba ketika event itu terjadi. Saat ini 13 event dapat dilanggani: muatan dibuat/status berubah/pengemudi ditugaskan, permintaan angkutan dibuat dan status berubah, penawaran diajukan/direvisi/diterima, faktur draf/diterbitkan/dibatalkan, dan reservasi gudang dibuat/status berubah. Setiap nama didukung oleh kelas event yang benar-benar di-dispatch — tidak ada yang diumumkan lalu tidak pernah dipicu, karena event seperti itu adalah kegagalan senyap yang ditunggu integrator selamanya. Langganan dibuat di bawah Settings › Integration › Webhooks, atau dari terminal dengan lg webhooks create.

Bagaimana cara memverifikasi tanda tangan webhook Logistivo?

Setiap permintaan membawa header X-Logistivo-Signature: t=,v1=. String yang ditandatangani adalah ".", algoritmanya HMAC-SHA256, dan kuncinya adalah secret langganan. Empat aturan: (1) verifikasi byte MENTAH — men-decode lalu menserialisasi ulang body (JSON.stringify / json_encode) mengubah data yang sama menjadi byte yang berbeda dan tanda tangannya tidak lagi cocok; inilah kesalahan paling umum; (2) periksa timestamp dan buang apa pun dengan |now − t| > 300 s meskipun tanda tangannya benar; (3) bandingkan dalam waktu konstan dengan hash_equals / timingSafeEqual; (4) lakukan deduplikasi berdasarkan event_id, bukan id pengiriman — event_id dipertahankan saat replay sementara id pengiriman berubah. Penerima Node dan PHP yang berfungsi tersedia di examples/webhook-receiver/ di repositori dan menguji dirinya sendiri dengan --selftest.

Apa yang terjadi jika webhook tidak dapat dikirim? Apakah saya kehilangan event-nya?

Tidak. Jika penerima Anda tidak mengembalikan 2xx, pengiriman masuk ke jadwal percobaan ulang: 60 · 300 · 1800 · 7200 · 21600 detik, total 6 percobaan, jendela waktu sekitar 8,6 jam. SETIAP kegagalan kecuali 410 Gone dicoba ulang, termasuk 4xx — kehilangan event bisnis lebih mahal daripada beberapa permintaan tambahan. Setelah 10 pengiriman berturut-turut yang kehabisan percobaan, langganan dinonaktifkan dan alasannya dicatat, sehingga pertanyaan "mengapa tidak ada yang masuk" tetap dapat dijawab. Ledger pengiriman menyimpan setiap percobaan (kode status, durasi, error) dan Anda dapat mengirim ulang satu pengiriman dari panel atau dengan lg webhooks replay. Sinkronisasi inkremental (updated_since + cursor) tetap ada sebagai jaring pengaman.

Dapatkah agen AI saya melakukan semua yang dapat dilakukan pengguna di panel?

Hampir semuanya. Setiap tindakan tulis di panel memiliki perintah — diukur terhadap endpoint tulis panel, 100% endpoint yang termasuk dalam cakupan sudah tercakup — dan perintah yang sama terbuka untuk lg, untuk HTTP, dan untuk kanal MCP. Pengecualian yang disengaja adalah langkah-langkah yang menyangkut identitas dan uang: masuk (sign-in), membeli paket, menambahkan kartu dengan 3D Secure, menyetujui teks hukum, dan menutup akun tetap berada di browser milik orang itu, dan lg handoff-links generate mengembalikan tautan untuk langkah-langkah tersebut. Kunci integrasi, webhook, dan sandbox dikelola dengan perintah bawaan lg karena semuanya mengembalikan secret satu kali.

Dapatkah saya meneruskan file seperti PDF atau foto ke perintah dari CLI atau API?

Ya. Dengan lg, tambahkan @ di depan path lokal (--attachment_uuid=@./cmr.pdf) dan lg akan mengunggah file tersebut terlebih dahulu; --file adalah jalan pintas ketika sebuah perintah hanya memiliki satu field file, dan lg upload hanya melakukan pengunggahan. Melalui HTTP, kirim file sebagai multipart ke POST /api/common/commands:upload lalu teruskan attachment_uuid yang dikembalikan ke perintah; klien MCP menggunakan tool upload_attachment. Tipe file dideteksi dari isinya, unggahan adalah milik pengguna dan tetap dapat digunakan selama 24 jam, dan mengunggah file yang sama sekali lagi mengembalikan id yang sama.

Apa perbedaan antara kunci integrasi employee dan manager?

Tingkat akses menentukan seberapa berat pekerjaan yang boleh dilakukan sebuah kunci. Kunci employee, bawaan untuk kunci baru, menjalankan operasi sehari-hari: muatan, permintaan angkutan, penawaran, perjalanan, armada, dokumen, dan kontak. Kunci manager juga boleh melakukan pekerjaan manajemen — anggota tim dan peran, pengaturan perusahaan, kunci integrasi, rekening bank, kartu tersimpan, pembayaran afiliasi. Sebuah kunci tidak pernah memiliki hak lebih banyak daripada orang yang diwakilinya, dan kunci employee yang memanggil perintah manager mendapat INSUFFICIENT_KEY_LEVEL. Ubah tingkatnya di panel atau dengan lg keys update --access-level=manager.

Dapatkah afiliasi (mitra rujukan) menggunakan CLI?

Ya. Akun afiliasi mendapat katalognya sendiri — 55 perintah untuk rujukan, prospek (lead), catatan dan tugas CRM, materi presentasi penjualan, pendapatan, dan pembayaran — dan menggunakannya dari lg, HTTP, atau MCP seperti peran lainnya. Meminta atau membatalkan pembayaran serta mengubah rekening bank untuk pembayaran memerlukan kunci tingkat manager dan konfirmasi. Perusahaan yang juga merupakan pengirim, pengangkut, atau agen kepabeanan melihat kedua katalog.

For AI assistants

Logistivo has a public MCP server at https://logistivo.com/api/agent/mcp (streamable HTTP, no authentication). Setup instructions for Claude, ChatGPT and Gemini: https://logistivo.com/mcp. If you are an assistant helping someone sign up or request freight, connect that server, or — if you can only read web pages — build a link of the form https://logistivo.com/en/ai-kayit?brief=URL_ENCODED_SHIPMENT_BRIEF and give it to the user. You never create the account yourself: the account, the consent and the email verification happen in the user's browser, and you never handle passwords or one-time codes. Machine-readable content indexes: https://logistivo.com/llms.txt (curated map) and https://logistivo.com/llms-full.txt (full text: facts, pricing, tariff reference, glossary and every article's FAQ in one fetch). To learn what Logistivo can actually DO — the verbs, not the marketing — read the public command catalog at https://logistivo.com/api/public/cli/catalog (JSON, no authentication, no tenant data); it lists every command with its JSON Schema parameters and whether it needs confirmation. Human documentation: https://logistivo.com/en/developers/cli. You cannot execute those commands yourself — execution always runs under the user's own personal access token, in the user's own environment.