Skip to content

Documents API

List documents for a person. Defaults to the viewer’s own documents. Executives, heads, and managers can filter by person (subject to hierarchy checks).

Permission: view:documents

Query Parameters:

ParameterTypeDefaultDescription
person_idstringViewer’s ownPerson ID to list documents for

Response:

{
"documents": [
{
"id": "uuid",
"person_id": "uuid",
"category": "employment_agreement",
"name": "Employment Agreement 2026",
"file_key": "documents/person-id/uuid.pdf",
"file_name": "agreement.pdf",
"file_size": 245000,
"content_type": "application/pdf",
"expiry_date": null,
"uploaded_by": "uuid",
"uploaded_by_name": "Jane Smith",
"created_at": "2026-03-04T10:00:00"
}
]
}

Scoping rules:

  • Executive: Any person
  • Head: Own + reporting subtree
  • Manager: Own + direct reports
  • Lead/Employee: Own only (person_id param ignored)

Download a document file from R2 storage.

Permission: view:documents + hierarchy check on document’s person

Response: Binary file with Content-Disposition: attachment header.


Upload a new document. Sent as multipart/form-data.

Permission: update:documents + hierarchy check on target person

Form Fields:

FieldTypeRequiredDescription
person_idstringYesPerson to attach document to
categorystringYesOne of: employment_agreement, superannuation, visa, certificate, policy, other
namestringYesHuman-readable document name
expiry_datestringNoExpiry date (YYYY-MM-DD) for visas/certificates
fileFileYesDocument file

Allowed file types: PDF, DOC, DOCX, XLS, XLSX, JPEG, PNG

Max file size: 10 MB

Response (201):

{
"id": "uuid"
}

Errors:

  • 400 — Missing fields, invalid category, invalid file type, file too large
  • 403 — No access to upload for this person
  • 404 — Person not found

Delete a document and its R2 file.

Permission: update:documents + hierarchy check on document’s person

Response:

{
"ok": true
}

Errors:

  • 403 — No access to delete for this person
  • 404 — Document not found

Documents are stored in R2 under the key pattern documents/{person_id}/{uuid}.{ext}, using the existing UPLOADS R2 bucket binding. Files are served through the Worker (not directly from R2), so no CORS configuration is required.