text extractions tests * increase timeout for slow ci/cd systems * add tests
38 KiB
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.
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> - 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:
# 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 retryingRateLimit: {burst};window={seconds}- Current rate limit
Response Body:
{
"message": "rate limit exceeded"
}
Client Management (ClientService)
POST /client
Creates a new client in the system.
Security: Requires JWT authentication
Request Body (ClientCreate):
{
"id": "string (max 36 chars, required)",
"name": "string (max 256 chars, required)"
}
Responses:
201: Client created successfully{ "id": "AAA" }400: Invalid request data401: Authentication required429: Rate limit exceeded500: 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):
{
"id": "AAA",
"name": "ACME Corporation",
"can_sync": true
}
PATCH /client/{id}
Updates client information.
Security: Requires JWT authentication
Request Body (ClientUpdate):
{
"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):
{
"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):
[
{
"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):
{
"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):
{
"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):
{
"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):
{
"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 successfully404: 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-LPT9are 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):
{
"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
parentIdto 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):
{
"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):
{
"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):
{
"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:
{
"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):
{
"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):
{
"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):
{
"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:
{
"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:
{
"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):
{
"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.00
}
],
"createdBy": "user@example.com"
}
Single Fields (1:1 relationship with document):
fileName,contractTitle,clientName,payerNamepayerState,providerState(2-letter state codes)filenameTin,provGroupTin,provGroupNpi,provGroupNameFullprovOtherTin,provOtherNpi,provOtherNameFullaareteDerivedEffectiveDt,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):
{
"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:
{
"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 UUIDversion(required): Version number (integer, minimum 1)
Response (200 OK):
{
"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 parameters404: 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):
{
"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):
{
"type": "JSON_EXTRACTOR",
"config": "{\"key\": \"value\"}",
"required_queries": ["019580df-ef65-7676-8de9-94435a93337b"]
}
Response (201 Created):
{
"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):
{
"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):
{
"document_id": "019580df-b3f8-7348-9ab1-1e55b3f18ed7",
"query_version": 1
}
Response (QueryTestResponse):
{
"value": "extracted result"
}
Collector Configuration (CollectorService)
GET /client/{id}/collector
Retrieves collector configuration for a client.
Security: Requires JWT authentication
Response (Collector):
{
"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):
{
"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):
{
"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):
{
"id": "019580de-4d51-713c-98ee-464e83811f13"
}
GET /export/{id}
Checks export status and retrieves download URL.
Security: Requires JWT authentication
Response (ExportDetails):
{
"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:
- User is created in AWS Cognito (primary authentication system)
- If Cognito creation fails, operation fails with HTTP 502
- User is created in Permit.io (authorization/role system)
- If Permit.io creation fails, user still exists in Cognito but
permit_synced=false - Roles are assigned in Permit.io (best-effort, continues on failures)
Request Body (AdminUserCreate):
{
"email": "john.doe@example.com",
"first_name": "John",
"last_name": "Doe",
"roles": ["user_admin", "auditor"]
}
| Field | Type | Required | Description |
|---|---|---|---|
| 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):
{
"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 data401: Authentication required403: Insufficient permissions409: User already exists with different attributes429: Rate limit exceeded502: 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 | Sort field: email, created_at, last_name |
|
| sort_order | string | asc | Sort direction: asc, desc |
Response (AdminUserList):
{
"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: booleanX-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
rolesarray 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):
{
"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 betrueto confirm deletion
Response (200 OK or 207 Multi-Status):
{
"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):
{
"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
{
"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:
/v1prefix - 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
- Authentication: User authenticates via AWS Cognito, receiving a JWT token
- User Identification: The
sub(subject) claim from the JWT is extracted as the user identifier - Resource Mapping: The first path segment of the URL becomes the resource (e.g.,
/client/{id}/document→client) - Action Mapping: The HTTP method is converted to lowercase to become the action (e.g.,
GET→get) - 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:
{
"error": "Authorization required",
"message": "This endpoint requires authentication. Please obtain a valid JWT token.",
"login_url": "/login"
}
HTTP 403 Forbidden - Insufficient permissions:
{
"error": "Insufficient permissions",
"message": "Access denied by authorization service",
"user": "<cognito-subject-id>",
"resource": "<resource-name>",
"action": "<action-name>"
}
Code Generation
OpenAPI Integration
The API is generated from the OpenAPI specification using:
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