Version: Phase 136
Transport: HTTP Batch (POST /api/trpc)
Auth: Session cookie (session=<jwt>) for agent procedures; Manus OAuth for admin SSO
All procedures use tRPC v11 with Superjson serialization.
Type: Query | Auth: Public
Returns the currently authenticated Manus OAuth user or null.
Type: Mutation | Auth: Protected
Clears the Manus OAuth session cookie.
Type: Mutation | Auth: Public
Input: { agentCode: string, pin: string }
Returns: { success: true, agentCode: string, name: string, role: string }
Sets a session HttpOnly cookie with a 12-hour JWT.
Type: Mutation | Auth: Agent
Clears the agent session cookie.
Type: Query | Auth: Agent
Returns: { agentCode, name, floatBalance, commissionBalance, loyaltyPoints, tier, role, isActive, location }
Type: Mutation | Auth: Public
Input: { agentCode, name, pin, phone, location? }
Type: Mutation | Auth: Agent
Input:
{
type: "cash_in" | "cash_out" | "transfer" | "airtime" | "bills" | "card" | "qr" | "nfc",
amount: number, // in kobo (₦1 = 100 kobo)
customerPhone?: string,
customerName?: string,
recipientAccount?: string,
recipientBank?: string,
billProvider?: string,
billAccountNumber?: string,
channel?: string,
metadata?: Record<string, unknown>
}Returns: { txRef, type, amount, fee, commission, loyaltyPoints, status, timestamp }
Side effects:
- Deducts/credits float balance
- Accrues commission
- Awards loyalty points
- Publishes
tx.createdto Kafka - Publishes to Fluvio fraud stream
- Tries TigerBeetle sidecar first, falls back to PostgreSQL
Type: Query | Auth: Agent
Input: { page?: number, limit?: number, type?: string, status?: string }
Returns: { transactions: Transaction[], total: number, page: number }
Type: Query | Auth: Agent
Input: { txRef: string }
Returns: Transaction | null
Type: Mutation | Auth: Admin
Input: { txRef: string, reason: string }
Returns: { success: true, reversalRef: string }
Type: Query | Auth: Agent
Input: { page?: number, limit?: number, status?: string, severity?: string }
Type: Mutation | Auth: Agent
Input: { txRef, severity, type, customerPhone, amount, reason }
Type: Mutation | Auth: Admin
Input: { alertId: number, status: "open" | "investigating" | "resolved" | "dismissed" }
Type: Query | Auth: Agent
Returns: { points, tier, nextTier, pointsToNextTier, history: LoyaltyEvent[] }
Type: Mutation | Auth: Agent
Input: { challengeId: string }
Type: Mutation | Auth: Agent
Input: { rewardId: string, pointsCost: number }
Type: Mutation | Auth: Agent
Returns: { sessionId: string }
Type: Mutation | Auth: Agent
Input: { sessionId: string, content: string, role: "agent" | "support" }
Type: Query | Auth: Agent
Input: { sessionId: string }
Returns: ChatMessage[]
Type: Query | Auth: Admin
Input: { page?: number, limit?: number, search?: string, role?: string, isActive?: boolean }
Type: Mutation | Auth: Admin
Input: { agentCode: string, role: "admin" | "agent" | "supervisor" }
Type: Mutation | Auth: Admin
Input: { agentCode: string, isActive: boolean }
Type: Mutation | Auth: Admin
Input: { agentCode: string, amount: number, operation: "credit" | "debit" }
Type: Query | Auth: Admin
Input: { status?: "pending" | "approved" | "rejected" }
Type: Mutation | Auth: Admin
Input: { requestId: number }
Type: Mutation | Auth: Admin
Input: { requestId: number, reason: string }
Type: Query | Auth: Agent
Input: { page?: number, limit?: number }
Type: Query | Auth: Admin
Input: { page?: number, limit?: number, actor?: string, action?: string }
Type: Mutation | Auth: Admin
Returns: { agentsProcessed, totalVolume, totalCommission, smsCount, errors }
Type: Query | Auth: Admin
Returns: { timestamp, agentsProcessed, totalVolume, errors } | null
Type: Query | Auth: Admin
Input: { startDate?: string, endDate?: string, agentCode?: string }
Returns: CSV string (download via Blob in browser)
Type: Mutation | Auth: Public
Input: { agentCode: string, phone: string }
Type: Mutation | Auth: Public
Input: { agentCode: string, otp: string, newPin: string }
Type: Mutation | Auth: Agent
Input: { phone: string, txRef: string, amount: number, type: string, timestamp: string }
Type: Mutation | Auth: Public (API key)
Input: { terminalId, carrier, rssi, latency, packetLoss, latE6?, lonE6?, txRef? }
Type: Query | Auth: Admin
Input: { terminalId?: string, limit?: number }
Type: Query | Auth: Admin
Input: { hours?: number, carrier?: string }
Returns: ProbeGeoPoint[] with latE6, lonE6, rssi, carrier, terminalId
Type: Mutation | Auth: Public (API key)
Input: { terminalId, fromSlot, toSlot, reason, latencyMs, lossX10, txRef? }
Type: Query | Auth: Admin
Input: { terminalId?: string, limit?: number }
Type: Mutation | Auth: Protected
Input: { title: string, content: string }
| Code | Message | Description |
|---|---|---|
UNAUTHORIZED |
Please login (10001) | No valid session cookie |
FORBIDDEN |
You do not have required permission (10002) | Insufficient role |
BAD_REQUEST |
Validation error | Zod schema validation failed |
NOT_FOUND |
Resource not found | Entity does not exist |
INTERNAL_SERVER_ERROR |
Internal error | Unexpected server error |
| Endpoint | Limit | Window |
|---|---|---|
/api/trpc/transactions.create |
30 req/min | per agent |
/api/trpc/agent.login |
10 req/min | per IP |
/api/trpc/pinReset.* |
5 req/min | per IP |
All other /api/trpc/* |
100 req/min | per agent |
fraud:new— new fraud alert{ id, severity, type, amount, agentCode, timestamp }fraud:updated— status update{ id, status }
chat:message— new message{ sessionId, content, role, timestamp }chat:typing— typing indicator{ sessionId, isTyping }
terminal:heartbeat— terminal online{ terminalId, timestamp }terminal:offline— terminal offline{ terminalId, lastSeen }