Spaces:
Running
Running
Document Service - UI Quick Reference
π Quick Start (3 Steps)
Step 1: Initialize Upload
POST /scm/storage/upload/init
Authorization: Bearer <JWT_TOKEN>
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)
// β οΈ NOT a backend endpoint - direct upload to MinIO
// Use the presigned_urls[0] from Step 1 response
PUT <presigned_urls[0]> // From Step 1 response
Content-Type: application/pdf
Body: <binary file data>
// 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
POST /scm/storage/upload/complete
Authorization: Bearer <JWT_TOKEN>
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:
headers: {
"Authorization": "Bearer <JWT_TOKEN>",
"Content-Type": "application/json"
}
JWT Payload Requirements:
user_id: User identifierusername: Usernamerole: buyer | supplier | ops | adminmerchant_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
POST /scm/storage/download-url
{ "object_id": "uuid" }
Option B: By Composite Key
POST /scm/storage/download-url
{
"domain": "po",
"entity_id": "PO-2024-001",
"category": "invoice",
"file_name": "invoice.pdf"
}
Response:
{
"url": "http://minio:9000/...",
"expires_in": 900 // 15 minutes
}
πΎ Get Metadata
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)
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
import DocumentUploadComponent from './DocumentUploadComponent';
<DocumentUploadComponent
token={jwtToken}
domain="po"
entityId="PO-2024-001"
category="invoice"
/>
π Common Workflows
Upload Invoice
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
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
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 guideDocumentUploadComponent.jsx- React component exampleDOCUMENT_TESTING_GUIDE.md- API testingapp/documents/README.md- Backend setup
Last Updated: January 14, 2026
API Version: 1.0.0
Status: Production Ready β