Gishen Admin — Backend Spec Sheet
Repo: Gishen-Admin
Audience: Shared backend team (consolidate with Ecom / Mobile / B2B sheets later)
Base URL (TBD): https://api.gishen.example/v1
Auth: Bearer JWT for staff roles
Mocks: Admin SPA uses in-memory mocks until VITE_USE_MOCKS=false and VITE_API_BASE_URL point here.
Changelog
| Date |
Change |
| 2026-08-06 |
Scaffold: conventions, roles, auth, empty module sections |
| 2026-08-06 |
Shell + RBAC + preferredLocale |
| 2026-08-06 |
Pharmacist: prescriptions, branch orders |
| 2026-08-06 |
Stock + Procurement |
| 2026-08-06 |
Finance, orgs create + pending self-reg approval |
| 2026-08-06 |
Marketing CRM / loyalty / campaigns |
| 2026-08-06 |
Dispatch board + Telegram bot events |
| 2026-08-06 |
Super Admin team/inbox/audit/settings + FAQ bilingual |
| 2026-08-06 |
Data migration import jobs |
| 2026-08-06 |
Deployed Admin SPA: https://gishen-admin.vercel.app |
| 2026-08-06 |
List UX: filters + create pages for orders, Rx, orgs, stock, team, procurement; POST create sketches below |
| 2026-08-06 |
Finance: gateway enable/disable moved to /admin/finance/gateways UI route /finance/gateways |
Conventions
| Item |
Convention |
| Content type |
application/json (multipart for imports / uploads) |
| IDs |
UUID v4 |
| Money |
Integer ETB (major units for Admin UI demos; align platform-wide later) |
| Errors |
{ "error": { "code": string, "message": string, "details"?: object } } |
| Pagination |
?page=&limit= → { data, meta: { page, limit, total } } |
Staff roles
pharmacist · stock_manager · procurement · finance · marketing_manager · operations · super_admin
Pharmacist (and optionally stock) sessions include branchId.
Auth & me
| Method |
Path |
Roles |
Description |
| POST |
/auth/staff/login |
— |
Email/password → tokens + user |
| POST |
/auth/refresh |
refresh |
Rotate access |
| POST |
/auth/logout |
staff |
Revoke |
| GET |
/auth/me |
staff |
Current principal, roles, branchId |
| PATCH |
/users/me |
staff |
{ preferredLocale?: "en"|"am", name? } |
Login response sketch:
{
"accessToken": "...",
"refreshToken": "...",
"user": {
"id": "uuid",
"name": "Hana Pharmacist",
"email": "pharmacist@gishen.et",
"role": "pharmacist",
"branchId": "uuid",
"preferredLocale": "en"
}
}
Mock key: authStore.login → /auth/staff/login
Orders (branch inbox)
| Method |
Path |
Roles |
Description |
| GET |
/pharmacy/orders |
pharmacist, operations, super_admin |
?branchId= required for pharmacist; filters: status, fulfillment, channel, q |
| POST |
/pharmacy/orders |
pharmacist, operations, super_admin |
Staff-created order (call centre / walk-in) |
| POST |
/orders/:id/confirm |
pharmacist, super_admin |
Confirm stock |
| POST |
/orders/:id/assign-rider |
pharmacist, operations, super_admin |
{ riderId } |
| PATCH |
/orders/:id/status |
pharmacist, operations, super_admin |
Status lifecycle |
Mock: mocks/data.orders
Prescriptions
| Method |
Path |
Roles |
Description |
| GET |
/pharmacy/prescriptions |
pharmacist, super_admin |
Branch-scoped queue; filters: status, controlled, q |
| POST |
/pharmacy/prescriptions |
pharmacist, super_admin |
Staff Rx intake |
| POST |
/admin/prescriptions/:id/verify |
pharmacist, super_admin |
{ status: approved|queried|rejected, qtyAdjustments?, substitute?, notes? } |
| POST |
/pharmacy/prescriptions/:id/dispense |
pharmacist |
{ batch, quantity } stamped pharmacistId + time |
| GET |
/pharmacy/prescriptions/controlled-report |
pharmacist, super_admin |
Controlled-substance report |
| GET |
/pharmacy/refills |
pharmacist, super_admin |
Days-of-supply / script expiry oversight |
Stock & ERP
| Method |
Path |
Roles |
Description |
| GET |
/pharmacy/inventory |
stock_manager, pharmacist (read), procurement (read), super_admin |
Per-branch stock; filters: branchId, q, alert=low|mismatch |
| POST |
/pharmacy/inventory |
stock_manager, super_admin |
Add stock line |
| GET |
/integrations/erp/sync-status |
stock_manager, super_admin |
Last sync |
| GET |
/admin/stock/discrepancies |
stock_manager, super_admin |
Platform vs ERP |
| POST |
/admin/stock/discrepancies/:id/resolve |
stock_manager, super_admin |
One-click resolve |
| GET/PUT |
/admin/stock/field-ownership |
stock_manager, super_admin |
ERP vs platform field owners |
| POST |
/admin/stock/entry/suggest |
stock_manager, super_admin |
Fuzzy duplicate suggestions body { query } |
| GET |
/admin/stock/merge-proposals |
stock_manager, super_admin |
Merge queue |
| POST |
/admin/stock/merge-proposals/:id/approve |
stock_manager, super_admin |
Human approve; reversible 30d |
| GET |
/admin/stock/alerts |
stock_manager, super_admin |
Low stock / expiry watchlist |
| POST |
/admin/stock/transfers |
stock_manager, super_admin |
Create/approve transfer (owner) |
Procurement
| Method |
Path |
Roles |
Description |
| GET |
/admin/procurement/reports |
procurement, stock_manager, super_admin |
Dead-stock / fast-mover |
| GET |
/admin/procurement/reorder-recommendations |
procurement, super_admin |
From velocity |
| POST |
/admin/stock/transfers/request |
procurement, super_admin |
Request only; Stock Manager approves |
Finance & organisations
| Method |
Path |
Roles |
Description |
| GET |
/admin/finance/settlements |
finance, super_admin |
Daily by channel |
| GET |
/admin/finance/reconciliations |
finance, super_admin |
Matched to orders |
| GET/PUT |
/admin/finance/gateways |
finance, super_admin |
Enable/disable Chapa, ArifPay, Telebirr, M-Pesa |
| POST |
/admin/finance/refunds |
finance, super_admin |
Refund handling |
| GET |
/admin/finance/loyalty-liability |
finance, super_admin |
Monthly Birr liability |
| GET |
/admin/organisations |
finance, super_admin |
List |
| POST |
/admin/organisations |
finance, super_admin |
Create org from Admin (active) |
| GET |
/admin/organisations/pending |
finance, super_admin |
Self-register queue |
| POST |
/admin/organisations/:id/approve |
finance, super_admin |
Activate credit |
| POST |
/admin/organisations/:id/reject |
finance, super_admin |
Reject |
| GET |
/admin/organisations/:id/statements |
finance, super_admin |
Consolidated invoices |
Cross-repo: B2B portal POST /company/register creates status=pending. Admin approve activates credit.
Marketing
| Method |
Path |
Roles |
Description |
| GET |
/admin/crm/customers |
marketing_manager, super_admin |
Read profiles |
| GET |
/admin/crm/segments |
marketing_manager, super_admin |
Self-updating segments |
| GET/POST |
/admin/campaigns |
marketing_manager, super_admin |
Compose/schedule SMS/Telegram/push |
| GET/PUT |
/admin/loyalty/config |
marketing_manager, super_admin |
Earn/tiers/referral bonus |
| GET |
/admin/kpi/summary |
marketing_manager, finance, super_admin |
Scoped KPIs |
Dispatch & Telegram bot
| Method |
Path |
Roles |
Description |
| GET |
/dispatch/board |
operations, super_admin |
Paid + Rx-approved queue |
| POST |
/dispatch/trips |
operations, super_admin |
Batch fulfilments |
| POST |
/dispatch/trips/:id/assign |
operations, super_admin |
{ riderId } → emits Telegram manifest |
| POST |
/dispatch/bot/webhook |
service |
Bot status + PoD callbacks |
| GET |
/dispatch/intake |
operations, super_admin |
Partner/POS webhook monitor |
| POST |
/dispatch/riders |
operations, super_admin |
Link Telegram account |
Events: trip.assigned → bot push; trip.status_updated → board + customer tracking.
FAQ (bilingual)
| Method |
Path |
Roles |
Description |
| GET |
/admin/faq |
all staff |
Published articles |
| POST |
/admin/faq |
marketing_manager, super_admin |
Create |
| PUT |
/admin/faq/:id |
marketing_manager, super_admin |
Update / publish |
| DELETE |
/admin/faq/:id |
super_admin |
Delete |
Fields: title_en, title_am, body_en, body_am, category, published.
Team, inbox, audit, settings
| Method |
Path |
Roles |
Description |
| GET/POST |
/admin/users |
super_admin |
Staff + role + branch; list supports role, branchScoped, q |
| GET |
/admin/inbox/threads |
super_admin |
Chatwoot/Telegram/WhatsApp attached to customer |
| GET |
/admin/audit |
super_admin |
Audit log |
| GET/PUT |
/admin/settings |
super_admin |
Retention, access controls |
Data migration / imports
| Method |
Path |
Roles |
Description |
| GET |
/admin/imports/templates |
stock_manager, finance, operations, super_admin |
Entity schemas |
| POST |
/admin/imports |
same |
Multipart file upload |
| POST |
/admin/imports/:id/map |
same |
Column mapping (+ save template) |
| POST |
/admin/imports/:id/validate |
same |
Dry-run row errors |
| POST |
/admin/imports/:id/commit |
same |
Upsert commit |
| GET |
/admin/imports |
same |
Job history |
| CRUD |
/admin/imports/mapping-templates |
same |
Saved presets |
Entity templates: stock, catalog, hr_members, branches, riders, generic.
Open questions
- Money units: ETB major vs cents — keep consistent with Ecom/B2B sheets
- Driver channel assumption: Telegram bot (flag if SMS/WhatsApp preferred)
- Checkout entitlement-split: shared component across Ecom/Mobile/B2B (out of Admin UI)