# API Documentation ## API Overview The DoczyAI Query Orchestration Platform exposes a comprehensive REST API following OpenAPI 3.0.3 specification. The API is automatically generated from the specification file `serviceAPIs/queryAPI.yaml` using oapi-codegen. also see [query api summary](./queryapi.summary.md) ### Base Configuration - **Base URL**: `https://doczy.com/v1` (production), `http://localhost:8080` (development) - **API Version**: 0.0.1 - **Content Type**: `application/json` - **Documentation**: Auto-generated Swagger UI at `/swagger/index.html` ### API Services The API is organized into the following service groups: | Service | Description | | ---------------------- | ----------------------------------------------------------- | | ClientService | Operations related to clients | | CollectorService | Operations related to collectors | | DocumentsService | Operations related to documents | | FolderService | Operations related to document folders and organization | | LabelService | Operations related to document labels and workflow tracking | | FieldExtractionService | Operations related to document field extractions | | QueryService | Operations related to queries | | ExportService | Operations related to exports | | AuthService | Operations related to authentication | | AdminService | Operations related to user management and administration | ## Authentication and Authorization ### Security Schemes #### JWT Authentication - **Type**: Bearer token authentication - **Header**: `Authorization: Bearer ` - **Token Source**: JWT cookie set via OAuth flow #### OAuth2 Flow - **Type**: OAuth2 Authorization Code Flow - **Provider**: AWS Cognito - **Scopes**: `openid`, `email`, `profile` ### Authentication Endpoints #### `GET /login` Initiates OAuth2 authentication flow with AWS Cognito. **Security**: Public (no authentication required) **Response**: `302` Redirect to Cognito login page #### `GET /login-callback` OAuth2 callback handler that processes authentication response. **Security**: Public **Parameters**: - `code` (query, required): Authorization code from Cognito (max 2048 chars) - `state` (query, required): CSRF protection parameter (max 1024 chars) **Response**: `302` Sets JWT cookie and redirects to application #### `GET /logout` Terminates user session and clears authentication cookies. **Security**: Requires JWT authentication **Response**: `302` Redirect to Cognito logout page or application home #### `GET /home` Returns the HTML menu page for authenticated users. **Security**: Requires JWT authentication **Response**: `200` HTML content ## Rate Limiting The queryAPI service implements dual-layer rate limiting to protect against DDoS attacks and ensure fair resource allocation. ### Rate Limit Architecture **Dual-Layer Protection:** - **Layer 1: Global/Aggregate Rate Limiting** - Protects total system capacity across all IP addresses - **Layer 2: Per-IP Rate Limiting** - Prevents individual client abuse Both layers must pass for a request to proceed. The global limiter checks first for faster failure paths under DDoS conditions. ### Default Rate Limits By default, all endpoints are limited to: - **Global**: 500 requests per second total (1000 burst) - **Per-IP**: 5 requests per second per IP (10 burst) - **Cleanup Interval**: 3 minutes for inactive IP limiters **Important:** No endpoints are exempt from rate limiting. This is a security requirement to prevent DDoS attacks. ### Configuration Rate limits are configured via environment variables: ```bash # Global/Aggregate rate limiting (protects total system capacity) RATE_LIMIT_GLOBAL_RATE=500 # total req/s across ALL IPs RATE_LIMIT_GLOBAL_BURST=1000 # total burst capacity across ALL IPs # Per-IP rate limiting (protects against individual abusers) RATE_LIMIT_DEFAULT_RATE=5 # req/s per IP RATE_LIMIT_DEFAULT_BURST=10 # burst per IP RATE_LIMIT_EXPIRES_IN=180 # seconds (3 minutes) # Per-endpoint overrides (JSON format) RATE_LIMIT_ENDPOINT_OVERRIDES={} ``` ### Rate Limit Headers All API responses include a `RateLimit` header indicating the current limit: ``` RateLimit: 20;window=2 ``` **Format:** `{burst};window={seconds}` ### Rate Limit Exceeded Response **HTTP Status:** `429 Too Many Requests` **Headers:** - `Retry-After: {seconds}` - Number of seconds before retrying - `RateLimit: {burst};window={seconds}` - Current rate limit **Response Body:** ```json { "message": "rate limit exceeded" } ``` --- ## Client Management (ClientService) ### `POST /client` Creates a new client in the system. **Security**: Requires JWT authentication **Request Body** (`ClientCreate`): ```json { "id": "string (max 36 chars, required)", "name": "string (max 256 chars, required)" } ``` **Responses**: - `201`: Client created successfully ```json { "id": "AAA" } ``` - `400`: Invalid request data - `401`: Authentication required - `429`: Rate limit exceeded - `500`: Internal server error ### `GET /client/{id}` Retrieves client details by ID. **Security**: Requires JWT authentication **Path Parameters**: - `id`: Client identifier (string, max 36 chars) **Response** (`DocClient`): ```json { "id": "AAA", "name": "ACME Corporation", "can_sync": true } ``` ### `PATCH /client/{id}` Updates client information. **Security**: Requires JWT authentication **Request Body** (`ClientUpdate`): ```json { "name": "string (max 256 chars, optional)", "can_sync": "boolean (optional)" } ``` **Response**: `200` Client updated successfully ### `GET /client/{id}/status` Returns client synchronization status. **Security**: Requires JWT authentication **Response** (`ClientStatusBody`): ```json { "status": "IN_SYNC" } ``` **Status Values**: `IN_SYNC`, `NOT_SYNCED`, `NOT_SYNCING` --- ## Document Operations (DocumentsService) ### `POST /client/{id}/document` Uploads a document for a specific client. **Security**: Requires JWT authentication **Content Type**: `multipart/form-data` **Max File Size**: 100GB **Form Parameters**: - `file` (required): Document file (PDF) - `filename` (optional): Custom filename (max 4096 chars) **Response**: `200` Document uploaded ### `GET /client/{id}/document` Lists documents for a client. **Security**: Requires JWT authentication **Response** (`ListDocuments`): ```json [ { "id": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "hash": "sha256:abc123..." } ] ``` ### `GET /document/{id}` Retrieves document details by ID. **Security**: Requires JWT authentication **Path Parameters**: - `id`: Document UUID (max 36 chars) **Response** (`Document`): ```json { "id": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "client_id": "AAA", "hash": "sha256:abc123...", "fields": { "property1": "string value", "property2": 42 } } ``` --- ## Batch Document Operations (DocumentsService) ### `POST /client/{id}/document/batch` Uploads a ZIP archive containing multiple PDF documents for asynchronous batch processing. **Security**: Requires JWT authentication **Content Type**: `multipart/form-data` **Max Archive Size**: 1GB **Supported Archive Type**: ZIP only **Form Parameters**: - `archive` (required): ZIP file containing PDF documents **Response** (`202 Accepted`): ```json { "batch_id": "019580df-ef65-7676-8de9-94435a93337a", "status": "processing", "status_url": "/client/AAA/document/batch/019580df-ef65-7676-8de9-94435a93337a" } ``` ### `GET /client/{id}/document/batch` Lists batch uploads for a client with pagination support. **Security**: Requires JWT authentication **Query Parameters**: - `limit`: Maximum results (integer, 1-100, default: 20) - `offset`: Pagination offset (integer, default: 0) **Response** (`BatchUploadList`): ```json { "batches": [ { "batch_id": "019580df-ef65-7676-8de9-94435a93337a", "client_id": "AAA", "original_filename": "documents.zip", "status": "completed", "total_documents": 100, "processed_documents": 100, "failed_documents": 2, "invalid_type_documents": 1, "progress_percent": 100, "created_at": "2024-01-01T00:00:00Z", "completed_at": "2024-01-01T00:05:00Z" } ], "total_count": 25 } ``` **Batch Status Values**: `processing`, `completed`, `failed`, `cancelled` ### `GET /client/{id}/document/batch/{batch_id}` Retrieves detailed status information for a specific batch upload. **Security**: Requires JWT authentication **Path Parameters**: - `batch_id`: Batch upload identifier (UUID) **Response** (`BatchUploadDetails`): ```json { "batch_id": "019580df-ef65-7676-8de9-94435a93337a", "client_id": "AAA", "original_filename": "documents.zip", "status": "completed", "total_documents": 100, "processed_documents": 100, "failed_documents": 2, "invalid_type_documents": 1, "progress_percent": 100, "created_at": "2024-01-01T00:00:00Z", "completed_at": "2024-01-01T00:05:00Z", "failed_filenames": ["doc3.pdf", "doc17.pdf"] } ``` ### `DELETE /client/{id}/document/batch/{batch_id}` Cancels a batch upload that is currently processing. **Security**: Requires JWT authentication **Response**: - `204`: Batch upload cancelled successfully - `404`: Batch not found --- ## Folder Operations (FolderService) ### Folder Path Constraints All folder paths must comply with the following constraints when creating or renaming folders: - **Must start with `/`**: All paths must begin with a forward slash - **Maximum path length**: 1000 characters - **Maximum segment length**: 255 characters per path segment - **Maximum depth**: 20 levels of nesting - **Valid characters only**: Alphanumeric (`a-z`, `A-Z`, `0-9`), hyphen (`-`), underscore (`_`), period (`.`), and space (` `) - **Segment must start with alphanumeric**: Each path segment must begin with a letter or number - **No empty segments**: Double slashes (`//`) or trailing slashes are not allowed - **No path traversal**: Sequences like `..` or `./` are blocked - **No hidden folders**: Segments cannot start with a period (e.g., `/.hidden`) - **No leading/trailing whitespace**: Spaces within segment names are allowed, but not at the start or end - **No control characters**: ASCII control characters (0x00-0x1F, 0x7F) are blocked - **No Windows reserved names**: Names like `CON`, `PRN`, `AUX`, `NUL`, `COM1`-`COM9`, `LPT1`-`LPT9` are blocked - **Root folder restrictions**: The root path `/` can only exist once per client and cannot be renamed - **Path uniqueness**: Each path must be unique within a client (enforced by database) - **Unicode normalization**: Paths are normalized to NFC form **Valid path examples**: - `/documents` - `/documents/2024/Q1` - `/client-data/reports_2024` - `/My Documents/Archive` **Invalid path examples**: - `documents` (missing leading `/`) - `/documents/` (trailing slash) - `/foo//bar` (empty segment) - `/foo/../bar` (path traversal) - `/.hidden` (dot prefix) - `/foo@bar` (invalid character) - `/CON` (Windows reserved name) --- ### `GET /clients/{clientId}/folders` Lists all folders for a specific client, including the folder hierarchy. **Security**: Requires JWT authentication **Path Parameters**: - `clientId`: Client external ID (string, max 36 chars) **Response** (`200 OK`): ```json { "folders": [ { "id": "019580df-ef65-7676-8de9-94435a93337a", "path": "/", "parentId": null, "clientId": "AAA", "createdAt": "2025-10-16T14:27:15Z", "createdBy": "system" }, { "id": "019580df-ef65-7676-8de9-94435a93337b", "path": "/documents", "parentId": "019580df-ef65-7676-8de9-94435a93337a", "clientId": "AAA", "createdAt": "2025-10-16T14:30:00Z", "createdBy": "user@example.com" }, { "id": "019580df-ef65-7676-8de9-94435a93337c", "path": "/documents/2024", "parentId": "019580df-ef65-7676-8de9-94435a93337b", "clientId": "AAA", "createdAt": "2025-10-16T14:35:00Z", "createdBy": "user@example.com" } ] } ``` **Notes**: - Returns all folders for the client, including nested folders - Folders are returned in a flat list; use `parentId` to reconstruct the hierarchy - The root folder (path="/") is included if it exists ### `POST /folders` Creates a new folder for organizing documents. **Security**: Requires JWT authentication **Request Body** (`FolderCreate`): ```json { "path": "/documents/2024", "clientId": "AAA", "createdBy": "user@example.com", "parentId": "019580df-ef65-7676-8de9-94435a93337a" } ``` | Field | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------------------- | | path | string | Yes | Folder path, must start with "/" (max 1000 chars) | | clientId | string | Yes | Client external ID | | createdBy | string | Yes | Email of user creating the folder | | parentId | uuid | No | Parent folder ID for nested folders | **Response** (`201 Created`): ```json { "id": "019580df-ef65-7676-8de9-94435a93337a", "path": "/documents/2024", "parentId": null, "clientId": "AAA", "createdAt": "2025-10-16T14:27:15Z", "createdBy": "user@example.com" } ``` ### `PATCH /folders/{folderId}` Renames an existing folder. **Security**: Requires JWT authentication **Path Parameters**: - `folderId`: Folder UUID **Request Body** (`FolderRename`): ```json { "path": "/documents/2024-renamed" } ``` **Response** (`200 OK`): Updated folder object ### `GET /folders/{folderId}/documents` Retrieves all documents within a specific folder. **Security**: Requires JWT authentication **Response**: ```json { "documents": [ { "id": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "client_id": "AAA", "hash": "sha256:abc123...", "fields": {} } ] } ``` ### `GET /folders/{folderId}/metrics` Retrieves processing progress metrics for documents in a folder. **Security**: Requires JWT authentication **Response** (`FolderMetrics`): ```json { "folderId": "019580df-ef65-7676-8de9-94435a93337a", "totalDocuments": 150, "byLabel": { "Ingested": 150, "OCR_Processed": 120, "Dashboard_Ready": 100 } } ``` --- ## Label Operations (LabelService) ### `POST /documents/{documentId}/labels` Applies a workflow label to a document for tracking processing state. **Security**: Requires JWT authentication **Path Parameters**: - `documentId`: Document UUID **Request Body** (`LabelApplication`): ```json { "label": "OCR_Processed", "appliedBy": "user@example.com" } ``` | Field | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------------------------------ | | label | string | Yes | Label name (alphanumeric and underscore only, max 100 chars) | | appliedBy | string | Yes | Email of user applying the label | **Response** (`201 Created`): ```json { "id": "019580df-ef65-7676-8de9-94435a93337a", "documentId": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "label": "OCR_Processed", "appliedBy": "user@example.com", "appliedAt": "2025-10-16T14:27:15Z" } ``` ### `GET /documents/{documentId}/labels` Retrieves all labels that have been applied to a document, ordered by most recent first. **Security**: Requires JWT authentication **Response**: ```json { "labels": [ { "id": "019580df-ef65-7676-8de9-94435a93337a", "documentId": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "label": "OCR_Processed", "appliedBy": "user@example.com", "appliedAt": "2025-10-16T14:27:15Z" } ] } ``` ### `GET /labels/{labelName}/documents` Retrieves all documents that have been tagged with a specific label. **Security**: Requires JWT authentication **Path Parameters**: - `labelName`: Label name (alphanumeric and underscore only, max 100 chars) **Query Parameters**: - `clientId` (required): Client ID to filter documents (UUID) **Response**: ```json { "documents": [ { "id": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "hash": "sha256:abc123..." } ] } ``` --- ## Field Extraction Operations (FieldExtractionService) ### `POST /field-extractions` Creates a new field extraction with single-value and array fields for a document. **Security**: Requires JWT authentication **Request Body** (`FieldExtractionRequest`): ```json { "documentId": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "singleFields": { "fileName": "contract_2024.pdf", "contractTitle": "Service Agreement", "clientName": "ACME Corp", "payerName": "Insurance Co", "payerState": "CA", "providerState": "NY", "aareteDerivedEffectiveDt": "2024-01-01", "aareteDerivedTerminationDt": "2025-12-31", "autoRenewalInd": true }, "arrayFields": [ { "exhibitTitle": "Exhibit A", "exhibitPage": "15", "reimbProvTin": "123456789", "reimbProvNpi": "1234567890", "reimbProvName": "Provider Name", "reimbEffectiveDt": "2024-01-01", "aareteDerivedClaimTypeCd": "INPATIENT", "aareteDerivedReimbMethod": "Per Diem", "reimbPctRate": 85.5, "reimbFeeRate": 150.0 } ], "createdBy": "user@example.com" } ``` **Single Fields** (1:1 relationship with document): - `fileName`, `contractTitle`, `clientName`, `payerName` - `payerState`, `providerState` (2-letter state codes) - `filenameTin`, `provGroupTin`, `provGroupNpi`, `provGroupNameFull` - `provOtherTin`, `provOtherNpi`, `provOtherNameFull` - `aareteDerivedEffectiveDt`, `aareteDerivedTerminationDt` (dates) - `aareteDerivedAmendmentNum` (integer) - `autoRenewalInd` (boolean), `autoRenewalTerm` **Array Fields** (1:N relationship - multiple rows per document): - Provider info: `reimbProvTin`, `reimbProvNpi`, `reimbProvName` - Dates: `reimbEffectiveDt`, `reimbTerminationDt` - Classifications: `aareteDerivedClaimTypeCd`, `aareteDerivedProduct`, `aareteDerivedLob`, `aareteDerivedProgram`, `aareteDerivedNetwork`, `aareteDerivedProvType` - Codes: `provTaxonomyCd`, `provSpecialtyCd`, `placeOfServiceCd`, `billTypeCd`, `cpt4ProcCd`, `revenueCd`, `diagCd`, `ndcCd` - Rates: `reimbPctRate`, `reimbFeeRate`, `reimbConversionFactor`, `grouperPctRate`, `grouperBaseRate` - Indicators: `carveoutInd`, `lesserOfInd`, `greaterOfInd`, `defaultInd` **Response** (`201 Created`): ```json { "id": "019580df-ef65-7676-8de9-94435a93337a", "documentId": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "version": 1, "singleFields": { ... }, "arrayFields": [ ... ], "createdAt": "2025-10-16T14:27:15Z", "createdBy": "user@example.com" } ``` ### `GET /field-extractions` Retrieves the most recent field extraction for a document. **Security**: Requires JWT authentication **Query Parameters**: - `documentId` (required): Document UUID **Response**: `FieldExtractionResponse` (same as POST response) ### `GET /field-extractions/history` Retrieves all versions of field extractions for a document, ordered by most recent first. **Security**: Requires JWT authentication **Query Parameters**: - `documentId` (required): Document UUID **Response**: ```json { "versions": [ { "id": "019580df-ef65-7676-8de9-94435a93337a", "documentId": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "version": 2, "createdAt": "2025-10-17T10:00:00Z", "createdBy": "user@example.com" }, { "id": "019580df-ef65-7676-8de9-94435a93337b", "documentId": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "version": 1, "createdAt": "2025-10-16T14:27:15Z", "createdBy": "user@example.com" } ] } ``` ### `GET /field-extractions/version` Retrieves a specific version of a field extraction for a document by version number. **Security**: Requires JWT authentication **Query Parameters**: - `documentId` (required): Document UUID - `version` (required): Version number (integer, minimum 1) **Response** (`200 OK`): ```json { "id": "019580df-ef65-7676-8de9-94435a93337a", "documentId": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "version": 1, "singleFields": { "fileName": "contract_2024.pdf", "contractTitle": "Service Agreement", "clientName": "ACME Corp" }, "arrayFields": [], "createdAt": "2025-10-16T14:27:15Z", "createdBy": "user@example.com" } ``` **Error Responses**: - `400`: Missing required query parameters - `404`: Version not found for the specified document --- ## Query Management (QueryService) ### `GET /query` Lists all available queries in the system. **Security**: Requires JWT authentication **Response** (`ListQueries`): ```json { "queries": [ { "id": "019580df-ef65-7676-8de9-94435a93337a", "type": "JSON_EXTRACTOR", "active_version": 1, "latest_version": 2, "config": "{...}", "required_queries": ["019580df-ef65-7676-8de9-94435a93337b"] } ] } ``` **Query Types**: `JSON_EXTRACTOR`, `CONTEXT_FULL` ### `POST /query` Creates a new query definition. **Security**: Requires JWT authentication **Request Body** (`QueryCreate`): ```json { "type": "JSON_EXTRACTOR", "config": "{\"key\": \"value\"}", "required_queries": ["019580df-ef65-7676-8de9-94435a93337b"] } ``` **Response** (`201 Created`): ```json { "id": "019580df-ef65-7676-8de9-94435a93337a" } ``` ### `GET /query/{id}` Retrieves query details including configuration. **Security**: Requires JWT authentication **Response**: `Query` object ### `PATCH /query/{id}` Updates query definition. **Security**: Requires JWT authentication **Request Body** (`QueryUpdate`): ```json { "config": "{\"updated\": true}", "active_version": 2, "required_queries": [] } ``` ### `POST /query/{id}/test` Tests query execution against a document. **Security**: Requires JWT authentication **Request Body** (`QueryTestRequest`): ```json { "document_id": "019580df-b3f8-7348-9ab1-1e55b3f18ed7", "query_version": 1 } ``` **Response** (`QueryTestResponse`): ```json { "value": "extracted result" } ``` --- ## Collector Configuration (CollectorService) ### `GET /client/{id}/collector` Retrieves collector configuration for a client. **Security**: Requires JWT authentication **Response** (`Collector`): ```json { "client_id": "AAA", "active_version": 1, "latest_version": 2, "minimum_cleaner_version": 1, "minimum_text_version": 1, "fields": [ { "name": "contract_date", "query_id": "019580df-ef65-7676-8de9-94435a93337a" } ] } ``` ### `PATCH /client/{id}/collector` Updates collector configuration. **Security**: Requires JWT authentication **Request Body** (`CollectorSet`): ```json { "minimum_cleaner_version": 2, "minimum_text_version": 2, "active_version": 1, "fields": [ { "name": "contract_date", "query_id": "019580df-ef65-7676-8de9-94435a93337a" } ] } ``` **Response**: `204` Collector set successfully --- ## Export Operations (ExportService) ### `POST /client/{id}/export` Triggers data export for a client. **Security**: Requires JWT authentication **Request Body** (`ExportTrigger`): ```json { "ingestion_filters": { "start_date": "2024-01-01T00:00:00Z", "end_date": "2024-12-31T23:59:59Z" }, "field_filters": [ { "field_name": "contract_type", "condition": "include", "values": ["SERVICE", "PRODUCT"] } ] } ``` **Filter Conditions**: `less_than`, `greater_than`, `closed_interval`, `open_interval`, `left_closed_interval`, `right_closed_interval`, `include`, `exclude` **Response** (`201 Created`): ```json { "id": "019580de-4d51-713c-98ee-464e83811f13" } ``` ### `GET /export/{id}` Checks export status and retrieves download URL. **Security**: Requires JWT authentication **Response** (`ExportDetails`): ```json { "client_id": "AAA", "status": "completed", "output_location": "s3://bucket/AAA/export/20250213/uuid.csv" } ``` **Export Status Values**: `completed`, `in_progress`, `failed` --- ## User Administration (AdminService) ### `POST /admin/users` Creates a new user in both AWS Cognito and Permit.io systems with optional role assignments. **Security**: Requires JWT authentication **Creation Flow**: 1. User is created in AWS Cognito (primary authentication system) 2. If Cognito creation fails, operation fails with HTTP 502 3. User is created in Permit.io (authorization/role system) 4. If Permit.io creation fails, user still exists in Cognito but `permit_synced=false` 5. Roles are assigned in Permit.io (best-effort, continues on failures) **Request Body** (`AdminUserCreate`): ```json { "email": "john.doe@example.com", "first_name": "John", "last_name": "Doe", "roles": ["user_admin", "auditor"] } ``` | Field | Type | Required | Description | | ---------- | ------ | -------- | ---------------------------------------- | | email | string | Yes | User email address (max 320 chars) | | first_name | string | Yes | User's given name (max 100 chars) | | last_name | string | Yes | User's family name (max 100 chars) | | roles | array | Yes | List of role keys to assign (1-50 roles) | **Response** (`201 Created` or `200 OK` if user exists): ```json { "cognito_subject_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "email": "john.doe@example.com", "first_name": "John", "last_name": "Doe", "status": "FORCE_CHANGE_PASSWORD", "enabled": true, "permit_synced": true, "roles_assigned": ["user_admin", "auditor"], "created_at": "2025-10-16T14:23:45Z" } ``` **Error Responses**: - `400`: Invalid request data - `401`: Authentication required - `403`: Insufficient permissions - `409`: User already exists with different attributes - `429`: Rate limit exceeded - `502`: External service (Cognito) error ### `GET /admin/users` Lists users from AWS Cognito with pagination, filtering, and sorting support. **Security**: Requires JWT authentication **Query Parameters**: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | page | integer | 1 | Page number (1-10000) | | page_size | integer | 50 | Results per page (1-100) | | search | string | - | Search term for email/name filtering | | status | string | all | Filter by status: `enabled`, `disabled`, `all` | | sort_by | string | email | Sort field: `email`, `created_at`, `last_name` | | sort_order | string | asc | Sort direction: `asc`, `desc` | **Response** (`AdminUserList`): ```json { "users": [ { "cognito_subject_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "email": "john.doe@example.com", "first_name": "John", "last_name": "Doe", "status": "CONFIRMED", "enabled": true, "roles": ["admin", "viewer"], "created_at": "2025-10-15T09:15:30Z", "updated_at": "2025-10-16T14:20:10Z" } ], "total": 147, "page": 1, "page_size": 50, "has_more": true } ``` ### `GET /admin/users/{email}` Retrieves user details from both AWS Cognito and Permit.io by email address. **Security**: Requires JWT authentication **Path Parameters**: - `email`: URL-encoded user email address (max 320 chars) **Response** (`AdminUserDetails`): Same structure as user list item ### `HEAD /admin/users/{email}` Checks if a user exists in Cognito and/or Permit.io without returning full details. **Security**: Requires JWT authentication **Response Headers**: - `X-User-Exists-Cognito`: boolean - `X-User-Exists-PermitIO`: boolean **Response**: - `200`: User exists (at least in one system) - `404`: User does not exist in either system ### `PATCH /admin/users/{email}` Updates user attributes (first_name, last_name) in Cognito and manages role assignments in Permit.io. **Security**: Requires JWT authentication **Role Management (REPLACE Strategy)**: - Providing a `roles` array REPLACES all existing roles (not additive) - To add a role: include ALL current roles PLUS the new role - To remove a role: include only the roles you want to keep - To remove all roles: send an empty array `[]` - Omitting the roles field leaves role assignments unchanged **Request Body** (`AdminUserUpdate`): ```json { "first_name": "Jonathan", "last_name": "Doe-Smith", "roles": ["user_admin", "auditor"] } ``` **Response** (`200 OK`): Updated `AdminUserDetails` ### `DELETE /admin/users/{email}` Permanently deletes a user from both AWS Cognito and Permit.io. This operation is irreversible. **Security**: Requires JWT authentication **Query Parameters**: - `confirm` (required): Must be `true` to confirm deletion **Response** (`200 OK` or `207 Multi-Status`): ```json { "success": true, "email": "john.doe@example.com", "cognito_subject_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "deleted_from": { "cognito": true, "permit": true }, "timestamp": "2025-10-16T14:27:15Z" } ``` ### `POST /admin/users/{email}/disable` Disables a user in AWS Cognito, preventing authentication. User data and roles are preserved. Idempotent. **Security**: Requires JWT authentication **Response** (`AdminUserActionResponse`): ```json { "success": true, "email": "john.doe@example.com", "action": "disable", "cognito_subject_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "timestamp": "2025-10-16T14:25:33Z" } ``` ### `POST /admin/users/{email}/enable` Re-enables a previously disabled user in AWS Cognito. Restores authentication capability. Idempotent. **Security**: Requires JWT authentication **Response**: Same as disable endpoint with `"action": "enable"` --- ## Request/Response Schemas ### Standard Response Headers All API responses include: - `RateLimit`: Current rate limit status (format: `100;window=60`) - `Content-Type`: `application/json` (except file downloads and HTML pages) ### Error Response Format ```json { "message": "Error description (max 256 chars)" } ``` ### Common HTTP Status Codes | Code | Description | | ---- | -------------------------------------------- | | 200 | Success | | 201 | Created | | 202 | Accepted (async processing) | | 204 | No Content (success with no body) | | 207 | Multi-Status (partial success) | | 302 | Redirect | | 400 | Bad Request (validation errors) | | 401 | Unauthorized (authentication required) | | 403 | Forbidden (insufficient permissions) | | 404 | Not Found | | 409 | Conflict (resource exists or state conflict) | | 429 | Too Many Requests (rate limited) | | 500 | Internal Server Error | | 502 | Bad Gateway (external service error) | --- ## API Versioning ### Current Strategy - **URL-based versioning**: `/v1` prefix - **Backward Compatibility**: Maintained within major versions - **OpenAPI Version**: 0.0.1 (development phase) ### Future Considerations - Semantic versioning for production releases - Deprecation notices for breaking changes - Version sunset policies --- ## Authorization Reference (Permit.io Integration) The API uses Permit.io for fine-grained authorization. This section documents what data is sent to Permit.io for each endpoint, enabling system administrators to configure the correct resources, actions, and roles in Permit.io. ### Authorization Flow 1. **Authentication**: User authenticates via AWS Cognito, receiving a JWT token 2. **User Identification**: The `sub` (subject) claim from the JWT is extracted as the user identifier 3. **Resource Mapping**: The first path segment of the URL becomes the resource (e.g., `/client/{id}/document` → `client`) 4. **Action Mapping**: The HTTP method is converted to lowercase to become the action (e.g., `GET` → `get`) 5. **Permission Check**: Permit.io checks if the user has permission to perform the action on the resource ### Permit.io Data Mapping Logic **Resource Extraction** (from `internal/cognitoauth/mapping.go`): - The resource is derived from the **first segment** of the URL path - Leading slashes are removed - Example: `/client/{id}/document/batch/{batch_id}` → resource: `client` **Action Extraction**: - HTTP method is converted to lowercase - `GET` → `get`, `POST` → `post`, `PATCH` → `patch`, `DELETE` → `delete`, `HEAD` → `head` ### Endpoints Exempt from Authorization The following endpoints skip authentication and Permit.io authorization entirely: | Endpoint | Purpose | | ---------------- | ------------------------ | | `GET /home` | Public home/menu page | | `GET /logout` | Session termination | | `GET /health` | Health check endpoint | | `GET /swagger/*` | Swagger UI documentation | | `GET /metrics` | Prometheus metrics | ### Special Authentication Endpoints These endpoints handle authentication flows and bypass normal authorization: | Endpoint | Purpose | | --------------------- | --------------------------------------------- | | `GET /login` | Initiates OAuth2/PKCE login flow with Cognito | | `GET /login-callback` | Handles OAuth2 callback from Cognito | ### Authorization Reference Table The following table shows what data is passed to Permit.io for each protected endpoint: | HTTP Method | Endpoint | Resource | Action | Notes | | ------------------------------- | ---------------------------------------- | ------------------- | -------- | ----------------------- | | **Client Management** | | `POST` | `/client` | `client` | `post` | Create new client | | `GET` | `/clients` | `clients` | `get` | List all clients | | `GET` | `/client/{id}` | `client` | `get` | Get client details | | `PATCH` | `/client/{id}` | `client` | `patch` | Update client | | `GET` | `/client/{id}/status` | `client` | `get` | Get client sync status | | **Document Operations** | | `POST` | `/client/{id}/document` | `client` | `post` | Upload document | | `GET` | `/client/{id}/document` | `client` | `get` | List client documents | | `GET` | `/document/{id}` | `document` | `get` | Get document details | | **Batch Document Operations** | | `POST` | `/client/{id}/document/batch` | `client` | `post` | Upload batch (ZIP) | | `GET` | `/client/{id}/document/batch` | `client` | `get` | List batch uploads | | `GET` | `/client/{id}/document/batch/{batch_id}` | `client` | `get` | Get batch status | | `DELETE` | `/client/{id}/document/batch/{batch_id}` | `client` | `delete` | Cancel batch upload | | **Folder Operations** | | `GET` | `/client/{id}/folders` | `client` | `get` | List client folders | | `POST` | `/folders` | `folders` | `post` | Create folder | | `PATCH` | `/folders/{folderId}` | `folders` | `patch` | Rename folder | | `GET` | `/folders/{folderId}/documents` | `folders` | `get` | List folder documents | | `GET` | `/folders/{folderId}/metrics` | `folders` | `get` | Get folder metrics | | **Label Operations** | | `POST` | `/documents/{documentId}/labels` | `documents` | `post` | Apply label to document | | `GET` | `/documents/{documentId}/labels` | `documents` | `get` | Get document labels | | `GET` | `/labels/{labelName}/documents` | `labels` | `get` | Get documents by label | | **Field Extraction Operations** | | `POST` | `/field-extractions` | `field-extractions` | `post` | Create field extraction | | `GET` | `/field-extractions` | `field-extractions` | `get` | Get latest extraction | | `GET` | `/field-extractions/version` | `field-extractions` | `get` | Get specific version | | `GET` | `/field-extractions/history` | `field-extractions` | `get` | Get extraction history | | **Query Management** | | `GET` | `/query` | `query` | `get` | List queries | | `POST` | `/query` | `query` | `post` | Create query | | `GET` | `/query/{id}` | `query` | `get` | Get query details | | `PATCH` | `/query/{id}` | `query` | `patch` | Update query | | `POST` | `/query/{id}/test` | `query` | `post` | Test query execution | | **Collector Configuration** | | `GET` | `/client/{id}/collector` | `client` | `get` | Get collector config | | `PATCH` | `/client/{id}/collector` | `client` | `patch` | Update collector config | | **Export Operations** | | `POST` | `/client/{id}/export` | `client` | `post` | Trigger export | | `GET` | `/export/{id}` | `export` | `get` | Get export status | | **User Administration** | | `POST` | `/admin/users` | `admin` | `post` | Create user | | `GET` | `/admin/users` | `admin` | `get` | List users | | `GET` | `/admin/users/{email}` | `admin` | `get` | Get user details | | `HEAD` | `/admin/users/{email}` | `admin` | `head` | Check user existence | | `PATCH` | `/admin/users/{email}` | `admin` | `patch` | Update user | | `DELETE` | `/admin/users/{email}` | `admin` | `delete` | Delete user | | `POST` | `/admin/users/{email}/disable` | `admin` | `post` | Disable user | | `POST` | `/admin/users/{email}/enable` | `admin` | `post` | Enable user | ### Permit.io Configuration Requirements To configure Permit.io to work with this API, administrators must create: #### Resources | Resource Key | Description | | ------------------- | ---------------------------------------------- | | `client` | Client management and client-scoped operations | | `clients` | Client listing (plural form) | | `document` | Individual document operations | | `documents` | Document collection and label operations | | `folders` | Folder management | | `labels` | Label-based document queries | | `field-extractions` | Field extraction operations | | `query` | Query definition management | | `export` | Export operations | | `admin` | User administration | #### Actions | Action Key | HTTP Method | Description | | ---------- | ----------- | ------------------------ | | `get` | GET | Read/retrieve resources | | `post` | POST | Create resources | | `patch` | PATCH | Update resources | | `delete` | DELETE | Delete resources | | `head` | HEAD | Check resource existence | #### Example Role Configuration Typical role assignments for common personas: | Role | Resources | Actions | Use Case | | ------------ | -------------------------------------------------------------------------- | ---------------------- | -------------------------- | | `viewer` | `client`, `clients`, `document`, `documents`, `folders`, `query`, `export` | `get` | Read-only access | | `editor` | `client`, `document`, `documents`, `folders`, `field-extractions`, `query` | `get`, `post`, `patch` | Content management | | `admin` | All resources | All actions | Full administrative access | | `user_admin` | `admin` | All actions | User management only | ### Authorization Error Responses When authorization fails, the API returns: **HTTP 401 Unauthorized** - No valid JWT token: ```json { "error": "Authorization required", "message": "This endpoint requires authentication. Please obtain a valid JWT token.", "login_url": "/login" } ``` **HTTP 403 Forbidden** - Insufficient permissions: ```json { "error": "Insufficient permissions", "message": "Access denied by authorization service", "user": "", "resource": "", "action": "" } ``` --- ## Code Generation ### OpenAPI Integration The API is generated from the OpenAPI specification using: ```bash oapi-codegen -generate "types,server" -o api.gen.go serviceAPIs/queryAPI.yaml ``` ### Generated Artifacts - Type-safe Go structs for requests/responses - Echo framework route handlers - OpenAPI validation middleware - Swagger documentation ### Validation - Automatic request validation against OpenAPI schema - Response validation in development mode - Parameter binding with type safety