# Document Service - UI Quick Reference ## 🚀 Quick Start (3 Steps) ### Step 1: Initialize Upload ```javascript POST /scm/storage/upload/init Authorization: Bearer Content-Type: application/json { "domain": "po", "entity_id": "PO-2024-001", "category": "invoice", "file_name": "invoice.pdf", "mime_type": "application/pdf", "file_size": 2097152, "visibility": "private" } Response: { "upload_id": "uuid", "presigned_urls": ["http://minio:9000/..."], "object_key": "tenant_001/po/PO-2024-001/invoice/invoice.pdf" } ``` ### Step 2: Upload File to MinIO (Direct) ```javascript // ⚠️ NOT a backend endpoint - direct upload to MinIO // Use the presigned_urls[0] from Step 1 response PUT // From Step 1 response Content-Type: application/pdf Body: // NO Authorization header needed (presigned URL is tenant-scoped) // Calculate checksum while uploading const fileBuffer = await file.arrayBuffer(); const checksum = await crypto.subtle.digest('SHA-256', fileBuffer); ``` **Note**: This step bypasses the backend API and uploads directly to MinIO for performance. The presigned URL is scoped to the tenant and expires in 15 minutes. ### Step 3: Complete Upload ```javascript POST /scm/storage/upload/complete Authorization: Bearer Content-Type: application/json { "upload_id": "uuid from step 1", "checksum_sha256": "hex_string" } Response: { "id": "object_id", "deduplicated": false } ``` --- ## 📋 Endpoints Reference | Method | Endpoint | Purpose | Auth Required | |--------|----------|---------|---------------| | POST | `/scm/storage/upload/init` | Initialize upload | ✅ | | POST | `/scm/storage/upload/complete` | Finalize upload | ✅ | | POST | `/scm/storage/download-url` | Get download URL | ✅ | | GET | `/scm/storage/{object_id}` | Get metadata | ✅ | | DELETE | `/scm/storage/{object_id}` | Delete object | ✅ | --- ## 🔑 Authentication All requests must include JWT token: ```javascript headers: { "Authorization": "Bearer ", "Content-Type": "application/json" } ``` **JWT Payload Requirements:** - `user_id`: User identifier - `username`: Username - `role`: buyer | supplier | ops | admin - `merchant_id`: Tenant identifier --- ## 🎯 Allowed Domains by Role | Role | PO | GRN | Inventory | Dispatch | Returns | Promotions | Profile | |------|----|----|-----------|----------|---------|-----------|---------| | buyer | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | | supplier | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | | ops | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | admin | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | **Delete Permissions**: Only ops and admin (legal_hold can block) --- ## 📦 File Limits | Type | Max Size | MIME Types | |------|----------|-----------| | Images | 10 MB | image/jpeg, image/png, image/webp, image/gif | | Documents | 50 MB | application/pdf, application/msword, application/vnd.ms-excel, text/csv | | Exports | 50 MB | Any (temporary) | --- ## 📊 Bucket Routing (Automatic) | Type | Bucket | Visibility | |------|--------|-----------| | Documents | `cutra-scm-documents` | Private only | | Images | `cutra-scm-images` | Private | | Images | `cutra-scm-images-public` | Public | | Exports | `cutra-scm-exports` | Temporary | --- ## ❌ Error Codes | Code | Meaning | Solution | |------|---------|----------| | 400 | Bad Request | Check request payload, verify all required fields | | 403 | Forbidden | Check user role/domain permissions | | 404 | Not Found | Object deleted or doesn't exist | | 409 | Conflict | Checksum mismatch - file corrupted during upload | | 413 | Too Large | File exceeds size limit | | 415 | Wrong Type | MIME type not allowed | --- ## 🔍 Download File (2 Options) ### Option A: By Object ID ```javascript POST /scm/storage/download-url { "object_id": "uuid" } ``` ### Option B: By Composite Key ```javascript POST /scm/storage/download-url { "domain": "po", "entity_id": "PO-2024-001", "category": "invoice", "file_name": "invoice.pdf" } ``` **Response:** ```javascript { "url": "http://minio:9000/...", "expires_in": 900 // 15 minutes } ``` --- ## 💾 Get Metadata ```javascript GET /scm/storage/{object_id} Response: { "id": "uuid", "file_name": "invoice.pdf", "mime_type": "application/pdf", "file_size": 2097152, "visibility": "private", "domain": "po", "entity_id": "PO-2024-001", "category": "invoice", "checksum_sha256": "hex", "created_at": "2024-01-14T10:30:00Z", "legal_hold": false } ``` --- ## 🗑️ Delete Object (Soft Delete) ```javascript DELETE /scm/storage/{object_id} Response: { "status": "deleted" } Notes: - Object marked as deleted (not removed) - Audit trail preserved - Returns 404 on subsequent fetches - Only ops/admin can delete - Legal hold prevents deletion ``` --- ## ♻️ Deduplication Same file uploaded twice? - **First upload**: `{id: "uuid1", deduplicated: false}` - **Second upload**: `{id: "uuid1", deduplicated: true}` **Benefits:** - Saves storage - Single copy for identical files - Service returns existing ID --- ## 🔐 Security Features ✅ **Tenant Isolation** - Cross-tenant access impossible - All queries filtered by merchant_id ✅ **Presigned URLs** - 15-minute expiry (900s) - Tenant-scoped - No auth headers in URL ✅ **RBAC** - Role-based domain access - Buyer ≠ Supplier domains - Delete restricted to ops/admin ✅ **Soft Delete** - Audit trail with timestamps - Legal hold enforcement - Compliance ready ✅ **Checksum Validation** - SHA-256 verification - Detects file corruption - Prevents tampering --- ## 📱 React Component Usage ```javascript import DocumentUploadComponent from './DocumentUploadComponent'; ``` --- ## 📝 Common Workflows ### Upload Invoice ```javascript POST /scm/storage/upload/init { "domain": "po", "entity_id": "PO-2024-001", "category": "invoice", "file_name": "invoice_001.pdf", "mime_type": "application/pdf", "file_size": 1048576, "visibility": "private" } ``` ### Upload Merchant Logo ```javascript POST /scm/storage/upload/init { "domain": "profile", "entity_id": "MERCHANT-001", "category": "profile", "file_name": "logo.png", "mime_type": "image/png", "file_size": 524288, "visibility": "public" } ``` ### Share Document ```javascript POST /scm/storage/download-url { "object_id": "uuid" } // Share the returned presigned URL (expires in 900s) ``` --- ## 🛠️ Troubleshooting | Issue | Solution | |-------|----------| | 403 Forbidden | Check user role has access to domain | | 404 Not Found | Object deleted or wrong ID | | 409 Checksum Error | File corrupted - retry upload | | 413 Too Large | Reduce file size or check limits | | Upload stuck | Check internet connection, retry | | No download | Verify presigned URL not expired (900s max) | --- ## 🚀 Production Checklist - [ ] Use production JWT tokens - [ ] Enable HTTPS for all requests - [ ] Set up MinIO with SSL - [ ] Configure CORS for domain - [ ] Monitor upload bandwidth - [ ] Set up error logging - [ ] Test with various file types - [ ] Verify multi-tenancy isolation - [ ] Set legal hold for compliance docs - [ ] Monitor presigned URL usage --- ## 📚 Related Files - `UI_INTEGRATION_GUIDE.md` - Detailed integration guide - `DocumentUploadComponent.jsx` - React component example - `DOCUMENT_TESTING_GUIDE.md` - API testing - `app/documents/README.md` - Backend setup --- **Last Updated**: January 14, 2026 **API Version**: 1.0.0 **Status**: Production Ready ✅