Skip to content

Tenant API v1 — Documents

Base path: /api/tenant/v1

Middleware: auth:tenant-api, tenant.user, not.suspended, verified, module:documents, plus permission middleware / policies.

Workspace must also be entitled to Storage. Uploads enforce Storage quota via WorkspaceStorageService::assertCanStore.

Stats

GET /documents/stats

Permission: documents.view

Same filters as list (minus pagination/sort). Payload:

total, categorized, uncategorized.

Documents CRUD

GET /documents

Permission: documents.view

Query: search (title, description, original_name), category_id, trashed (only | true | with), sort (title | original_name | size_bytes | created_at | updated_at, default created_at), direction, page, per_page.

List items include nested category / creator when loaded, plus original_name, mime, size_bytes.

POST /documents

Permission: documents.create

multipart/form-data:

FieldRules
titlerequired, string, max 255
descriptionoptional string, max 5000
category_idoptional; must exist in tenant document_categories and not soft-deleted
lead_idsoptional array of lead ids (requires entitled Leads + LinkableLead scope)
contact_idsoptional array of contact ids (requires entitled Contacts + LinkableContact scope)
company_idsoptional array of company ids (requires entitled Companies + LinkableCompany scope)
project_idsoptional array of project ids (requires entitled Projects + LinkableProject scope)
employee_idsoptional array of employee ids (requires entitled Employees + LinkableEmployee scope)
asset_idsoptional array of asset ids (requires entitled Assets + LinkableAsset scope)
task_idsoptional array of task ids (requires entitled Tasks + LinkableTask scope)
filerequired file, max 51200 KB (50 MB); mimes: pdf,doc,docx,xls,xlsx,ppt,pptx,txt,csv,rtf,odt,ods,odp,jpg,jpeg,png,gif,webp,zip,rar,7z

Stores under the tenant Documents directory on the uploads disk. Counts toward Storage used bytes.

GET /documents/{id}

Permission: documents.view. Soft-deleted rows return not found on show.

Includes category, creator, and links (with label per linkable) when loaded. Does not expose path / disk.

PUT /documents/{id}

Permission: documents.update

JSON body (partial): title, description, category_id, and any of the optional *_ids link arrays above. Omitted link arrays leave that type unchanged; send [] to clear links for a type.

POST /documents/{id}

Permission: documents.update (named route documents.update.post)

multipart/form-data (partial): title, description, category_id, optional link arrays (lead_ids[], …), optional file (same mime/size rules as create). Replacing a larger file checks quota on the positive size delta only.

File replace: use this POST twin — PHP does not populate uploaded files on a true HTTP PUT. The official SPA posts FormData here (no _method spoofing required). Method-spoofed POST + _method=PUT also works.

POST /documents/bulk-delete

Permission: documents.delete

Body: { "ids": [1, 2, …] } — required array, 1–100 distinct positive integers.

Soft-deletes active documents the actor is allowed to delete (uploader or workspace owner). Already-trashed / missing / unauthorized ids are listed under failed. Response data: processed (int), failed ([{ id, message }]). Returns 422 when processed === 0.

POST /documents/bulk-force-delete

Permission: documents.force.delete

Body: same ids shape as bulk-delete.

Permanently deletes soft-deleted documents the actor is allowed to force-delete (uploader or workspace owner) and removes objects from storage. Active documents are reported in failed (must soft-delete first). Returns 422 when processed === 0.

DELETE /documents/{id}

Soft delete. Permission: documents.delete, and the actor must be the uploader (created_by) or the workspace owner. Soft-deleted bytes stop counting toward Storage used. Objects remain on disk until force delete or workspace trash retention purge (trash:purge-expired). Already-trashed rows return not found.

POST /documents/{id}/restore

Restore a soft-deleted document. Permission: documents.restore. Re-checks Storage quota for the document's size_bytes (soft-deleted rows are excluded from used bytes until restored). Returns 422 with STORAGE_QUOTA_EXCEEDED when restore would exceed allowance.

DELETE /documents/{id}/force

Permanently delete a soft-deleted document and remove the object from storage. Permission: documents.force.delete, and the actor must be the uploader or the workspace owner. Active documents return 400 (must soft-delete first).

GET /documents/{id}/download

Permission: documents.view (policy download). Streams the original file (Content-Disposition attachment using original_name). Soft-deleted or missing objects → 404.

Document categories

GET /document-categories

Permission: documents.view

Paginated list ordered by sort_order, then name.

POST /document-categories

Permission: documents.create

Body:

FieldRules
namerequired, string, max 255
slugoptional; unique per tenant; defaults to Str::slug(name)
sort_orderoptional integer (default 0)
is_activeoptional boolean (default true)

GET /document-categories/{id}

Permission: documents.view

PUT /document-categories/{id}

Permission: documents.update

Partial update of name, slug, sort_order, is_active. Changing name without slug regenerates the slug.

DELETE /document-categories/{id}

Soft delete. Permission: documents.delete. Fails with 422 if the category still has documents.

POST /document-categories/{id}/restore

Permission: documents.restore

DELETE /document-categories/{id}/force

Permanent delete. Permission: documents.force.delete. Fails with 422 if any documents (including soft-deleted) still reference the category.

Official documentation for the EloSync SaaS Platform.