Services¶
Web - port 3000¶
Next.js 15 app serving both the user-facing product and the admin console. Routed through nginx — the user app is at /, the admin console at /app-admin.
User pages
| Route | Description |
|---|---|
/ |
Dashboard — wallet balance, recent activity |
/wallet |
Wallet details, balance chart, transaction limits |
/transactions |
Transaction history with filtering |
/analytics |
Spend by category, top merchants, monthly budgets |
/social |
Contacts, money requests, bill splits |
/notifications |
Activity feed (SSE-backed real-time updates) |
/verification |
KYC tier status and document upload |
Admin pages (requires role=ADMIN JWT)
| Route | Description |
|---|---|
/app-admin |
User search, freeze/unfreeze, role management |
/app-admin/kyc |
KYC review queue and user lookup |
/app-admin/fraud |
Fraud case review queue |
/app-admin/transactions |
Transaction list, reverse, force-complete |
/app-admin/system |
Global transaction limit configuration |
/app-admin/audit |
Audit log viewer |
Stack: Next.js 15, React, TanStack Query, Tailwind CSS, Shadcn UI
Notes: All API calls are proxied through nginx at http://localhost. NEXT_PUBLIC_API_URL sets the API base URL.
Auth - port 4001¶
Handles user registration, login, JWT issuance, and RBAC. Built with FastAPI + SQLAlchemy, using sreekarnv-fastauth for auth primitives.
Key endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/auth/register |
Register new user |
| POST | /api/v1/auth/login |
Login, returns JWT |
| POST | /api/v1/auth/refresh |
Refresh access token |
| POST | /api/v1/auth/logout |
Invalidate session |
| GET | /.well-known/jwks.json |
Public key for JWT verification |
Database: mint_auth - users, roles, refresh tokens, JWKS
Kafka out: auth.events
Notes: RSA key pair at ./keys/ - mounted read-only in production. Email verification via SMTP (MailHog in dev).
Wallet - port 4002, gRPC 50051¶
Manages user wallets and balance history. Exposes a gRPC interface for the transactions service to debit/credit funds atomically. Built with FastAPI + SQLAlchemy.
Key endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/wallet/ |
Get current balance |
| GET | /api/v1/wallet/history |
Balance change history |
| POST | /api/v1/wallet/freeze |
Freeze wallet (admin) |
| POST | /api/v1/wallet/unfreeze |
Unfreeze wallet (admin) |
gRPC methods: DebitWallet, CreditWallet, GetBalance, GetWallet, FreezeWallet, UnfreezeWallet
Database: mint_wallet - wallets, balance history
Kafka in: auth.events (create wallet on signup), transaction.events (log changes)
Kafka out: wallet.events
Notes: Runs with --workers 1 because the gRPC server binds inside the FastAPI lifespan.
Transactions - port 4003¶
Core transaction engine. Validates, scores, and settles transfers and top-ups. Enforces idempotency and KYC limits.
Key endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/transactions/transfer |
Transfer funds (idempotent) |
| POST | /api/v1/transactions/topup |
Top up wallet |
| GET | /api/v1/transactions/ |
Transaction history |
| GET | /api/v1/transactions/:id |
Get transaction |
Database: mint_txns - transactions, idempotency keys
gRPC clients: fraud (score), kyc (limits), wallet (settle)
Kafka in: social.events (accepted money request → auto-transfer)
Kafka out: transaction.events
Notes: Uses Idempotency-Key header; response cached in Redis for 24 h. State machine: PENDING → PROCESSING → COMPLETED / FAILED / CANCELLED / REVERSED.
Fraud - gRPC only, port 50052¶
Rules-based fraud scoring engine. Called synchronously by transactions service before every settlement. No HTTP API.
gRPC method: ScoreTransaction → { decision: ALLOW|REVIEW|BLOCK, score: 0-100, rulesFired, reason }
Database: mint_fraud - fraud cases, user transfer statistics
Notes: Thresholds configured via FRAUD_BLOCK_THRESHOLD / FRAUD_REVIEW_THRESHOLD env vars. Blocked transactions never reach wallet settlement.
KYC - port 4004, gRPC 50053¶
Identity verification tiers and document management. Enforces per-transaction and daily/monthly spend limits based on user tier.
Key endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/kyc/ |
Get KYC profile |
| GET | /api/v1/kyc/limits |
Get tier limits |
| POST | /api/v1/kyc/submit |
Submit documents |
| POST | /api/v1/kyc/webhook |
Receive verification result (Persona) |
gRPC methods: GetUserTier, GetLimits, GetProfile, ListPendingQueue
Database: mint_kyc - profiles, tiers, document metadata
Object storage: MinIO bucket mint-kyc-docs (uploaded documents)
Kafka in: auth.events (create profile on signup), admin.events (approve/reject KYC)
Kafka out: kyc.events
Tiers: UNVERIFIED → BASIC → VERIFIED. Tiers can be frozen by admin.
Analytics - port 4005¶
Spend categorization and budget tracking. Powered entirely by Kafka events - no writes from HTTP clients.
Key endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/analytics/insights |
Monthly spend by category |
| GET | /api/v1/analytics/summary |
Transaction count and total spend |
| GET | /api/v1/analytics/top-merchants |
Top merchants this month |
| GET | /api/v1/analytics/budgets |
List budgets |
| POST | /api/v1/analytics/budgets |
Create / update budget |
| DELETE | /api/v1/analytics/budgets/:id |
Delete budget |
Database: mint_analytics - spend events, monthly aggregates, budgets
Kafka in: transaction.events (completed non-topup transactions)
Kafka out: analytics.events (budget threshold alerts)
Categories: FOOD, TRANSPORT, ENTERTAINMENT, UTILITIES, OTHER — auto-classified from merchant name and description.
Notifications - port 4006¶
Persistent in-app notifications with real-time delivery via Server-Sent Events.
Key endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/notifications/ |
List recent notifications |
| POST | /api/v1/notifications/:id/read |
Mark as read |
| POST | /api/v1/notifications/read-all |
Mark all as read |
| GET | /api/v1/notifications/unread-count |
Unread count |
| GET | /api/v1/notifications/stream |
SSE stream (real-time) |
Database: mint_notifications - notifications, read status
Kafka in: all topics (auth, wallet, transaction, kyc, social, analytics, webhook.events)
Notes: nginx disables proxy buffering for the SSE endpoint so events stream immediately.
Social - port 4007¶
Contacts, money requests (with expiry), and bill splits.
Key endpoints
| Method | Path | Description |
|---|---|---|
| GET/POST | /api/v1/social/contacts |
List / add contacts |
| DELETE | /api/v1/social/contacts/:id |
Remove contact |
| GET/POST | /api/v1/social/requests |
List / create money requests |
| POST | /api/v1/social/requests/:id/accept |
Accept request |
| POST | /api/v1/social/requests/:id/decline |
Decline request |
| DELETE | /api/v1/social/requests/:id |
Cancel request |
| GET/POST | /api/v1/social/splits |
List / create bill splits |
| POST | /api/v1/social/splits/:id/pay |
Pay participant share |
Database: mint_social - contacts, money requests, splits, split participants
Kafka out: social.events
Queue: BullMQ (Redis) - scheduled jobs for request expiry (default 7 days).
Notes: Accepting a money request publishes an event; the transactions service auto-initiates the transfer.
Webhook - port 4008¶
User-registered webhooks with signed delivery and retry.
Key endpoints
| Method | Path | Description |
|---|---|---|
| GET/POST | /api/v1/webhooks/ |
List / register endpoints |
| GET/PATCH/DELETE | /api/v1/webhooks/:id |
Manage endpoint |
| GET | /api/v1/webhooks/:id/deliveries |
Delivery log |
Database: mint_webhook - endpoints (URL, subscribed events, signing key), deliveries
Kafka in: transaction.events, wallet.events, social.events, analytics.events
Queue: BullMQ (Redis) - delivery with exponential backoff retry.
Notes: Payloads are HMAC-signed. Delivery log captures HTTP status and response body.
Admin - port 4009¶
Privileged operational API. All endpoints require role=ADMIN in the JWT. Backs the admin console at /app-admin.
Key endpoints
| Method | Path | Description |
|---|---|---|
| GET | /admin/users/search |
Search users by email |
| GET | /admin/users/:id |
Get user profile (cross-service) |
| POST | /admin/users/:id/freeze |
Freeze account + wallet |
| POST | /admin/users/:id/unfreeze |
Unfreeze account |
| PATCH | /admin/users/:id/role |
Update user role |
| GET | /admin/kyc/queue |
Pending KYC review queue |
| GET | /admin/kyc/user/:userId |
Get KYC profile by user |
| POST | /admin/kyc/:profileId/approve |
Approve KYC submission |
| POST | /admin/kyc/:profileId/reject |
Reject KYC submission |
| GET | /admin/fraud/queue |
Pending fraud review queue |
| POST | /admin/fraud/:caseId/approve |
Approve flagged transaction |
| POST | /admin/fraud/:caseId/block |
Block flagged transaction |
| GET | /admin/transactions |
List transactions (filterable) |
| POST | /admin/transactions/:id/reverse |
Reverse a transaction |
| POST | /admin/transactions/:id/force-complete |
Force-complete a stuck transaction |
| GET/PATCH | /admin/system/limits |
View and update global limits |
Kafka out: admin.events (every action logged and consumed by kyc + audit)
Notes: KYC approve/reject flow: admin emits a Kafka event → KYC service consumes it and updates the profile/tier.
Audit - port 4010¶
Immutable, append-only audit log. Every event from every service lands here.
Key endpoints
| Method | Path | Description |
|---|---|---|
| GET | /internal/audit |
Query log (actorId, action, date range, pagination) |
Database: mint_audit - append-only log table (PostgreSQL trigger prevents UPDATE/DELETE)
Kafka in: all topics
Notes: Preserves OpenTelemetry trace ID for cross-service correlation. Indexed on actorId, action, and createdAt.