# Backend overview Living spec for the **shared platform API** consumed by Gishen-B2B. This repo implements mocks typed to this contract; the backend service is consolidated later from B2B, Ecom, Mobile, and Admin spec sheets. **Workspace:** `/Users/kirukib/Desktop/Yaltopia Project/Gishen-B2B` ## Base URL (future) ``` https://api.gishenpharmacy.org/v1 ``` Mock adapters in this repo live under `src/lib/api/` and `src/mocks/`. ## Tenancy - Every authenticated request (except public registration) is scoped to an **organisation** via `orgId` on the session or explicit path prefix. - Org lifecycle: `pending_activation` → `active` → `suspended` | `closed`. - Pending orgs: SUPER_USER may read status and submitted registration; **cannot** create members, packages, or migration commits until Admin activates. ``` X-Org-Id: org_01H... ``` For single-org sessions, `orgId` is implicit from JWT/session. Multi-org users (rare in B2B) pass `X-Org-Id`. ## Authentication | Mode (this phase) | Description | |-------------------|-------------| | Mock demo | `/login` offers **Google, email, phone, Telegram** (UI mocked) plus a **test-user dropdown**; any path sets session via `demo_user_id` | | Production (future) | Google OIDC, email magic-link/OTP, SMS OTP, Telegram Login Widget / Mini App — see [`endpoints/auth.md`](endpoints/auth.md) | Demo personas: `user_super`, `user_hr`, `user_fin`, `user_mem` — see `GET /v1/auth/demo-profiles`. ### Session shape See [`entities/session-user.md`](entities/session-user.md). Includes `avatar_url`, `roles[]`, and `active_role` for multi-role demo users. ### JWT claims (future) ```json { "sub": "usr_01H...", "org_id": "org_01H...", "roles": ["HR_ADMIN", "FINANCE"], "active_role": "HR_ADMIN", "locale": "am", "iat": 1710000000, "exp": 1710003600 } ``` ## RBAC matrix | Resource / action | SUPER_USER | HR_ADMIN | FINANCE | MEMBER | |-------------------|:----------:|:--------:|:-------:|:------:| | Own profile (`/v1/me/profile`) | ✓ | ✓ | ✓ | ✓ | | Org profile (commercial read) | ✓ | ✓ (active only) | ✓ (active only) | — | | Org registration submit | ✓ (public path) | — | — | — | | Departments CRUD | ✓ | ✓ | read | — | | Members CRUD / import / invite | ✓ | ✓ | read metadata | own row | | Pharmacy ID card (print / QR payload) | ✓ | ✓ | — | own only | | Packages CRUD | ✓ | ✓ | read | own assignment | | Migration jobs | ✓ | ✓ | read history | — | | Finance spend / statements | ✓ | read aggregates | ✓ | — | | Finance approvals | ✓ | — | ✓ | — | | Member allowance / orders | ✓ | — | — | ✓ | | Prescriptions (clinical) | ✓ full | **withheld** | **withheld** | own only | | Settings / verification | ✓ | ✓ | — | — | **Clinical withhold:** HR_ADMIN and FINANCE never receive prescription images, medicine line items, dosages, prescriber notes, or diagnosis-related fields. Aggregates (e.g. “Rx order — ETB 450, category: chronic”) may appear in finance spend. SUPER_USER bypasses withhold for support/disputes. ## Error model All errors return: ```json { "error": { "code": "VALIDATION_FAILED", "message": "Human-readable summary", "details": [ { "field": "email", "code": "INVALID_FORMAT", "message": "..." } ], "request_id": "req_01H..." } } ``` ### Standard HTTP status codes | Status | Usage | |--------|-------| | 400 | Validation failed, malformed JSON | | 401 | Missing or expired session | | 403 | Authenticated but role/org forbids action; clinical withhold | | 404 | Resource not found or not visible in tenant | | 409 | Conflict (duplicate member, job already committed) | | 422 | Business rule violation (org pending, credit exceeded) | | 429 | Rate limit | | 500 | Internal error | ### Error codes (catalog) | Code | HTTP | Description | |------|------|-------------| | `UNAUTHENTICATED` | 401 | No valid session | | `FORBIDDEN` | 403 | Role cannot perform action | | `CLINICAL_WITHHELD` | 403 | Clinical field requested by HR/Finance | | `ORG_PENDING` | 422 | Org not yet activated by Admin | | `ORG_SUSPENDED` | 422 | Org suspended | | `VALIDATION_FAILED` | 400 | Field-level validation | | `DUPLICATE_MEMBER` | 409 | Phone/email/employee_id conflict | | `CREDIT_LIMIT_EXCEEDED` | 422 | Would exceed org credit | | `MIGRATION_JOB_LOCKED` | 409 | Job already committed or rolled back | | `INVITE_EXPIRED` | 422 | Invite token expired | | `NOT_FOUND` | 404 | Generic not found | ## Pagination List endpoints accept: ``` ?page=1&page_size=25&sort=-created_at&q=search ``` Response envelope: ```json { "data": [ ... ], "pagination": { "page": 1, "page_size": 25, "total_items": 142, "total_pages": 6 } } ``` Default `page_size`: 25. Max: 100. ## Filtering & date ranges List endpoints support search + filters (portal toolbar / modal when >3 filters): | Domain | Query params (common) | |--------|------------------------| | Members | `q`, `status`, `department_id`, `package_id`, `member_type`, `has_dependants` | | Finance spend | `from`, `to`, `department_id`, `category`, `coverage`, `member_id`, `approval_status` | | Statements | `year`, `status` | | Migration | `status`, `dataset_type`, `has_errors`, `created_by` | | Packages / departments | `q`, `status` / site | ``` ?from=2026-01-01&to=2026-01-31&department_id=dept_01H... ``` ISO 8601 dates in org timezone (default `Africa/Addis_Ababa`). ## Pharmacy ID card (platform contract) Members carry a printable verification card used at Gishen pharmacy / Ecom checkout: | Field | Purpose | |-------|---------| | `avatar_url` | Photo on card + portal UI | | `card_id` | Human-readable card number (e.g. `GSH-1042`) | | `verification_id` | Org verification token encoded in QR | **QR payload (canonical):** ``` gishen://member/{member_id}?v={verification_id} ``` Admin / POS scans the QR to resolve the member for covered purchase. Clinical detail is **not** on the card. See [`endpoints/members.md`](endpoints/members.md#get-v1-membersidid-card) and [`../coordination/gishen-admin.md`](../coordination/gishen-admin.md). ## File uploads Multipart for Rx images and migration files: ``` POST /v1/... Content-Type: multipart/form-data ``` Max file size (Rx page): 10 MB. Accepted: `image/jpeg`, `image/png`, `application/pdf`. Migration uploads: 50 MB `.xlsx`, `.xls`, `.csv`. ## Idempotency Mutating endpoints that create billing or migration commits accept: ``` Idempotency-Key: ``` Duplicate keys within 24h return the original response. ## Webhooks / events Domain events published to platform bus; see [`events/README.md`](events/README.md). B2B emits and consumes events for org registration, migration, and prescriptions. ## Environment (Vercel frontend) | Variable | Purpose | |----------|---------| | `NEXT_PUBLIC_API_BASE_URL` | Platform API base (mock or real) | | `NEXT_PUBLIC_MOCK_AUTH` | `true` for demo role selector | | `NEXT_PUBLIC_DEFAULT_LOCALE` | `en` | Document production URL in this file when first Vercel deploy is finalized. ## Related docs - [Entities index](entities/README.md) - [Endpoints index](endpoints/README.md) - [Events](events/README.md) - [Features INDEX](../features/INDEX.md)