API Reference — SAPI Mobile POS
Referensi lengkap endpoint REST API untuk aplikasi kasir mobile SAPI. Semua endpoint mengembalikan JSON dan menggunakan autentikasi Bearer token via Laravel Sanctum.
Autentikasi
API menggunakan Laravel Sanctum Bearer Token. Login melalui
POST /api/v1/mobile/login
untuk mendapatkan token, lalu sertakan di setiap request terproteksi.
Authorization: Bearer {token}
Accept: application/json
Content-Type: application/json
Setiap pengguna terikat pada satu tenant. Semua data difilter otomatis
berdasarkan tenant user yang login — tidak perlu mengirim tenant_id secara eksplisit.
Role yang tersedia: owner dan cashier.
Beberapa endpoint hanya bisa diakses oleh role tertentu (ditandai dengan badge pada tiap endpoint).
Setiap login baru akan menghapus token lama untuk akun yang sama. Simpan token dengan aman di sisi client.
Konvensi & Kode Status
Semua request dan response menggunakan format JSON. Tambahkan header Accept: application/json agar error juga dikembalikan sebagai JSON.
{
"message": "Email atau password salah."
}
POST /api/v1/mobile/login — maksimum 5 request per menit per IP.
Endpoint lain tidak memiliki rate limit khusus.
Auth
Login kasir atau owner. Menghapus token lama dan mengembalikan token Sanctum baru beserta informasi user dan tenant.
| Field | Tipe | Keterangan |
|---|---|---|
| email WAJIB | string | Alamat email terdaftar |
| password WAJIB | string | Password pengguna |
{
"email": "kasir@cafesapi.com",
"password": "rahasia123"
}
curl -X POST https://sapi-v1.obiwannn.web.id/api/v1/mobile/login \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "email": "kasir@cafesapi.com", "password": "rahasia123" }'
{
"token": "1|abc123xyz...",
"user": {
"id": 5,
"name": "Budi Santoso",
"email": "kasir@cafesapi.com",
"role": "cashier"
},
"tenant": {
"id": 1,
"name": "SAPI Cafe",
"address": "Jl. Sudirman No. 12",
"phone": "+62812345678"
}
}
Mencabut token saat ini. Gunakan saat pengguna keluar dari aplikasi.
Authorization: Bearer {token}
Accept: application/json
curl -X POST https://sapi-v1.obiwannn.web.id/api/v1/mobile/logout \ -H "Authorization: Bearer {token}" \ -H "Accept: application/json"
{
"message": "Logout berhasil."
}
Tenant & Produk
Mengembalikan informasi tenant milik pengguna yang sedang login (nama, alamat, telepon).
Authorization: Bearer {token}
Accept: application/json
curl https://sapi-v1.obiwannn.web.id/api/v1/mobile/tenant/profile \ -H "Authorization: Bearer {token}" \ -H "Accept: application/json"
{
"data": {
"id": 1,
"name": "SAPI Cafe",
"address": "Jl. Sudirman No. 12",
"phone": "+62812345678"
}
}
Mengembalikan semua produk aktif beserta varian yang memiliki stok > 0 dan kategorinya. Digunakan untuk membangun daftar menu di kasir.
Authorization: Bearer {token}
Accept: application/json
{
"data": [
{
"id": 1,
"name": "Kopi Susu",
"category_id": 2,
"category": { "id": 2, "name": "Minuman" },
"variants": [
{
"id": 10,
"product_id": 1,
"name": "Regular",
"price": 18000,
"stock": 50
}
]
}
]
}
Cash Drawer
Mengecek apakah pengguna saat ini memiliki sesi kas yang sedang terbuka.
Authorization: Bearer {token}
Accept: application/json
// Sesi kas terbuka { "is_open": true, "drawer_id": 7, "opened_at": "2026-05-29T08:00:00.000000Z" } // Tidak ada sesi terbuka { "is_open": false, "drawer_id": null, "opened_at": null }
Membuka sesi kas baru dengan modal awal. Gagal jika sudah ada sesi kas yang terbuka untuk user ini.
| Field | Tipe | Keterangan |
|---|---|---|
| opening_amount WAJIB | numeric | Modal awal kas (min 0) |
Authorization: Bearer {token}
Accept: application/json
Content-Type: application/json
{
"opening_amount": 500000
}
curl -X POST https://sapi-v1.obiwannn.web.id/api/v1/mobile/cash-drawer/open \ -H "Authorization: Bearer {token}" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "opening_amount": 500000 }'
{
"message": "Kas berhasil dibuka.",
"drawer_id": 7,
"opened_at": "2026-05-29T08:00:00.000000Z"
}
Menutup sesi kas aktif. Sistem otomatis menghitung selisih antara kas yang diharapkan (berdasarkan transaksi) dan kas aktual yang dihitung pengguna.
| Field | Tipe | Keterangan |
|---|---|---|
| closing_amount WAJIB | numeric | Jumlah kas fisik yang dihitung saat tutup (min 0) |
| notes OPSIONAL | string | Catatan penutupan kas (maks 500 karakter) |
Authorization: Bearer {token}
Accept: application/json
Content-Type: application/json
{
"closing_amount": 520000,
"notes": "Sesuai"
}
curl -X POST https://sapi-v1.obiwannn.web.id/api/v1/mobile/cash-drawer/close \ -H "Authorization: Bearer {token}" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "closing_amount": 520000, "notes": "Sesuai" }'
{
"message": "Kas berhasil ditutup.",
"drawer_id": 7,
"expected_amount": 515000,
"closing_amount": 520000,
"difference": 5000
}
Mengembalikan ringkasan sesi kas: detail buka/tutup, jumlah transaksi, dan rincian per metode pembayaran. Kasir hanya bisa melihat sesi kasnya sendiri; owner bisa melihat sesi siapapun dalam tenant yang sama.
| Parameter | Tipe | Keterangan |
|---|---|---|
| cashDrawer WAJIB | integer | ID sesi kas (drawer_id dari endpoint buka/tutup) |
Authorization: Bearer {token}
Accept: application/json
{
"data": {
"drawer": {
"id": 7,
"opening_amount": 500000,
"closing_amount": 520000,
"expected_amount": 515000,
"difference": 5000,
"notes": "Sesuai",
"opened_at": "2026-05-29T08:00:00.000000Z",
"closed_at": "2026-05-29T17:00:00.000000Z",
"is_open": false
},
"transaction_count": 45,
"payment_summary": [
{ "name": "Tunai", "type": "cash", "total": 300000 },
{ "name": "QRIS", "type": "digital", "total": 215000 }
]
}
}
Transaksi
Membuat transaksi POS baru. Stok langsung dikurangi saat endpoint ini dipanggil.
Dua mode: transaksi langsung (kirim payments, status completed) atau
open bill (is_open_bill: true, pembayaran menyusul via endpoint Bayar Open Bill).
| Field | Tipe | Keterangan |
|---|---|---|
| items WAJIB | array | Min 1 item |
| items[].variant_id WAJIB | integer | ID varian produk |
| items[].variant_name WAJIB | string | Nama varian (disimpan di snapshot transaksi) |
| items[].qty WAJIB | integer | Jumlah (min 1) |
| items[].notes OPSIONAL | string | Catatan item (maks 500 karakter) |
| items[].modifiers OPSIONAL | array | Modifier/topping item |
| items[].modifiers[].id WAJIB* | integer | ID modifier |
| is_open_bill OPSIONAL | boolean | Jika true, field payments tidak diperlukan. Ditolak 422 bila outlet mematikan setelan "izinkan tagihan terbuka"; tagihan yang sudah terbuka tetap bisa dilunasi |
| order_type OPSIONAL | string | dine_in atau takeaway |
| customer_name OPSIONAL | string | Nama pelanggan (maks 255) |
| table_number OPSIONAL | string | Nomor meja (maks 50) |
| notes OPSIONAL | string | Catatan transaksi (maks 500) |
| payments WAJIB† | array | †Wajib jika bukan open bill |
| payments[].payment_method_id WAJIB† | integer | ID metode pembayaran |
| payments[].amount WAJIB† | numeric | Nominal pembayaran (min 0) |
| payments[].reference_code OPSIONAL | string | Kode referensi transfer/QRIS (maks 255) |
Authorization: Bearer {token}
Accept: application/json
Content-Type: application/json
{
"items": [
{
"variant_id": 10,
"variant_name": "Kopi Susu Regular",
"qty": 2,
"notes": "Gula sedikit",
"modifiers": [{ "id": 3 }]
}
],
"order_type": "dine_in",
"table_number": "A2",
"payments": [
{ "payment_method_id": 1, "amount": 40000 }
]
}
{
"message": "Transaksi berhasil.",
"transaction_id": 42,
"code": "TRX-2026-001",
"total_amount": 36000,
"change_amount": 4000,
"status": "completed",
"is_open_bill": false
}
Mengembalikan daftar transaksi dengan pagination. Kasir hanya melihat transaksinya sendiri; owner melihat semua transaksi tenant.
| Parameter | Tipe | Keterangan |
|---|---|---|
| from OPSIONAL | date | Filter dari tanggal (format: YYYY-MM-DD) |
| to OPSIONAL | date | Filter sampai tanggal (format: YYYY-MM-DD) |
| status OPSIONAL | string | pending, completed, atau voided |
| per_page OPSIONAL | integer | Jumlah item per halaman (default: 20) |
Authorization: Bearer {token}
Accept: application/json
GET https://sapi-v1.obiwannn.web.id/api/v1/mobile/transactions?from=2026-06-01&to=2026-06-30&status=completed&per_page=10
curl "https://sapi-v1.obiwannn.web.id/api/v1/mobile/transactions?from=2026-06-01&status=completed&per_page=10" \ -H "Authorization: Bearer {token}" \ -H "Accept: application/json"
{
"data": [
{
"id": 42,
"code": "TRX-2026-001",
"cashier": "Budi Santoso",
"total_amount": 36000,
"status": "completed",
"order_type": "dine_in",
"customer_name": null,
"table_number": "A2",
"created_at": "29/05/2026 14:30"
}
],
"meta": {
"current_page": 1,
"last_page": 5,
"per_page": 10,
"total": 48
}
}
Membayar transaksi open bill yang masih berstatus pending. Setelah berhasil, status transaksi berubah menjadi completed.
| Parameter | Tipe | Keterangan |
|---|---|---|
| transaction WAJIB | integer | ID transaksi |
| Field | Tipe | Keterangan |
|---|---|---|
| payments WAJIB | array | Min 1 pembayaran |
| payments[].payment_method_id WAJIB | integer | ID metode pembayaran |
| payments[].amount WAJIB | numeric | Nominal pembayaran (min 0) |
| payments[].reference_code OPSIONAL | string | Kode referensi (maks 255) |
Authorization: Bearer {token}
Accept: application/json
Content-Type: application/json
{
"payments": [
{
"payment_method_id": 1,
"amount": 40000
}
]
}
curl -X POST https://sapi-v1.obiwannn.web.id/api/v1/mobile/transactions/42/pay \ -H "Authorization: Bearer {token}" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{ "payments": [{ "payment_method_id": 1, "amount": 40000 }] }'
{
"message": "Open bill berhasil dibayar.",
"transaction_id": 42,
"code": "TRX-2026-001",
"total_amount": 36000,
"change_amount": 4000,
"status": "completed"
}
Mengembalikan data struk lengkap: informasi tenant, detail transaksi, item dengan modifier, dan rincian pembayaran. Digunakan untuk mencetak struk.
price adalah harga yang dibayar per unit, original_price harga normal saat penjualan, dan discount_amount potongan per unit (0 bila tanpa potongan). Cetak baris berdiskon sebagai harga normal lalu baris Diskon. discount_total adalah jumlah potongan untuk baris "Anda hemat" — keterangan saja, subtotal sudah bersih dari potongan. original_price bernilai null pada transaksi tanpa jejak potongan (penjualan offline).
| Parameter | Tipe | Keterangan |
|---|---|---|
| transaction WAJIB | integer | ID transaksi |
Authorization: Bearer {token}
Accept: application/json
{
"data": {
"tenant": {
"name": "SAPI Cafe",
"address": "Jl. Sudirman No. 12",
"phone": "+62812345678"
},
"transaction": {
"code": "TRX-2026-001",
"date": "29/05/2026 14:30",
"cashier": "Budi Santoso",
"total_amount": 36000,
"discount_total": 0,
"change_amount": 4000,
"status": "completed",
"order_type": "dine_in",
"table_number": "A2",
"notes": null,
"is_open_bill": false
},
"items": [
{
"name": "Kopi Susu Regular",
"qty": 2,
"price": 18000,
"original_price": 18000,
"discount_amount": 0,
"subtotal": 36000,
"notes": "Gula sedikit",
"modifiers": [
{ "name": "Extra Shot", "extra_price": 0 }
]
}
],
"payments": [
{ "method": "Tunai", "amount": 40000, "reference_code": null }
]
}
}
Membatalkan (void) transaksi yang sudah berstatus completed. Hanya owner yang dapat melakukan ini. Stok akan dikembalikan secara otomatis.
| Parameter | Tipe | Keterangan |
|---|---|---|
| transaction WAJIB | integer | ID transaksi yang akan di-void |
Authorization: Bearer {token}
Accept: application/json
curl -X POST https://sapi-v1.obiwannn.web.id/api/v1/mobile/transactions/42/void \ -H "Authorization: Bearer {token}" \ -H "Accept: application/json"
{
"message": "Transaksi berhasil di-void.",
"transaction_id": 42,
"code": "TRX-2026-001",
"status": "voided"
}