📄

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.

GET/api/v1/matters/:id/documents

List Documents for Matter

Returns all document requests and their upload status for a given matter.

Authentication

Requires documents:read scope.

Query Parameters

ParameterTypeDescription
statusstringFilter by status: pending, uploaded, approved, rejected
pageintegerPage number (default 1)
per_pageintegerResults per page (default 20, max 100)

Response (200)

json
{
  "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
  }
}
POST/api/v1/matters/:id/documents/request

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

json
{
  "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

FieldTypeRequiredDescription
namestringYesDocument name shown to client
descriptionstringNoMore detail about what's needed
requiredbooleanNoWhether client must provide this (default: true)
instructionsstringNoStep-by-step instructions for client
accepted_typesstring[]NoAllowed MIME types
max_file_size_mbintegerNoMax file size in MB (default: 25)

Response (201)

json
{
  "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
  }'
PATCH/api/v1/uploads/:id/status

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

json
{
  "status": "approved",
  "reviewer_notes": "Document looks good, all pages present and legible."
}

Reject

json
{
  "status": "rejected",
  "reviewer_notes": "Document is blurry and unreadable. Please re-upload."
}

Response (200)

json
{
  "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

StatusDescription
approvedDocument accepted — marks document as complete
rejectedDocument rejected — client can re-upload
POST/api/v1/uploads/signed-url

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

json
{
  "document_id": "doc_01HDDD",
  "filename": "medical_records.pdf",
  "content_type": "application/pdf",
  "file_size_bytes": 1048576
}

Response (200)

json
{
  "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"
POST/api/v1/uploads/:id/confirm

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)

json
{
  "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

StatusCodeDescription
404upload_not_foundUpload ID does not exist
409upload_already_confirmedUpload was already confirmed
422upload_url_expiredSigned URL expired before upload completed
422file_not_found_in_storageFile was not found at the upload URL