# 04 — API Reference This document covers all Next.js route handlers (`app/api/`) and key server actions (`features/*/api/`). > **Authentication:** All protected endpoints require a valid Clerk session. Unauthenticated requests receive a `401 Unauthorized` response. Route-level protection is enforced by `proxy.ts` (Clerk middleware). --- ## Route Handlers (`app/api/`) ### `POST /api/uploadthing` **File:** `app/api/uploadthing/route.ts` **Purpose:** UploadThing file upload endpoint. Handles authenticated file uploads and triggers contract creation. **Constraints:** - Max file size: 32 MB - Accepted types: `application/pdf`, `image/jpeg`, `image/png`, `image/webp` - Authentication: Clerk session required **Flow:** 1. UploadThing middleware validates the session via Clerk 2. File is stored on UploadThing CDN 3. On completion, returns `{ fileUrl, fileName, fileSize, mimeType }` to the client --- ### `POST /api/webhooks/clerk` **File:** `app/api/webhooks/clerk/route.ts` **Purpose:** Receives Clerk lifecycle events and syncs user data to PostgreSQL. **Verification:** Request signature verified via Svix + `CLERK_WEBHOOK_SECRET` **Handled Events:** | Event | Action | |-------|--------| | `user.created` | Creates a new `User` record in PostgreSQL | | `user.updated` | Updates `email`, `firstName`, `lastName`, `imageUrl` | | `user.deleted` | Deletes `User` record (cascades to contracts, notifications) | **Response:** `200 OK` on success, `400 Bad Request` if signature invalid. --- ### `GET /api/contracts/[id]/download` **File:** `app/api/contracts/[id]/download/route.ts` **Purpose:** Proxies contract file download through the server (adds auth check before serving). **Authentication:** Clerk session + ownership validation **Response:** File stream with appropriate `Content-Type` and `Content-Disposition` headers. --- ## Server Actions (`features/contracts/api/contract.action.ts`) All server actions are decorated with `"use server"` and perform double authentication (Clerk session + DB ownership check). --- ### `saveContract(data)` Saves a new contract after UploadThing upload, then **automatically triggers AI analysis**. **Parameters:** ```typescript { fileName: string fileUrl: string fileSize: number mimeType: string } ``` **Returns:** ```typescript { success: true, contract: Contract } | { success: true, contract: Contract, analysisSuccess: false, analysisError: string } | { success: false, error: string } ``` **Side effects:** - Creates `Contract` record (status: `UPLOADED`) - Creates in-app notification (upload success) - Triggers `analyzeContractAction()` automatically - Revalidates `/contacts` and `/dashboard` --- ### `getContracts(filters?)` Returns all contracts for the authenticated user with optional filtering. **Parameters:** ```typescript { status?: ContractStatus type?: ContractType search?: string // searches title, provider, policyNumber, fileName } ``` **Returns:** ```typescript { success: true, contracts: SerializedContract[] } ``` **Serialization:** `Decimal` → `number`, `Date` → ISO string --- ### `getContract(id)` Returns a single contract by ID. **Parameters:** `id: string` (contract ID) **Returns:** `{ success: true, contract: Contract } | { success: false, error: string }` --- ### `deleteContract(id)` Permanently deletes a contract (ownership validated, cascades to RAG chunks and notifications). **Returns:** `{ success: true } | { success: false, error: string }` **Side effects:** - Deletes file from UploadThing CDN - Cascades delete to `ContractRagChunk`, `Notification`, `BlockchainTransaction` - Creates in-app notification (delete success) - Revalidates `/contacts` and `/dashboard` --- ### `deleteAllContractsAction()` Deletes all contracts for the authenticated user. **Returns:** `{ success: true, deletedCount: number } | { success: false, error: string }` --- ### `analyzeContractAction(id)` Triggers the full AI analysis pipeline for a contract. **Parameters:** `id: string` (contract ID) **Returns:** ```typescript { success: true, contract: NormalizedAnalysis } | { success: false, error: string, errorCode: "INVALID_CONTRACT" | "ANALYSIS_ERROR" } ``` **Pipeline:** 1. Set status → `PROCESSING` 2. Run AI extraction (Gemini → Mistral fallback) 3. Validate AI output 4. Save extracted fields to DB 5. Build RAG chunks (embeddings) 6. Register document hash on blockchain (non-blocking) 7. Send analysis complete email (non-blocking) 8. Create in-app notification 9. Set status → `COMPLETED` **Error codes:** - `INVALID_CONTRACT` — uploaded file is not a recognized contract document - `ANALYSIS_ERROR` — analysis pipeline failed (AI error, parse error, etc.) --- ### `askContractQuestionAction(id, question)` Answers a natural-language question about a specific contract using RAG + AI. **Parameters:** ```typescript id: string // contract ID question: string // user's question ``` **Returns:** ```typescript { success: true, answer: string } | { success: false, error: string } ``` **Pipeline:** 1. Load contract from DB 2. RAGService retrieves top-6 most relevant chunks 3. AIService sends question + contract context + RAG chunks to Gemini 4. Sanitized answer returned to UI --- ### `getContractStats()` Returns analytics data for the dashboard. **Returns:** ```typescript { success: true, stats: { total: number byStatus: Record byType: Record byMonth: Array<{ month: string, count: number }> expiringSoon: number averagePremium: number | null blockchainCoverage: number // percentage } } ``` --- ## Notification Server Actions **File:** `features/notifications/api/` (or `lib/services/notification.service.ts`) ### `getNotificationsAction()` Returns all unread (and recent read) notifications for the user. ### `markNotificationReadAction(id)` Marks a single notification as read. ### `markAllNotificationsReadAction()` Marks all notifications as read. ### `deleteNotificationAction(id)` Deletes a single notification. --- ## Blockchain Server Actions **File:** `features/blockchain/api/` ### `getBlockchainStatsAction()` Returns network stats: total verified docs, block number, network name, connection status. ### `verifyDocumentAction(documentHash)` Verifies whether a given SHA-256 hash is registered on-chain. **Returns:** ```typescript { exists: boolean timestamp: number // Unix timestamp depositor: string // Ethereum address formattedDate: string // Human-readable date } ``` --- ## Health Check Endpoint The Dockerfile `HEALTHCHECK` calls: ``` GET /api/health ``` This route should return `200 OK` with `{ status: "ok" }`. If this route does not exist, create it at `app/api/health/route.ts`: ```typescript export async function GET() { return Response.json({ status: "ok" }); } ```