Documents API

Upload, download, and manage encrypted documents with classification levels.


Overview

The Documents API provides secure file storage with automatic encryption for sensitive documents. All documents are classified by sensitivity level, which determines encryption and retention policies.

Base URL: https://api.meister-bill.com/v1/documents


Authentication

All document endpoints require authentication via Bearer token:

Authorization: Bearer {jwt_token}

Document Classification

Level Encryption Retention Access
public None Unlimited Anyone
internal AES-256-GCM 365 days Organization members
confidential AES-256-GCM 90 days Organization members
restricted AES-256-GCM 30 days Owner only

Endpoints

Upload Document

Upload a file with classification metadata.

POST /v1/documents/upload
Content-Type: multipart/form-data
Authorization: Bearer {token}
Classification-Level: confidential
Entity-Type: invoice
Entity-Id: 550e8400-e29b-41d4-a716-446655440000
Expires-At: 2025-05-01T00:00:00Z

Headers:

Header Required Description
Classification-Level Yes Document classification level
Entity-Type No Associated entity type (invoice, quote, etc.)
Entity-Id No Associated entity UUID
Expires-At No Expiration date (ISO 8601)

Body: - file: Binary file data (multipart/form-data)

Response:

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "ownerId": "550e8400-e29b-41d4-a716-446655440002",
    "filename": "invoice_001.pdf",
    "originalName": "Invoice 001.pdf",
    "documentType": "invoice",
    "classification": "confidential",
    "mimeType": "application/pdf",
    "sizeBytes": 1048576,
    "isEncrypted": true,
    "entityType": "invoice",
    "entityId": "550e8400-e29b-41d4-a716-446655440000",
    "expiresAt": "2025-05-01T00:00:00Z",
    "createdAt": "2025-02-26T12:00:00Z"
  }
}

Errors:

Status Code Description
400 invalid_classification Invalid classification level
400 file_too_large File exceeds size limit (50MB)
401 unauthorized Invalid or missing token
413 payload_too_large Request body too large

Download Document

Download a document (automatically decrypted if encrypted).

GET /v1/documents/download/{fileId}
Authorization: Bearer {token}

Parameters:

Name Type Description
fileId UUID Document ID from upload response

Response: File stream with appropriate content type

Headers:

Header Value
Content-Type Original MIME type
Content-Disposition attachment; filename="{originalName}"
Content-Length File size in bytes
Cache-Control private, no-store
X-Document-Classification Classification level

Errors:

Status Code Description
403 access_denied Insufficient permissions
404 not_found Document not found
410 expired Document has expired

Get Document Metadata

Retrieve document metadata without downloading.

GET /v1/documents/{fileId}/metadata
Authorization: Bearer {token}

Response:

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "filename": "invoice_001.pdf",
    "originalName": "Invoice 001.pdf",
    "documentType": "invoice",
    "classification": "confidential",
    "mimeType": "application/pdf",
    "sizeBytes": 1048576,
    "isEncrypted": true,
    "entityType": "invoice",
    "entityId": "550e8400-e29b-41d4-a716-446655440000",
    "expiresAt": "2025-05-01T00:00:00Z",
    "createdAt": "2025-02-26T12:00:00Z"
  }
}

Get Document Audit Log

Retrieve access history for a document.

GET /v1/documents/{fileId}/audit-log
Authorization: Bearer {token}

Response:

{
  "success": true,
  "data": {
    "fileId": "550e8400-e29b-41d4-a716-446655440001",
    "fileName": "invoice_001.pdf",
    "entries": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440003",
        "action": "upload",
        "actorId": "550e8400-e29b-41d4-a716-446655440002",
        "actorEmail": "user@example.com",
        "ipAddress": "192.168.1.1",
        "userAgent": "Mozilla/5.0...",
        "createdAt": "2025-02-26T12:00:00Z"
      },
      {
        "id": "550e8400-e29b-41d4-a716-446655440004",
        "action": "download",
        "actorId": "550e8400-e29b-41d4-a716-446655440002",
        "actorEmail": "user@example.com",
        "ipAddress": "192.168.1.1",
        "userAgent": "Mozilla/5.0...",
        "createdAt": "2025-02-26T12:30:00Z"
      }
    ]
  }
}

Actions:

Action Description
upload Document uploaded
download Document downloaded
delete Document deleted
view Metadata viewed
share Share link created
expire Document expired

Delete Document

Permanently delete a document.

DELETE /v1/documents/{fileId}
Authorization: Bearer {token}

Response:

{
  "success": true,
  "message": "Document deleted successfully"
}

Note: Only the document owner can delete. This action is logged and irreversible.


List Documents

List documents with optional filtering.

GET /v1/documents?entityType=invoice&entityId={id}&page=1&limit=20
Authorization: Bearer {token}

Query Parameters:

Name Type Description
entityType string Filter by entity type
entityId UUID Filter by entity ID
classification string Filter by classification
page number Page number (default: 1)
limit number Items per page (default: 20, max: 100)

Response:

{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "filename": "invoice_001.pdf",
      "originalName": "Invoice 001.pdf",
      "classification": "confidential",
      "mimeType": "application/pdf",
      "sizeBytes": 1048576,
      "isEncrypted": true,
      "createdAt": "2025-02-26T12:00:00Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 150,
    "pages": 8
  }
}

Code Examples

Upload with JavaScript

async function uploadDocument(file, classification, entityType, entityId) {
  const formData = new FormData()
  formData.append('file', file)

  const response = await fetch('https://api.meister-bill.com/v1/documents/upload', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Classification-Level': classification,
      'Entity-Type': entityType,
      'Entity-Id': entityId,
    },
    body: formData,
  })

  if (!response.ok) {
    const error = await response.json()
    throw new Error(error.message)
  }

  return await response.json()
}

// Usage
const result = await uploadDocument(
  fileInput.files[0],
  'confidential',
  'invoice',
  '550e8400-e29b-41d4-a716-446655440000'
)
console.log('Uploaded:', result.data.id)

Download with Progress

async function downloadDocument(fileId, filename) {
  const response = await fetch(`https://api.meister-bill.com/v1/documents/download/${fileId}`, {
    headers: { 'Authorization': `Bearer ${token}` },
  })

  if (!response.ok) {
    throw new Error(`Download failed: ${response.status}`)
  }

  const contentLength = +response.headers.get('Content-Length')
  const reader = response.body.getReader()
  const chunks = []
  let received = 0

  while (true) {
    const { done, value } = await reader.read()
    if (done) break

    chunks.push(value)
    received += value.length

    const progress = (received / contentLength * 100).toFixed(1)
    console.log(`Download progress: ${progress}%`)
  }

  // Combine chunks and create download
  const blob = new Blob(chunks)
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = filename
  a.click()
  URL.revokeObjectURL(url)
}

List with Pagination

async function listDocuments(entityType, entityId, page = 1) {
  const params = new URLSearchParams({
    entityType,
    entityId,
    page: page.toString(),
    limit: '20',
  })

  const response = await fetch(`https://api.meister-bill.com/v1/documents?${params}`, {
    headers: { 'Authorization': `Bearer ${token}` },
  })

  return await response.json()
}

Limits

Limit Value
Max file size 50 MB
Max request size 55 MB
Max upload per hour 100 files
Max downloads per hour 1000 files

Security Notes

  1. Encryption Keys: Documents are encrypted with user-specific keys derived from a master key. The master key is stored only in Cloudflare Worker secrets.

  2. Access Control: Access is strictly enforced based on document classification and ownership.

  3. Audit Trail: Every access (upload, download, view, delete) is logged with IP address and timestamp.

  4. Expiration: Documents automatically expire based on classification level. Expired documents are permanently deleted.

  5. Download Security: Downloads use Cache-Control: private, no-store to prevent caching.


Error Codes

Code HTTP Description
invalid_classification 400 Invalid classification level
file_too_large 400 File exceeds 50MB limit
missing_file 400 No file in request
unauthorized 401 Invalid or missing token
access_denied 403 Insufficient permissions
not_found 404 Document not found
expired 410 Document has expired
payload_too_large 413 Request body too large
rate_limit_exceeded 429 Too many requests
encryption_error 500 Encryption/decryption failed
storage_error 500 R2 storage error

*Last updated: 2026-02-26