Documents API
Manage document requests, handle file uploads via signed URLs, and approve or reject client-submitted documents. Documents belong to matters.
Upload Flow
Files are uploaded directly to storage — not through the API. Use POST /uploads/signed-url to get a pre-signed URL, upload the file directly, then confirm via POST /uploads/:id/confirm.
List Documents for Matter
Returns all document requests and their upload status for a given matter.
Authentication
Requires documents:read scope.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| status | string | Filter by status: pending, uploaded, approved, rejected |
| page | integer | Page number (default 1) |
| per_page | integer | Results per page (default 20, max 100) |
Response (200)
{
"data": [
{
"id": "doc_01HAAA",
"matter_id": "mat_01HXYZ",
"name": "Driver's License",
"description": "Front and back of government-issued ID",
"status": "approved",
"required": true,
"upload": {
"id": "upl_01HBBB",
"filename": "drivers_license.pdf",
"content_type": "application/pdf",
"file_size_bytes": 245760,
"uploaded_at": "2024-03-07T15:00:00Z"
},
"created_at": "2024-03-07T14:00:00Z"
},
{
"id": "doc_01HCCC",
"matter_id": "mat_01HXYZ",
"name": "Police Report",
"description": "Official police report from the incident",
"status": "pending",
"required": true,
"upload": null,
"created_at": "2024-03-07T14:00:00Z"
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total": 5,
"has_more": false
}
}Request a Document
Adds a document request to a matter. The client will see this request in their intake portal.
Authentication
Requires documents:write scope.
Request Body
{
"name": "Medical Records",
"description": "All medical records related to the accident injuries",
"required": true,
"instructions": "Please include records from all treating providers",
"accepted_types": ["application/pdf", "image/jpeg", "image/png"],
"max_file_size_mb": 25
}Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Document name shown to client |
| description | string | No | More detail about what's needed |
| required | boolean | No | Whether client must provide this (default: true) |
| instructions | string | No | Step-by-step instructions for client |
| accepted_types | string[] | No | Allowed MIME types |
| max_file_size_mb | integer | No | Max file size in MB (default: 25) |
Response (201)
{
"data": {
"id": "doc_01HDDD",
"matter_id": "mat_01HXYZ",
"name": "Medical Records",
"description": "All medical records related to the accident injuries",
"required": true,
"instructions": "Please include records from all treating providers",
"status": "pending",
"accepted_types": ["application/pdf", "image/jpeg", "image/png"],
"max_file_size_mb": 25,
"upload": null,
"created_at": "2024-03-07T16:00:00Z"
}
}Code Examples
curl -X POST https://api.calmintake.com/api/v1/matters/mat_01HXYZ/documents/request \
-H "X-Api-Key: ch_live_sk_abc123xyz" \
-H "Content-Type: application/json" \
-d '{
"name": "Medical Records",
"description": "All medical records related to the accident injuries",
"required": true
}'Approve or Reject Upload
Approves or rejects a client document upload. Rejected documents can be re-uploaded by the client.
Authentication
Requires documents:write scope.
Approve
{
"status": "approved",
"reviewer_notes": "Document looks good, all pages present and legible."
}Reject
{
"status": "rejected",
"reviewer_notes": "Document is blurry and unreadable. Please re-upload."
}Response (200)
{
"data": {
"id": "upl_01HBBB",
"document_id": "doc_01HAAA",
"status": "approved",
"reviewer_notes": "Document looks good, all pages present and legible.",
"reviewed_at": "2024-03-07T17:00:00Z"
}
}Valid Status Values
| Status | Description |
|---|---|
| approved | Document accepted — marks document as complete |
| rejected | Document rejected — client can re-upload |
Get Signed Upload URL
Generates a pre-signed URL for uploading a file directly to CaseHug storage. The URL expires in 15 minutes. After uploading, call /uploads/:id/confirm to complete the upload.
Authentication
Requires documents:write scope.
Request Body
{
"document_id": "doc_01HDDD",
"filename": "medical_records.pdf",
"content_type": "application/pdf",
"file_size_bytes": 1048576
}Response (200)
{
"data": {
"upload_id": "upl_01HEEE",
"upload_url": "https://storage.calmintake.com/uploads/upl_01HEEE?X-Amz-Signature=...",
"upload_url_expires_at": "2024-03-07T16:15:00Z",
"fields": {
"Content-Type": "application/pdf",
"x-amz-meta-upload-id": "upl_01HEEE"
}
}
}Full Upload Flow
# Step 1: Get signed URL
curl -X POST https://api.calmintake.com/api/v1/uploads/signed-url \
-H "X-Api-Key: ch_live_sk_abc123xyz" \
-H "Content-Type: application/json" \
-d '{
"document_id": "doc_01HDDD",
"filename": "medical_records.pdf",
"content_type": "application/pdf",
"file_size_bytes": 1048576
}'
# Step 2: Upload directly to S3
curl -X PUT "https://storage.calmintake.com/uploads/upl_01HEEE?X-Amz-Signature=..." \
-H "Content-Type: application/pdf" \
--data-binary @medical_records.pdf
# Step 3: Confirm upload
curl -X POST https://api.calmintake.com/api/v1/uploads/upl_01HEEE/confirm \
-H "X-Api-Key: ch_live_sk_abc123xyz"Confirm Upload
Confirms that a file has been successfully uploaded to the signed URL. This triggers virus scanning and document processing. No request body required.
Authentication
Requires documents:write scope.
Response (200)
{
"data": {
"id": "upl_01HEEE",
"document_id": "doc_01HDDD",
"filename": "medical_records.pdf",
"content_type": "application/pdf",
"file_size_bytes": 1048576,
"status": "pending",
"uploaded_at": "2024-03-07T16:10:00Z"
}
}Error Responses
| Status | Code | Description |
|---|---|---|
| 404 | upload_not_found | Upload ID does not exist |
| 409 | upload_already_confirmed | Upload was already confirmed |
| 422 | upload_url_expired | Signed URL expired before upload completed |
| 422 | file_not_found_in_storage | File was not found at the upload URL |