Core Architecture Diagram Illustrating primary system components, service boundaries, client request pathways, and infrastructure layers:
Data Modeling & Schema Diagram Outlining the relational schema, entity relationships, foreign key constraints, and data hierarchy:
The platform authentication suite exposes endpoints for session management and credential updates:
POST /login— Acceptsemailandpasswordto generate session tokens.POST /refresh— Acceptsrefresh_tokenand performs atomic token rotation.POST /password-reset— Acceptsemailto generate a secure reset token.POST /password-reset/confirm— Acceptstoken,password, andpassword_confirm.
- Token Storage: Refresh tokens and password-reset tokens are stored exclusively as SHA-256 digests.
- Revocation: A successful password reset consumes the reset token and immediately revokes all active refresh tokens associated with the user account.
- Access Scope: Authenticated users can list accounts or search by email/username. Reading or mutating specific user IDs (including password updates) is strictly restricted to the resource owner.
- Admin Isolation: The
adminrole executes without privileged bypasses in the standard client API. Administrative actions belong strictly within the/api/v1/adminroute boundary.
Valid JSON requests containing invalid fields return an HTTP 400 Bad Request. Error payloads mirror request field keys:
{
"error": "validation_failed",
"fields": {
"email": "must be a valid email address",
"password": "must be between 8 and 72 bytes"
}
}
- Malformed Payload: Unparseable JSON returns an
invalid_jsonresponse code. - Domain Errors: Authentication failures or expired credentials return non-field domain error objects.
The user collection endpoint utilizes opaque cursor pagination:
GET /api/v1/users?limit=20
GET /api/v1/users?limit=20&cursor=<next_cursor>
- Limit Constraints: Defaults to
20(minimum1, maximum100). - Response Format:
{
"users": [],
"next_cursor": "eyJjcmVhdGVkX2F0IjoiLi4uIiwiaWQiOiIuLi4ifQ"
}
- Clients must pass
next_cursorback unmodified. The key is omitted on the terminal page.
Authenticated users can manage a courier profile under /api/v1/couriers:
| Endpoint | Method | Payload / Parameters | Description |
|---|---|---|---|
/api/v1/couriers |
POST |
phone_number (E.164), vehicle_type |
Registers caller as a courier. |
/api/v1/couriers/me |
GET |
— | Retrieves caller's courier profile. |
/api/v1/couriers/me |
PATCH |
phone_number, vehicle_type |
Updates courier attributes. |
/api/v1/couriers/me/availability |
PUT |
available (boolean) |
Updates courier online status. |
- Supported Vehicles:
foot,bicycle,scooter,motorcycle,car. - State Transition: Profile registration atomically transitions the user's role to
courier. Access tokens issued immediately prior to registration maintain valid scope to manage the newly assigned profile.
Organization hierarchy and access controls managed under /api/v1/merchants:
POST /api/v1/merchants— Registers a new merchant organization and assigns the caller asowner.GET /api/v1/merchants— Lists the caller's organization memberships using cursor pagination.GET /api/v1/merchants/:id— Returns organization details (restricted to active members).PATCH /api/v1/merchants/:id— Renames the merchant organization (restricted toownerormanager).
Security Note: Authorization is verified dynamically against active database memberships rather than static JWT claims. Non-member access attempts return
not_foundto prevent resource enumeration.
Nested restaurant locations inherit access controls from the parent merchant:
POST /api/v1/merchants/:merchant_id/restaurants
GET /api/v1/merchants/:merchant_id/restaurants
GET /api/v1/merchants/:merchant_id/restaurants/:restaurant_id
PATCH /api/v1/merchants/:merchant_id/restaurants/:restaurant_id
Each location requires an IANA timezone identifier and an optional address structure. owner and manager roles possess mutation permissions; all organization members retain read access.
Draft menu entities reside nested under restaurant resource endpoints:
/api/v1/merchants/:merchant_id/restaurants/:restaurant_id/menus
/api/v1/merchants/:merchant_id/restaurants/:restaurant_id/menus/:menu_id/sections
/api/v1/merchants/:merchant_id/restaurants/:restaurant_id/menus/:menu_id/sections/:section_id/items
- Currency & Pricing: Menus define a 3-letter ISO currency code. Prices are stored as exact integer values in minor units (
price_minor). - Ordering & Status: Sections and items enforce deterministic
positionintegers. Item availability can be toggled viaavailableboolean flags.
PUT .../menus/:menu_id/publication
POST .../menus/:menu_id/schedules
PATCH .../menus/:menu_id/schedules/:schedule_id
POST .../restaurants/:restaurant_id/public-link
GET /api/v1/public/restaurants/:restaurant_id/menu
- Publication Lifecycle: Tconfigured via
draftorpublished. Menu entity must contain at least one item prior to publishing. - Scheduling Rules: Operating windows enforce Sunday-based indexes (
0–6). Minutes are calculated from0to1439(start_minute) and1to1440(end_minute), operating as half-open intervals evaluated against the restaurant's configured IANA timezone. Overnight shifts must be split across midnight boundaries. - Dynamic Links: Integrates with dynamic link services to generate short-links and QR assets. If
DUB_API_KEYis not provided, menu operations remain functional while link generation endpoints yieldqr_provider_unavailable. - Public API:
GET /api/v1/public/restaurants/:restaurant_id/menurequires no authentication and returns currently published menus and active items.
To configure password-reset email delivery, supply the following environment variables:
MAILTRAP_API_TOKEN=your_api_token
MAILTRAP_PASSWORD_RESET_TEMPLATE_UUID=your_template_uuid
MAILTRAP_FROM_EMAIL=noreply@yourdomain.com
MAILTRAP_FROM_NAME="Identity Service"
MAILTRAP_SEND_URL=https://send.api.mailtrap.io/api/send
- Both
MAILTRAP_API_TOKENandMAILTRAP_PASSWORD_RESET_TEMPLATE_UUIDmust be configured concurrently. Production environments will fail startup checks if these are missing. - Email templates receive
user_emailandpass_reset_linkvariable injections. - Reset tokens expire exactly 1 hour after generation.
- Development Fallback: In non-production environments where mailer parameters remain unset, the password-reset endpoint directly returns
reset_token,reset_url, andexpires_atin the JSON response body to streamline local testing workflows.