API (api-v2)
Customers, staff & shop
The CRM, staff and role administration, the in-app notification inbox, and onboarding lookups.
Customers
Tables:
customer(with nickname)customer_address,customer_tagshop_customer: links a storefront login to a CRM row
| Route | Guard |
|---|---|
/merchant/customer/v1/get-shop-customers | perm customers.view |
create-customer | wide customers.write |
get-customer, update-customer, delete-customer, create/update/delete-customer-address, set-default-customer-address | oScoped |
- There's no deduplication; the POS warns on duplicate phones instead.
- Editing a customer never rewrites order snapshots.
- Order count and total spent are derived.
- Deleting a customer keeps their orders.
- POS-side routes are on POS & shifts.
Staff & roles
Tables:
shop_staff: birthday and emergency contact were added in #80shop_staff_role,shop_staff_role_permission,shop_staff_role_categoryshop_staff_assignment+shop_staff_assignment_location: several roles per person, each scoped to branches (#73)shop_staff_location: legacystaff_shift_rule: the weekly rota (API only; the dashboard UI was removed)staff_discount_pin
| Route | Guard |
|---|---|
/merchant/staff/v1/get-shop-staff, get-shop-staff-roles | perm staff.manage |
create/update/delete-staff, create/update/delete-staff-role, set-staff-shifts, PIN routes | owner |
get-staff-permissions | authenticated (the permission catalogue, grouped) |
Rules:
canSignInlinks the staff row to ausersaccount and requires an email.- An assignment's
modeisallorspecificlocations. The dashboard saves "every branch ticked" asall, so future branches are included. - A role that staff still hold can't be deleted (409). The same role can't be assigned twice to one person.
- A role scoped to specific branches can't perform shop-wide writes (prices, categories, discounts, sections, customers), because those need
requireShopWidePermission. - Catalogue scope:
shop_staff_role_categoryroots, including descendants. Empty means the whole catalogue. - Rota: a shift that ends before it starts crosses midnight, and overlapping shifts are a 409.
No get-staff-by-id
There's no single-staff read. The dashboard's staff editor finds the record by searching the first page of staff, so a deep link to a staff member beyond page one won't load.
Notifications
Tables: notification, merchant_notification, merchant_notification_preference
| Route | Guard |
|---|---|
/merchant/notifications/v1/list, unread-count, mark-read, mark-all-read, preferences, update-preferences | perm orders.view |
This is an in-app inbox only. Only order cancellation produces notifications today. There's no push, email or socket delivery, and the dashboard has no inbox UI yet.
Onboarding & lookups
Table: shop_dismissed_alert
| Route | Guard |
|---|---|
get-languages, get-currencies, get-weight-units, get-dimension-units | authenticated |
get-guide, get-shop-alerts, dismiss-shop-alert | owner |
- The setup guide has seven completion flags. Alerts appear only once the guide is complete.
- The dashboard sends
x-language-id(looked up fromget-languages, not hard-coded) on every request, and names come back already localized.