POST
/auth/login
Public
Owner login dengan identifier dan password
Autentikasi owner via identifier (email ATAU klay_id) + password. Akun harus sudah di-create oleh admin Skalar saat onboarding. Berhasil = access token di body, refresh token di httpOnly cookie. Response data hanya berisi access_token (tidak ada nested user object).
Request body
| Field | Type | Description |
| identifierrequired |
string |
Email atau klay_id owner. |
| passwordrequired |
string · password |
|
{
"identifier": "string",
"password": "string"
}
Responses
401
Email atau password salah
403
Akun ditemukan tapi business status SUSPENDED atau CHURNED
POST
/auth/google/login
Public
Owner login dengan Google OAuth (BELUM DIIMPLEMENTASI)
Handler saat ini mengembalikan 501 Not Implemented (auth/handler.go GoogleLogin). Google OAuth login belum di-wire. Entri 200/403 di bawah adalah kontrak yang dimaksudkan tapi belum dibangun: dipanggil NextAuth setelah Google OAuth berhasil, server verifikasi email terdaftar sebagai owner, lalu issue JWT Klay sendiri.
Request body
| Field | Type | Description |
| emailrequired |
string · email |
|
| namerequired |
string |
|
{
"email": "owner@klay.id",
"name": "string"
}
Responses
200
[INTENDED, BELUM DIBANGUN] Login berhasil
403
[INTENDED, BELUM DIBANGUN] Email belum terdaftar sebagai owner
501
Google login belum diimplementasi (perilaku aktual server)
POST
/auth/refresh
Public
Refresh access token
Baca refresh token dari httpOnly cookie, issue access token baru.
Responses
200
Access token baru
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
401
Refresh token tidak valid atau expired
POST
/auth/logout
🔒 Auth required
Logout
Hapus refresh token dari httpOnly cookie.
Responses
GET
/users/me
🔒 Auth required
Profil owner yang sedang login
Mengembalikan profil owner beserta data business yang terkait.
Responses
200
Profil berhasil diambil
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
PATCH
/users/me
🔒 Auth required
Update profil owner
Owner update nama atau phone. Tidak bisa ubah email (admin Skalar only).
Request body
| Field | Type | Description |
| full_name |
string |
|
| phone |
string |
|
{
"full_name": "string",
"phone": "string"
}
Responses
200
Profil ter-update
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
GET
/business
🔒 Auth required
Detail business owner yang login
Responses
200
Data business
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
PATCH
/business
🔒 Auth required
Update info business (terbatas)
Owner hanya bisa ubah field display business. Field plan, features, max_operators, status, xendit_status — admin Skalar only.
Request body
| Field | Type | Description |
| business_name |
string |
|
| phone |
string |
|
| address |
string |
|
| city |
string |
|
| country |
string |
|
| qris_string |
string |
QRIS static payload merchant. |
| category |
string |
|
{
"business_name": "string",
"phone": "string",
"address": "string",
"city": "string",
"country": "string",
"qris_string": "string",
"category": "string"
}
Responses
200
Business ter-update
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
GET
/business/operators
🔒 Auth required
List operator business
Daftar operator + info kuota. qr_token tidak dikembalikan di list, hanya saat create dan regenerate.
Responses
200
List operator
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
POST
/business/operators
🔒 Auth required
Tambah operator baru
Owner-only. Cap divalidasi terhadap business.max_operators (default 3, admin update kalo beli add-on, -1 = unlimited). Body hanya berisi name; server generate operator_code (KL-XXXXX), PIN, dan qr_token. operator_code dan credential (qr_token/pin) dikembalikan di response create/regenerate ini saja (one-time).
Request body
| Field | Type | Description |
| namerequired |
string |
Satu-satunya field input. Server generate operator_code dan credential, dikembalikan di response.
|
{
"name": "string"
}
Responses
201
Operator dibuat
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
409
operator_code sudah dipakai atau slot penuh
GET
/business/operators/{operator_id}
🔒 Auth required
Detail satu operator
Parameters
| Name | In · Type | Description |
| operator_idrequired |
path · string |
|
Responses
200
Operator ditemukan
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
PUT
/business/operators/{operator_id}
🔒 Auth required
Update operator (partial)
Owner update name atau is_active. Reset password dan QR di endpoint terpisah.
Parameters
| Name | In · Type | Description |
| operator_idrequired |
path · string |
|
Request body
| Field | Type | Description |
| name |
string |
|
| is_active |
boolean |
|
{
"name": "string",
"is_active": true
}
Responses
200
Operator ter-update
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
DELETE
/business/operators/{operator_id}
🔒 Auth required
Hapus operator (soft delete)
Soft delete — deleted_at di-set, row tetap ada. operator_code TIDAK dibebaskan (tetap tersimpan di row, slot unique index terpakai). Operator tidak bisa login lagi. Slot operator aktif berkurang (COUNT is_active turun). Data historis tetap valid.
Parameters
| Name | In · Type | Description |
| operator_idrequired |
path · string |
|
Responses
POST
/business/operators/{operator_id}/regenerate-qr
🔒 Auth required
Regenerate qr_token operator
Issue qr_token baru. QR lama tidak valid untuk login baru, tapi sesi JWT yang masih aktif tetap berlaku sampai access token expired.
Parameters
| Name | In · Type | Description |
| operator_idrequired |
path · string |
|
Responses
200
QR baru
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
GET
/business/operators/{operator_id}/qr
🔒 Auth required
Ambil operator_code + qr_token operator
UndocumentedPresent in user/routes.go, absent from prior contract.
Owner-only. Mengembalikan operator_code dan qr_token saat ini untuk re-render QR di dashboard tanpa regenerate.
Parameters
| Name | In · Type | Description |
| operator_idrequired |
path · string |
|
Responses
200
operator_code dan qr_token
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
POST
/business/operators/{operator_id}/rotate
🔒 Auth required
Rotate credential operator
UndocumentedPresent in user/routes.go, absent from prior contract.
Owner-only. Issue ulang qr_token dan PIN operator. Credential baru dikembalikan satu kali (one-time) di response ini.
Parameters
| Name | In · Type | Description |
| operator_idrequired |
path · string |
|
Responses
200
Credential operator ter-rotate
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
POST
/business/operators/{operator_id}/force-logout
🔒 Auth required
Paksa logout operator
UndocumentedPresent in user/routes.go, absent from prior contract.
Owner-only. Cabut sesi aktif operator (revoke refresh token). Mengembalikan 204 No Content saat sukses.
Parameters
| Name | In · Type | Description |
| operator_idrequired |
path · string |
|
Responses
204
Sesi operator dicabut (no content)
POST
/operator/auth/login
Public
Operator login dengan credential
Endpoint public. identifier (email atau klay_id business) me-resolve business; operator dicari via operator_code + PIN.
Request body
| Field | Type | Description |
| identifierrequired |
string |
Email atau klay_id business (resolves business). |
| operator_coderequired |
string |
|
| pinrequired |
string · password |
Tepat 6 digit numerik (server: validate len=6,numeric). |
{
"identifier": "string",
"operator_code": "string",
"pin": "string"
}
Responses
200
Login berhasil
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
403
Operator non-aktif atau business SUSPENDED/CHURNED
POST
/operator/auth/login/qr
Public
Operator login dengan QR scan
Endpoint public. qr_token statis, regenerate via owner.
Request body
| Field | Type | Description |
| qr_tokenrequired |
string |
|
{
"qr_token": "string"
}
Responses
200
Login berhasil
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
403
Operator non-aktif atau business SUSPENDED/CHURNED
GET
/operator/me
🔒 Auth required
Profil operator yang sedang login (PWA operator)
Responses
200
Profil operator
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
GET
/products
🔒 Auth required
List produk business
Owner & operator (RequireOperator) bisa GET. Handler hanya pakai business_id dari JWT; tidak ada query filter/sort/search yang diproses oleh server.
Responses
200
List produk (data dibungkus sebagai object { products: [...] })
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
POST
/products
🔒 Auth required
Tambah produk (owner only)
UndocumentedOwner-facing product CRUD exists at /products in routes.go; prior contract said admin-only.
RequireOwner. business_id diambil dari JWT, bukan body.
Request body
Responses
201
Produk dibuat
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
400
Body invalid atau gagal validasi
GET
/products/{product_id}
🔒 Auth required
Detail produk
Owner & operator (RequireOperator).
Parameters
| Name | In · Type | Description |
| product_idrequired |
path · string |
|
Responses
200
Detail produk
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
PATCH
/products/{product_id}
🔒 Auth required
Update produk (owner only, partial)
UndocumentedOwner-facing product CRUD exists at /products in routes.go; prior contract said admin-only.
RequireOwner. Partial update — field yang tidak dikirim tidak berubah.
Parameters
| Name | In · Type | Description |
| product_idrequired |
path · string |
|
Request body
Responses
200
Produk ter-update
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
400
Body invalid atau product_id tidak valid
DELETE
/products/{product_id}
🔒 Auth required
Hapus produk (owner only)
UndocumentedOwner-facing product CRUD exists at /products in routes.go; prior contract said admin-only.
RequireOwner.
Parameters
| Name | In · Type | Description |
| product_idrequired |
path · string |
|
Responses
200
Produk dihapus. response.NoContent() mengembalikan HTTP 200 dengan body { success: true }, BUKAN 204 No Content.
| Field | Type | Description |
| success |
boolean |
|
GET
/orders
🔒 Auth required
List order milik operator yang login
order.ListMyOrders (RequireOperator). Hanya order milik operator dari JWT. Kalau dipanggil owner (operator_id kosong) mengembalikan array kosong. Response adalah array Transaction langsung di `data`, BUKAN object berpaginasi.
Responses
200
Array order milik operator (atau [] untuk owner)
| Field | Type | Description |
| success |
boolean |
|
| data |
array<object> |
|
| error |
string |
|
POST
/orders
🔒 Auth required
Buat order baru (status DRAFT)
order.CreateOrder (RequireOperator). Status awal DRAFT. Backend hitung total_amount dari items dan snapshot product_name + unit_price ke order_items. operator_id diambil dari JWT (nullable kalau owner).
Request body
| Field | Type | Description |
| itemsrequired |
array<object> |
|
| note |
string |
|
{
"items": [
{
"product_id": "string",
"quantity": 0
}
],
"note": "string"
}
Responses
201
Order DRAFT dibuat (response.Created -> 201)
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
400
Body invalid, items kosong, atau produk tidak ditemukan/unavailable (ErrEmptyItems / ErrProductNotFound dipetakan ke 400 BadRequest)
PATCH
/orders/{order_id}/items
🔒 Auth required
Ganti items order DRAFT
order.UpdateItems (RequireOperator). Hanya valid saat status DRAFT.
Parameters
| Name | In · Type | Description |
| order_idrequired |
path · string |
|
Request body
| Field | Type | Description |
| itemsrequired |
array<object> |
|
| note |
string |
|
{
"items": [
{
"product_id": "string",
"quantity": 0
}
],
"note": "string"
}
Responses
200
Order diperbarui
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
400
Body invalid / produk tidak ditemukan
409
Order tidak dalam status DRAFT (ErrNotDraft)
POST
/orders/{order_id}/checkout/begin
🔒 Auth required
Mulai checkout (DRAFT -> PENDING)
order.BeginCheckout (RequireOperator). Stamp checkout_started_at dan pindah status ke PENDING. Tidak ada request body. Wajib dipanggil sebelum confirm — confirm tanpa begin menghasilkan ErrCheckoutNotStarted (400).
Parameters
| Name | In · Type | Description |
| order_idrequired |
path · string |
|
Responses
200
Checkout dimulai, status PENDING
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
409
Order tidak dalam status DRAFT (ErrNotDraft)
POST
/orders/{order_id}/checkout/confirm
🔒 Auth required
Konfirmasi checkout (PENDING -> CONFIRMED)
order.ConfirmCheckout (RequireOperator). Body wajib `payment_method`. Server menolak kalau begin-checkout belum dipanggil (ErrCheckoutNotStarted) atau gesture terlalu cepat <800ms sejak checkout_started_at (ErrGestureTooFast). Stamp confirmed_at + confirmed_by (string), lalu trigger consumption_log dari product.recipe. Response menyertakan qris_string saat payment QRIS_STATIC.
Parameters
| Name | In · Type | Description |
| order_idrequired |
path · string |
|
Request body
| Field | Type | Description |
| payment_methodrequired |
object |
|
{
"payment_method": null
}
Responses
200
Order CONFIRMED. data = Order + qris_string (nullable, hanya QRIS_STATIC).
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
400
Body invalid atau checkout belum dimulai (ErrCheckoutNotStarted)
409
Order tidak PENDING (ErrNotPending) atau konfirmasi terlalu cepat <800ms (ErrGestureTooFast)
POST
/orders/{order_id}/void
🔒 Auth required
Void order (operator)
order.VoidOrder (RequireOperator). Tidak ada request body. Mengembalikan response.NoContent = HTTP 200 dengan body {"success":true} (BUKAN 204).
Parameters
| Name | In · Type | Description |
| order_idrequired |
path · string |
|
Responses
200
Order di-void. Body {"success":true} tanpa data.
| Field | Type | Description |
| success |
boolean |
|
POST
/transactions/{id}/void
🔒 Auth required
Void order (owner)
order.VoidOrderByOwner (RequireOwner). Write mutation di domain order tapi di-mount di path /transactions/{id}/void. Body wajib `void_reason`. Owner identity diambil dari user_id JWT. Mengembalikan response.NoContent = HTTP 200 dengan {"success":true} (BUKAN 204).
Parameters
| Name | In · Type | Description |
| idrequired |
path · string |
|
Request body
| Field | Type | Description |
| void_reasonrequired |
string |
Wajib diisi untuk audit |
{
"void_reason": "string"
}
Responses
200
Order di-void. Body {"success":true} tanpa data.
| Field | Type | Description |
| success |
boolean |
|
400
void_reason kosong / body invalid
GET
/transactions
🔒 Auth required
List transaksi business (read-only log)
transaction.List (RequireOwner). Read-only history. status query menerima hanya DRAFT/CONFIRMED/VOIDED — PENDING ditolak 400 oleh validasi list-filter.
Parameters
| Name | In · Type | Description |
| start_date |
query · string |
Format YYYY-MM-DD |
| end_date |
query · string |
Format YYYY-MM-DD |
| status |
query · string |
Hanya DRAFT, CONFIRMED, VOIDED diterima. PENDING ditolak 400. |
| payment_method |
query · string |
|
| operator_id |
query · string |
|
| page |
query · integer |
|
| limit |
query · integer |
|
Responses
200
List transaksi berpaginasi
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
400
Filter invalid (mis. status=PENDING, payment_method/tanggal/page/limit salah)
GET
/transactions/{transaction_id}
🔒 Auth required
Detail transaksi
transaction.GetByID (RequireOperator). Mengembalikan OrderWithItems (Transaction + items[]).
Parameters
| Name | In · Type | Description |
| transaction_idrequired |
path · string |
|
Responses
200
Detail transaksi
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
400
transaction_id tidak valid
GET
/operator/transactions
🔒 Auth required
List transaksi milik operator yang login
transaction.OperatorList (RequireOperatorOnly). Operator-scoped — hanya transaksi operator dari JWT. Filter: start_date, end_date, page, limit (tidak ada filter status/payment_method/operator_id di endpoint ini).
Parameters
| Name | In · Type | Description |
| start_date |
query · string |
Format YYYY-MM-DD |
| end_date |
query · string |
Format YYYY-MM-DD |
| page |
query · integer |
|
| limit |
query · integer |
|
Responses
200
List transaksi operator berpaginasi
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
GET
/metrics/summary
🔒 Auth required
Ringkasan performa business
Aggregate untuk dashboard. Count hanya status CONFIRMED.
Parameters
| Name | In · Type | Description |
| period |
query · string |
|
Responses
200
Summary
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
GET
/metrics/trend
🔒 Auth required
Tren transaksi harian
Parameters
| Name | In · Type | Description |
| start_date |
query · string |
|
| end_date |
query · string |
|
Responses
200
Tren harian
| Field | Type | Description |
| success |
boolean |
|
| data |
array<object> |
|
| error |
string |
|
GET
/metrics/top-products
🔒 Auth required
Produk terlaris
Parameters
| Name | In · Type | Description |
| period |
query · string |
|
| limit |
query · integer |
|
Responses
200
Top produk
| Field | Type | Description |
| success |
boolean |
|
| data |
array<object> |
|
| error |
string |
|
GET
/metrics/peak-hours
🔒 Auth required
Distribusi jam tersibuk
Parameters
| Name | In · Type | Description |
| period |
query · string |
|
Responses
200
Peak hours
| Field | Type | Description |
| success |
boolean |
|
| data |
array<object> |
|
| error |
string |
|
GET
/metrics/overview
🔒 Auth required
Analytics overview untuk custom date range
Dipakai di page Analytics, mendukung custom timeframe dan period comparison.
Parameters
| Name | In · Type | Description |
| start_daterequired |
query · string |
|
| end_daterequired |
query · string |
|
| compare_with |
query · string |
|
Responses
200
Analytics overview
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
GET
/metrics/reports/daily-sales
🔒 Auth required
Laporan penjualan harian
Aggregate transaksi CONFIRMED per hari pada tanggal tertentu.
Parameters
| Name | In · Type | Description |
| daterequired |
query · string |
|
Responses
200
Laporan harian
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
GET
/metrics/reports/monthly-sales
🔒 Auth required
Laporan penjualan bulanan
Parameters
| Name | In · Type | Description |
| monthrequired |
query · string |
Format YYYY-MM |
Responses
200
Laporan bulanan
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
GET
/metrics/reports/consumption
🔒 Auth required
Laporan pemakaian bahan baku
Aggregate dari consumption_log per ingredient untuk periode. Foundation buat owner restock decision dan AI insight downstream.
Parameters
| Name | In · Type | Description |
| start_daterequired |
query · string |
|
| end_daterequired |
query · string |
|
| ingredient |
query · string |
Filter per ingredient name (diabaikan server) |
Responses
200
Laporan consumption
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
POST
/metrics/reports/export
🔒 Auth required
Export laporan ke PDF atau CSV
Generate file download. Server return URL temporary atau stream langsung. Type laporan dan periode di-passing via body.
Request body
| Field | Type | Description |
| typerequired |
string · enum |
|
| formatrequired |
string · enum |
|
| periodrequired |
object |
Shape tergantung type. Daily=date, monthly=month, consumption=start_date+end_date. |
{
"type": "daily_sales",
"format": "pdf",
"period": {}
}
Responses
200
File generated
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
422
Type / format / period combination tidak valid
GET
/metrics/insight
🔒 Auth required
List insight cards
Rule-based MVP. Schema sudah siap untuk model_version + confidence kalau nanti switch ke AI engine.
Responses
200
Insight cards
| Field | Type | Description |
| success |
boolean |
|
| data |
array<object> |
|
| error |
string |
|
POST
/admin/auth/login
Public
Admin Skalar login
Request body
| Field | Type | Description |
| emailrequired |
string · email |
|
| passwordrequired |
string · password |
|
{
"email": "owner@klay.id",
"password": "string"
}
Responses
200
Admin login berhasil
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
POST
/admin/auth/refresh
Public
Refresh admin access token
Responses
200
Token baru
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
POST
/admin/auth/logout
🔒 Auth required
Admin logout
Responses
GET
/admin/me
🔒 Auth required
Profil admin yang sedang login
Responses
200
Profil admin
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
GET
/admin/owners
🔒 Auth required
List semua owners (cross-merchant)
Admin view. handler.ListOwners — hanya menerima page dan limit. Filter status/plan/q tidak diproses server (admin/handler.go).
Parameters
| Name | In · Type | Description |
| page |
query · integer |
|
| limit |
query · integer |
|
Responses
200
List owners berpaginasi
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
POST
/admin/owners
🔒 Auth required
Onboard merchant baru
Create Business + User owner sekaligus. Body matches admin/model.go CreateBusinessRequest. business_id digenerate server.
Request body
Responses
201
Owner berhasil di-onboard
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
409
Email owner sudah dipakai
GET
/admin/owners/{owner_id}
🔒 Auth required
Detail owner
Parameters
| Name | In · Type | Description |
| owner_idrequired |
path · string |
|
Responses
200
Detail owner
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
PATCH
/admin/owners/{owner_id}
🔒 Auth required
Update business info owner (admin-controlled fields)
Matches admin/model.go UpdateBusinessRequest. Field yang tidak dikirim tidak berubah. merchant_status enum: PENDING | REGISTERED | ACTIVE | SUSPENDED.
Parameters
| Name | In · Type | Description |
| owner_idrequired |
path · string |
|
Request body
Responses
200
Owner ter-update
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
PATCH
/admin/owners/{owner_id}/status
🔒 Auth required
Toggle aktif/suspend owner
handler.SetOwnerStatus — boolean toggle. enabled=true mengaktifkan, enabled=false mensuspend. Returns HTTP 200 { success: true } via response.NoContent().
Parameters
| Name | In · Type | Description |
| owner_idrequired |
path · string |
|
Request body
| Field | Type | Description |
| enabledrequired |
boolean |
true = aktifkan, false = suspend |
{
"enabled": true
}
Responses
200
Status ter-toggle. response.NoContent() returns HTTP 200 { success:true }.
POST
/admin/owners/{owner_id}/credential
🔒 Auth required
Set credential login owner (email dan/atau password)
UndocumentedRoute present in admin/routes.go but absent from prior contract.
handler.SetOwnerCredential. email opsional (jika dikirim harus valid email). password wajib, min 6 karakter. Returns HTTP 200 { success:true } via response.NoContent().
Parameters
| Name | In · Type | Description |
| owner_idrequired |
path · string |
|
Request body
| Field | Type | Description |
| email |
string · email |
Opsional. Jika dikirim harus valid email format. |
| passwordrequired |
string · password |
Wajib, min 6 karakter. |
{
"email": "owner@klay.id",
"password": "string"
}
Responses
200
Credential diperbarui. response.NoContent() returns HTTP 200 { success:true }.
GET
/admin/owners/{owner_id}/products
🔒 Auth required
List produk milik business
handler.ListOwnerProducts. Response membungkus products di dalam object { products: [...] } (bukan array langsung).
Parameters
| Name | In · Type | Description |
| owner_idrequired |
path · string |
|
Responses
200
List produk
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
POST
/admin/owners/{owner_id}/products
🔒 Auth required
Tambah produk untuk business
handler.CreateProduct. Body matches admin/model.go AdminCreateProductRequest. business_id diambil dari path param, bukan body.
Parameters
| Name | In · Type | Description |
| owner_idrequired |
path · string |
|
Request body
Responses
201
Produk dibuat
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
GET
/admin/products/{product_id}
🔒 Auth required
Detail produk (termasuk recipe)
handler.GetProduct. Returns AdminProductDetail yang menyertakan recipe array.
Parameters
| Name | In · Type | Description |
| product_idrequired |
path · string |
|
Responses
200
Detail produk dengan recipe
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
PATCH
/admin/products/{product_id}
🔒 Auth required
Update produk (partial)
handler.UpdateProduct. Body matches admin/model.go AdminUpdateProductRequest. Semua field optional (pointer) — field tidak dikirim tidak berubah.
Parameters
| Name | In · Type | Description |
| product_idrequired |
path · string |
|
Request body
Responses
200
Produk ter-update
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
DELETE
/admin/products/{product_id}
🔒 Auth required
Hapus produk
handler.DeleteProduct. Soft delete kalau produk sudah ada di transaksi historis. Returns HTTP 200 { success:true } via response.NoContent() (BUKAN 204).
Parameters
| Name | In · Type | Description |
| product_idrequired |
path · string |
|
Responses
PUT
/admin/products/{product_id}/recipe
🔒 Auth required
Replace recipe produk
handler.UpdateProductRecipe. Mengganti seluruh recipe array (bukan merge). Returns HTTP 200 { success:true } via response.NoContent() (BUKAN 204).
Parameters
| Name | In · Type | Description |
| product_idrequired |
path · string |
|
Request body
| Field | Type | Description |
| reciperequired |
array<object> |
Array ingredient baru. Mengganti seluruh recipe yang ada. |
{
"recipe": [
null
]
}
Responses
200
Recipe ter-update. response.NoContent() returns HTTP 200 { success:true }.
DELETE
/admin/owners/{owner_id}/operators/{operator_id}
🔒 Auth required
Hapus operator atas permintaan merchant
handler.DeleteOperator. Admin hanya bisa REMOVE operator on request. Tidak ada request body — handler tidak membaca body (admin/handler.go). Returns HTTP 200 { success:true } via response.NoContent() (BUKAN 204).
Parameters
| Name | In · Type | Description |
| owner_idrequired |
path · string |
|
| operator_idrequired |
path · string |
|
Responses
200
Operator dihapus. response.NoContent() returns HTTP 200 { success:true }.
GET
/admin/transactions
🔒 Auth required
List transaksi cross-merchant
handler.ListTransactions. Filter yang diproses: status (string), business_id (uuid), start_date (YYYY-MM-DD, filter created_at >= start), end_date (YYYY-MM-DD, filter created_at < end+1day), page (default 1), limit (default 20, max 100).
Parameters
| Name | In · Type | Description |
| status |
query · string |
Filter status transaksi (string bebas, tidak divalidasi enum di handler). |
| business_id |
query · string |
Filter per business. |
| start_date |
query · string |
Format YYYY-MM-DD. created_at >= start_date. |
| end_date |
query · string |
Format YYYY-MM-DD. created_at < end_date + 1 hari. |
| page |
query · integer |
|
| limit |
query · integer |
|
Responses
200
List transaksi cross-merchant berpaginasi
| Field | Type | Description |
| success |
boolean |
|
| data |
object |
|
| error |
string |
|
POST
/admin/transactions/{transaction_id}/void
🔒 Auth required
Void transaksi (admin)
handler.VoidTransaction. Tidak ada request body — handler tidak membaca body. Returns HTTP 200 { success:true } via response.NoContent() (BUKAN 204). 409 dikembalikan jika transaksi bukan status PENDING/CONFIRMED (tidak bisa di-void).
Parameters
| Name | In · Type | Description |
| transaction_idrequired |
path · string |
|
Responses
200
Transaksi di-void. response.NoContent() returns HTTP 200 { success:true }.
409
Transaksi tidak bisa di-void (sudah VOIDED atau status tidak valid)