Skip to content

Document Management System

Overview

The SynthesQ document management system provides a unified, enterprise-grade solution for storing, organizing, and managing documents across all business modules. Built on a polymorphic architecture, the system enables any entity (customers, leads, products, invoices, employees, etc.) to have associated documents while maintaining consistent behavior and security controls.

Key Capabilities:

  • Polymorphic document storage for any entity type
  • 16 entity types across 5 modules (CRM, Operations, Sales, Finance, HRM)
  • Public and private storage options with secure access control
  • Document versioning and archival
  • File integrity verification with SHA-256 hashing
  • Rich metadata and tagging system
  • Document type categorization (23 document types)
  • Expiration date tracking
  • Download tracking and audit trail
  • Soft delete with archive functionality

System Architecture

Polymorphic Document Model

Documents use Laravel's polymorphic relationships to attach to any entity:

Polymorphic Fields:

  • documentable_type - The fully qualified class name of the parent entity
  • documentable_id - The ID of the parent entity

This allows a single shared_documents table to serve all modules while maintaining data isolation and tenant security.

Supported Entities

The document system supports 16 different entity types:

CRM Module (5 entities):

  • Customers
  • Leads
  • Opportunities
  • Contacts
  • Activities

Operations Module (3 entities):

  • Products
  • Suppliers
  • Purchase Orders

Sales Module (3 entities):

  • Orders
  • Invoices
  • Payments

Finance Module (2 entities):

  • Journal Entries
  • Chart of Accounts

HRM Module (3 entities):

  • Employees
  • Leave Requests
  • Performance Reviews

Document Types

The system supports 23 predefined document types, each with specific use cases:

TypeCategoryDescriptionTypical Use
contractLegal & ComplianceLegal contracts and agreementsCustomer contracts, supplier agreements
invoiceFinancialInvoice documentsSales invoices, purchase invoices
receiptFinancialPayment receiptsPayment confirmations, purchase receipts
quoteSalesPrice quotationsSales quotes, supplier quotes
proposalSalesBusiness proposalsSales proposals, vendor proposals
presentationBusinessPresentation filesSales presentations, product demos
specificationTechnicalTechnical specificationsProduct specs, project requirements
manualTechnicalUser manuals and guidesProduct manuals, operation guides
reportBusinessBusiness reportsFinancial reports, performance reports
certificateCredentialsCertificates and certificationsQuality certificates, compliance certs
licenseCredentialsLicense documentsSoftware licenses, business licenses
formAdministrativeForms and templatesApplication forms, registration forms
policyLegal & CompliancePolicy documentsCompany policies, procedures
procedureLegal & ComplianceProcedural documentsOperating procedures, workflows
marketingMarketingMarketing materialsBrochures, flyers, marketing content
legalLegal & ComplianceLegal documentsLegal notices, compliance documents
financialFinancialFinancial documentsFinancial statements, tax documents
technicalTechnicalTechnical documentationTechnical drawings, diagrams
imageMediaImage filesPhotos, screenshots, diagrams
videoMediaVideo filesProduct videos, training videos
audioMediaAudio filesVoice recordings, podcasts
archiveGeneralArchive filesZIP files, compressed archives
otherGeneralOther document typesMiscellaneous documents

Document Type Properties

Each document type has associated properties:

Sensitivity Flags:

  • contract, legal, financial, certificate, license are marked as sensitive by default
  • Sensitive documents require additional access controls

Approval Requirements:

  • contract, policy, procedure, legal, financial require approval workflows
  • These documents typically need management review before publication

Storage Architecture

Storage Disks

Documents can be stored on different storage disks based on visibility requirements:

Public Storage (public disk):

  • Files are publicly accessible via URL
  • Used for: Product images, marketing materials, public documents
  • Path: storage/app/public/documents/{entity_type}/{entity_id}/
  • Accessible via: https://api.crm.test/storage/documents/...

Private Storage (local disk):

  • Files require authentication to access
  • Used for: Contracts, financial documents, employee records, sensitive data
  • Path: storage/app/private/documents/{entity_type}/{entity_id}/
  • Accessible via: Download endpoint with authentication

Storage Organization

Documents are organized in entity-specific directories:

storage/
└── documents/
    ├── customers/
    │   └── 100001/
    │       ├── contract_abc123.pdf
    │       ├── invoice_def456.pdf
    │       └── profile_ghi789.jpg
    ├── products/
    │   └── 50001/
    │       ├── thumbnail_jkl012.jpg
    │       ├── manual_mno345.pdf
    │       └── spec_pqr678.pdf
    └── employees/
        └── 30001/
            ├── contract_stu901.pdf
            ├── id_document_vwx234.pdf
            └── certification_yza567.pdf

File Requirements and Limits

File Size Limits

File size limits vary by entity type based on business requirements:

Entity TypeMaximum SizeReasoning
Customer Documents50 MBLarge contracts, KYC documents
Customer Profile Images5 MBImage-specific optimization
Lead Documents20 MBProposals, specifications
Product Documents10 MBImages and product manuals
Supplier Documents50 MBLarge contracts, certifications
Employee Documents50 MBPersonnel files, certifications
Invoice Documents10 MBPDF invoices and receipts
Order Documents20 MBShipping documents, customs forms

Supported File Types

The system supports a wide range of file formats:

Images:

  • JPEG (.jpg, .jpeg)
  • PNG (.png)
  • GIF (.gif)
  • WebP (.webp)

Documents:

  • PDF (.pdf)
  • Microsoft Word (.doc, .docx)
  • Microsoft Excel (.xls, .xlsx)
  • Microsoft PowerPoint (.ppt, .pptx)

Archives:

  • ZIP (.zip)
  • RAR (.rar)
  • 7-Zip (.7z)

Media:

  • Videos (entity-specific)
  • Audio files (entity-specific)

Text:

  • Plain text (.txt)
  • CSV (.csv)
  • JSON (.json)
  • XML (.xml)

Document Tags

Tags provide a flexible categorization system beyond document types. Tags are entity-specific and reflect business workflows:

Tag Strategy

Purpose:

  • Fine-grained categorization within document types
  • Workflow-specific organization
  • Search and filtering
  • Business process alignment

Best Practices:

  • Use lowercase tags with underscores (e.g., proof_of_payment)
  • Keep tags consistent within entity type
  • Limit tags to 3-5 per document for manageability
  • Use tags that reflect business processes

API Operations

Common Endpoints

All entities follow a consistent endpoint pattern:

List Documents:

GET /api/v1/{module}/{entity}/{entityId}/documents

Upload Document:

POST /api/v1/{module}/{entity}/{entityId}/documents

Get Document Details:

GET /api/v1/{module}/{entity}/{entityId}/documents/{documentId}

Download Document:

GET /api/v1/{module}/{entity}/{entityId}/documents/{documentId}/download

Delete Document:

DELETE /api/v1/{module}/{entity}/{entityId}/documents/{documentId}

List Documents

Retrieve all documents attached to an entity with optional filtering.

Authentication: Required (Bearer token)

Query Parameters:

ParameterTypeDescriptionExample
typestringFilter by document typeinvoice
tagsstringComma-separated list of tagscontract,kyc
include_archivedbooleanInclude archived documentsfalse
per_pageintegerResults per page (1-100)25
pageintegerPage number1

Example Request:

bash
curl -X GET "https://api.crm.test/api/v1/crm/customers/100001/documents?type=contract&tags=kyc&per_page=10" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"

Success Response (200 OK):

json
{
  "data": [
    {
      "id": 5001,
      "type": {
        "value": "contract",
        "label": "Contract"
      },
      "status": {
        "value": "published",
        "label": "Published"
      },
      "name": "Customer Service Agreement",
      "description": "Annual service contract 2025",
      "file_size": 524288,
      "formatted_file_size": "512.00 KB",
      "mime_type": "application/pdf",
      "file_extension": "pdf",
      "is_public": false,
      "is_sensitive": true,
      "is_archived": false,
      "tags": ["contract", "kyc", "agreement"],
      "download_url": "https://api.crm.test/api/v1/crm/customers/100001/documents/5001/download",
      "expires_at": "2026-12-31T23:59:59.000000Z",
      "created_at": "2025-01-15T10:30:00.000000Z",
      "updated_at": "2025-01-15T10:30:00.000000Z"
    }
  ],
  "meta": {
    "total": 15,
    "per_page": 10,
    "current_page": 1,
    "last_page": 2
  }
}

Upload Document

Upload a new document and attach it to an entity.

Authentication: Required (Bearer token)

Content-Type: multipart/form-data

Request Body:

FieldTypeRequiredDescription
filefileYesThe document file to upload
typestringYesDocument type (see Document Types)
tagsarrayOptionalArray of tags for categorization
namestringOptionalCustom name (max 255 characters)
descriptionstringOptionalDocument description (max 1000 characters)
is_publicbooleanOptionalPublic visibility (default: false)
is_sensitivebooleanOptionalSensitive flag (default: false)
expires_atstringOptionalExpiration date (ISO 8601 format)

Example Request (cURL):

bash
curl -X POST https://api.crm.test/api/v1/crm/customers/100001/documents \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -F "file=@/path/to/contract.pdf" \
  -F "type=contract" \
  -F "tags[]=contract" \
  -F "tags[]=kyc" \
  -F "tags[]=agreement" \
  -F "name=Customer Service Agreement 2025" \
  -F "description=Annual service contract for premium support" \
  -F "is_public=false" \
  -F "is_sensitive=true" \
  -F "expires_at=2026-12-31T23:59:59Z"

Example Request (JavaScript):

javascript
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('type', 'contract');
formData.append('tags[]', 'contract');
formData.append('tags[]', 'kyc');
formData.append('name', 'Customer Service Agreement 2025');
formData.append('description', 'Annual service contract for premium support');
formData.append('is_public', 'false');
formData.append('is_sensitive', 'true');
formData.append('expires_at', '2026-12-31T23:59:59Z');

const response = await fetch('https://api.crm.test/api/v1/crm/customers/100001/documents', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
    'Accept': 'application/json'
  },
  body: formData
});

const data = await response.json();

Example Request (PHP):

php
$ch = curl_init();

$file = new CURLFile('/path/to/contract.pdf', 'application/pdf', 'contract.pdf');

curl_setopt_array($ch, [
    CURLOPT_URL => 'https://api.crm.test/api/v1/crm/customers/100001/documents',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer YOUR_ACCESS_TOKEN',
        'Accept: application/json'
    ],
    CURLOPT_POSTFIELDS => [
        'file' => $file,
        'type' => 'contract',
        'tags' => ['contract', 'kyc', 'agreement'],
        'name' => 'Customer Service Agreement 2025',
        'description' => 'Annual service contract for premium support',
        'is_public' => false,
        'is_sensitive' => true,
        'expires_at' => '2026-12-31T23:59:59Z'
    ]
]);

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);

Success Response (201 Created):

json
{
  "message": "Document uploaded successfully",
  "data": {
    "id": 5001,
    "type": {
      "value": "contract",
      "label": "Contract"
    },
    "status": {
      "value": "published",
      "label": "Published"
    },
    "name": "Customer Service Agreement 2025",
    "description": "Annual service contract for premium support",
    "file_size": 524288,
    "formatted_file_size": "512.00 KB",
    "mime_type": "application/pdf",
    "file_extension": "pdf",
    "is_public": false,
    "is_sensitive": true,
    "is_archived": false,
    "tags": ["contract", "kyc", "agreement"],
    "download_url": "https://api.crm.test/api/v1/crm/customers/100001/documents/5001/download",
    "expires_at": "2026-12-31T23:59:59.000000Z",
    "created_at": "2025-12-22T14:30:00.000000Z",
    "updated_at": "2025-12-22T14:30:00.000000Z"
  }
}

Download Document

Download the actual file content of a document.

Authentication: Required (Bearer token)

Example Request:

bash
curl -X GET https://api.crm.test/api/v1/crm/customers/100001/documents/5001/download \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  --output contract.pdf

Success Response (200 OK):

  • Returns the binary file content
  • Content-Disposition header includes original filename
  • Content-Type matches the document's MIME type
  • Download count is automatically incremented

Delete Document

Delete a document. By default, documents are soft-deleted (archived). Use hard delete to permanently remove the file.

Authentication: Required (Bearer token)

Query Parameters:

ParameterTypeDescriptionDefault
hard_deletebooleanPermanently delete the document and filefalse

Example Request (Soft Delete):

bash
curl -X DELETE https://api.crm.test/api/v1/crm/customers/100001/documents/5001 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"

Example Request (Hard Delete):

bash
curl -X DELETE "https://api.crm.test/api/v1/crm/customers/100001/documents/5001?hard_delete=true" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"

Success Response (200 OK):

json
{
  "message": "Document deleted successfully"
}

File Integrity and Security

SHA-256 File Hashing

Every uploaded document has a SHA-256 hash calculated for integrity verification:

Benefits:

  • Detects file corruption during transfer or storage
  • Enables duplicate detection across the system
  • Supports compliance and audit requirements
  • Verifies file authenticity

Hash Storage:

  • Calculated during upload
  • Stored in file_hash field
  • Available via API (with appropriate permissions)

Access Control

Authentication:

  • All document operations require authentication (Bearer token)
  • Laravel Sanctum manages token-based authentication

Authorization:

  • Document access follows entity-level permissions
  • Users must have permission to view/manage the parent entity
  • Sensitive documents require additional permissions

Tenant Isolation:

  • Multi-tenant architecture ensures complete data isolation
  • Documents are scoped to tenant database
  • Cross-tenant access is impossible by design

Data Privacy

Public vs Private Documents:

  • Public documents are accessible via direct URL (no authentication required)
  • Private documents require authenticated download endpoint
  • Default is private for security

Sensitive Documents:

  • Marked with is_sensitive flag
  • Require elevated permissions for access
  • File hash only visible to authorized users
  • Additional audit logging for access

Document Versioning

Version History

The system maintains complete document history through archival:

How Versioning Works:

  1. When a new document replaces an existing one (same tags/purpose), the old document is archived
  2. Archived documents remain in the database with full metadata
  3. Files are retained in storage for audit purposes
  4. Version history is accessible via include_archived=true parameter

Example: Customer Profile Image Versioning

bash
# Upload new profile image - old one automatically archived
POST /api/v1/crm/customers/100001/documents
{
  "file": <new_image>,
  "type": "image",
  "tags": ["profile", "avatar"]
}

# View all versions (including archived)
GET /api/v1/crm/customers/100001/documents?tags=profile&include_archived=true

Archival vs Deletion

Soft Delete (Archive):

  • Document marked as is_archived: true
  • Metadata retained in database
  • File may be retained in storage
  • Reversible operation
  • Default behavior for DELETE endpoint

Hard Delete:

  • Document permanently removed from database
  • File deleted from storage
  • Irreversible operation
  • Requires hard_delete=true parameter

Metadata and Custom Fields

Standard Metadata

All documents include standard metadata fields:

FieldTypeDescription
uploaded_byintegerUser ID who uploaded the document
download_countintegerNumber of times document was downloaded
last_downloaded_attimestampLast download timestamp
created_attimestampUpload timestamp
updated_attimestampLast modification timestamp
expires_attimestampOptional expiration date

Custom Metadata

The metadata JSON field allows entity-specific custom data:

Example Use Cases:

  • Image dimensions (width, height)
  • Display order for gallery images
  • Source system for imported documents
  • Processing status for uploaded forms
  • User agent and IP address tracking

Example Metadata:

json
{
  "metadata": {
    "width": 1920,
    "height": 1080,
    "display_order": 1,
    "source": "import_legacy_system",
    "uploaded_from_ip": "192.168.1.100",
    "user_agent": "Mozilla/5.0..."
  }
}

Error Handling

Validation Errors

File Size Exceeded (422 Unprocessable Entity):

json
{
  "message": "Document size must not exceed 50MB",
  "errors": {
    "file": ["Document size must not exceed 50MB"]
  }
}

Invalid Document Type (422 Unprocessable Entity):

json
{
  "message": "The selected type is invalid.",
  "errors": {
    "type": ["The selected type is invalid."]
  }
}

Invalid Tags (422 Unprocessable Entity):

json
{
  "message": "Invalid tag for customer documents.",
  "errors": {
    "tags.0": ["Invalid tag for customer documents. Allowed: profile, avatar, contract, identity, document, kyc, agreement, invoice, receipt"]
  }
}

Missing Required Field (422 Unprocessable Entity):

json
{
  "message": "The file field is required.",
  "errors": {
    "file": ["The file field is required."]
  }
}

Resource Errors

Entity Not Found (404 Not Found):

json
{
  "message": "Customer not found"
}

Document Not Found (404 Not Found):

json
{
  "message": "Document not found"
}

Unauthorized Access (401 Unauthorized):

json
{
  "message": "Unauthenticated"
}

Forbidden Access (403 Forbidden):

json
{
  "message": "This action is unauthorized"
}

Best Practices

1. Document Organization

Use Appropriate Document Types:

  • Select the most specific document type available
  • Avoid using other unless truly miscellaneous
  • Document type affects default permissions and workflows

Tag Strategically:

  • Use 1-3 tags per document
  • Keep tags consistent within entity type
  • Use tags that reflect your business processes
  • Combine generic and specific tags (e.g., contract + service_agreement)

Name Documents Clearly:

  • Use descriptive, human-readable names
  • Include dates or versions in names when appropriate
  • Example: "Service Agreement 2025", "Q4 2024 Financial Report"

2. Storage and Performance

Optimize File Sizes:

  • Compress large files before upload
  • Use appropriate image formats (WebP for web, JPEG for photos)
  • Target reasonable file sizes for document types

Use Public Storage When Appropriate:

  • Product images, marketing materials
  • Public documentation, brochures
  • Non-sensitive content
  • Improves performance by serving from CDN

Private Storage for Sensitive Data:

  • Contracts, financial documents
  • Employee records, HR documents
  • Customer KYC documents
  • Anything containing PII or confidential data

3. Security and Compliance

Mark Sensitive Documents:

  • Always set is_sensitive: true for confidential data
  • Enables additional audit logging
  • Restricts access to authorized users
  • Required for compliance (GDPR, HIPAA, etc.)

Use Expiration Dates:

  • Set expiration for time-limited documents
  • Examples: Temporary permits, trial agreements, promotional materials
  • System can automatically flag expired documents

Leverage Access Controls:

  • Rely on entity-level permissions
  • Don't make sensitive documents public
  • Regularly audit document access logs

4. Workflow Integration

Upload Documents in Context:

  • Upload documents when creating related entities
  • Example: Upload contract when closing a deal
  • Maintains business context and traceability

Archive Old Versions:

  • System handles this automatically for replacements
  • Manual archive when document is superseded
  • Retain for compliance and audit trail

Clean Up Periodically:

  • Review and remove truly obsolete documents
  • Follow data retention policies
  • Use hard delete for expired, non-compliance documents

5. Error Handling and Validation

Client-Side Validation:

  • Check file size before upload
  • Validate file type on client
  • Provide clear error messages to users
  • Implement retry logic for network failures

Handle Upload Failures Gracefully:

  • Check response status codes
  • Parse validation error messages
  • Retry transient failures
  • Log errors for debugging

Business Scenarios

Scenario 1: Customer Onboarding with KYC Documents

Context: New customer registration requires identity verification documents

Workflow:

  1. Customer created in CRM
  2. Upload KYC documents (ID, proof of address, etc.)
  3. Mark documents as sensitive
  4. Set expiration dates for temporary documents

API Calls:

bash
# Step 1: Create customer
POST /api/v1/crm/customers
{
  "first_name": "John",
  "last_name": "Doe",
  "email": "john.doe@example.com",
  "type": "individual"
}
# Response: { "data": { "id": 100001 } }

# Step 2: Upload ID document
POST /api/v1/crm/customers/100001/documents
{
  "file": <id_scan.pdf>,
  "type": "form",
  "tags": ["identity", "kyc"],
  "name": "Driver's License",
  "is_public": false,
  "is_sensitive": true,
  "expires_at": "2030-05-15T23:59:59Z"
}

# Step 3: Upload proof of address
POST /api/v1/crm/customers/100001/documents
{
  "file": <utility_bill.pdf>,
  "type": "form",
  "tags": ["kyc", "document"],
  "name": "Proof of Address - Utility Bill",
  "is_public": false,
  "is_sensitive": true
}

# Step 4: Verify all KYC documents uploaded
GET /api/v1/crm/customers/100001/documents?tags=kyc

Scenario 2: Product Catalog with Images and Manuals

Context: Adding products with gallery images and technical documentation

Workflow:

  1. Create product in Operations module
  2. Upload thumbnail image (public, for catalog display)
  3. Upload gallery images (public, for product page)
  4. Upload product manual (public, for customer download)
  5. Upload technical specifications (private, for internal use)

API Calls:

bash
# Step 1: Create product
POST /api/v1/operations/products
{
  "name": "Industrial Widget XL-2000",
  "sku": "WIDGET-XL-2000",
  "type": "physical"
}
# Response: { "data": { "id": 50001 } }

# Step 2: Upload thumbnail
POST /api/v1/operations/products/50001/documents
{
  "file": <thumbnail.jpg>,
  "type": "image",
  "tags": ["thumbnail", "primary"],
  "name": "Product Thumbnail",
  "is_public": true,
  "metadata": {
    "width": 800,
    "height": 800,
    "display_order": 1
  }
}

# Step 3: Upload gallery images
POST /api/v1/operations/products/50001/documents
{
  "file": <angle1.jpg>,
  "type": "image",
  "tags": ["gallery"],
  "name": "Product Image - Front View",
  "is_public": true,
  "metadata": { "display_order": 2 }
}

# Step 4: Upload product manual (public)
POST /api/v1/operations/products/50001/documents
{
  "file": <manual.pdf>,
  "type": "manual",
  "tags": ["manual"],
  "name": "User Manual - Widget XL-2000",
  "is_public": true
}

# Step 5: Upload internal specs (private)
POST /api/v1/operations/products/50001/documents
{
  "file": <specs.pdf>,
  "type": "specification",
  "tags": ["specification", "datasheet"],
  "name": "Technical Specifications (Internal)",
  "is_public": false
}

Scenario 3: Invoice Document Attachment

Context: Attach PDF invoice and payment receipt to sales invoice

Workflow:

  1. Create sales invoice
  2. Generate PDF invoice
  3. Upload PDF as document
  4. Customer makes payment
  5. Upload payment receipt
  6. Link both documents to invoice

API Calls:

bash
# Step 1: Create invoice (already exists)
# Invoice ID: 80001

# Step 2: Upload generated PDF invoice
POST /api/v1/sales/invoices/80001/documents
{
  "file": <invoice_80001.pdf>,
  "type": "invoice",
  "tags": ["invoice_pdf"],
  "name": "Invoice #INV-80001",
  "description": "Official invoice document",
  "is_public": false
}

# Step 3: Upload payment receipt after payment
POST /api/v1/sales/invoices/80001/documents
{
  "file": <receipt.pdf>,
  "type": "receipt",
  "tags": ["receipt", "proof_of_payment"],
  "name": "Payment Receipt",
  "description": "Stripe payment confirmation",
  "is_public": false
}

# Step 4: Retrieve all invoice documents
GET /api/v1/sales/invoices/80001/documents

Scenario 4: Employee Onboarding Documentation

Context: New employee requires contracts, certifications, and ID documents

Workflow:

  1. Create employee record
  2. Upload employment contract
  3. Upload ID documents
  4. Upload certifications
  5. Set expiration dates for time-limited documents

API Calls:

bash
# Step 1: Create employee
POST /api/v1/hrm/employees
{
  "first_name": "Jane",
  "last_name": "Smith",
  "email": "jane.smith@company.com",
  "employee_number": "EMP-30001"
}
# Response: { "data": { "id": 30001 } }

# Step 2: Upload employment contract
POST /api/v1/hrm/employees/30001/documents
{
  "file": <contract.pdf>,
  "type": "contract",
  "tags": ["contract"],
  "name": "Employment Contract 2025",
  "description": "Full-time employment agreement",
  "is_public": false,
  "is_sensitive": true
}

# Step 3: Upload ID documents
POST /api/v1/hrm/employees/30001/documents
{
  "file": <passport.pdf>,
  "type": "form",
  "tags": ["id_document"],
  "name": "Passport Copy",
  "is_public": false,
  "is_sensitive": true,
  "expires_at": "2030-08-20T23:59:59Z"
}

# Step 4: Upload certification
POST /api/v1/hrm/employees/30001/documents
{
  "file": <certification.pdf>,
  "type": "certificate",
  "tags": ["certification"],
  "name": "Professional Certification - PMP",
  "description": "Project Management Professional certification",
  "is_public": false,
  "expires_at": "2028-06-30T23:59:59Z"
}

Troubleshooting

Upload Issues

Problem: File upload fails with 413 Payload Too Large

Solution:

  • Check file size against entity-specific limits
  • Verify nginx/Apache configuration for upload limits
  • Confirm PHP upload_max_filesize and post_max_size settings

Problem: Upload succeeds but document not visible in list

Solution:

  • Verify document wasn't archived on upload
  • Check that filter parameters don't exclude the document
  • Confirm user has permissions to view the entity
  • Check include_archived=true if document might be archived

Download Issues

Problem: Download endpoint returns 404

Solution:

  • Confirm document ID is correct
  • Verify file exists in storage
  • Check storage disk configuration
  • Ensure user has permission to access parent entity

Problem: Public document URL not accessible

Solution:

  • Verify document is marked as is_public: true
  • Confirm storage link is created (php artisan storage:link)
  • Check APP_URL environment variable is correct
  • Verify file exists in public storage directory

Permission Issues

Problem: 403 Forbidden when accessing document

Solution:

  • Confirm user has permission on parent entity
  • Check role-based access control policies
  • Verify sensitive documents require elevated permissions
  • Ensure user belongs to correct tenant

Module-Specific Documentation

For detailed information on document management for specific modules and entities, see:

API Reference

For complete API specifications including all endpoints, request/response schemas, and interactive examples:

  • OpenAPI Specification: /docs/openapi.yaml
  • Interactive API Documentation: Scribe-generated documentation
  • Endpoint Group: Document Management (across all modules)

Documentation for SynthesQ CRM/ERP Platform