Skip to content

Files

Endpoints for uploading, registering, attaching, and retrieving files associated with mentorships.

Base path: /api/files

Authentication: All endpoints require a valid session and an organization context.


Endpoints

Method Path Min Role Description
POST /api/files/presign Any Request a presigned PUT URL for direct S3/R2 upload
POST /api/files/register Any Register a file in the database after a direct upload
POST /api/files/upload Any Upload a file directly via multipart form (legacy)
POST /api/files/:fileId/attach Coach Attach a registered file to a mentorship object
DELETE /api/files/:fileId/attach/:objectType/:objectId Coach Detach a file from an object
GET /api/files/:fileId Any Get file metadata and a presigned download URL
GET /api/files/mentorship/:mentorshipId/attachments Any List files attached to a mentorship

Upload Flow

The recommended upload flow avoids proxying binary data through the backend:

1. POST /api/files/presign        → { presignedUrl, storageKey }
2. PUT  <presignedUrl>             → upload directly to S3/R2 (browser XHR, with progress)
3. POST /api/files/register       → { fileId }
4. POST /api/files/:id/attach     → { attachmentId }

Allowed File Types and Size Limits

MIME type pattern Max size
image/* 100 MB
video/* 500 MB
text/* 100 MB
application/pdf 100 MB
application/zip, application/x-zip-compressed 100 MB

Requests exceeding these limits or using an unsupported MIME type are rejected before any upload occurs.


POST /api/files/presign

Request a presigned PUT URL to upload a file directly to S3/R2 storage. After a successful PUT, call POST /api/files/register to create the database record.

Request body:

{
  "filename": "report.pdf",
  "mimeType": "application/pdf",
  "sizeBytes": 204800,
  "prefix": "mentorships"
}
Field Type Required Constraints
filename string Yes Original filename including extension
mimeType string Yes Must be an allowed MIME type (see table above)
sizeBytes integer Yes File size in bytes; must not exceed the type limit
prefix string No Storage key prefix (e.g. mentorships, avatars); default: uploads

Response 200:

{
  "data": {
    "presignedUrl": "https://r2.example.com/bucket/mentorships/uuid.pdf?X-Amz-Signature=...",
    "storageKey": "mentorships/uuid.pdf"
  }
}

Error responses:

Code Condition
400 Unsupported MIME type
413 File size exceeds the limit for this MIME type

POST /api/files/register

Create the database record for a file that was uploaded directly to S3/R2 using a presigned URL.

Request body:

{
  "storageKey": "mentorships/uuid.pdf",
  "filename": "report.pdf",
  "mimeType": "application/pdf",
  "sizeBytes": 204800
}
Field Type Required Description
storageKey string Yes The key returned by POST /api/files/presign
filename string Yes Original filename
mimeType string Yes MIME type of the file
sizeBytes integer Yes File size in bytes

Response 201:

{
  "data": {
    "fileId": "file-uuid"
  }
}

POST /api/files/upload

Upload a file directly to the backend using multipart form data. Includes optional targets for automatically updating avatar or organization logo.

Prefer presign flow

The presign → direct-upload → register flow is preferred for large files and provides upload progress feedback. Use /api/files/upload only for simple cases.

Request body: multipart/form-data

Field Type Required Description
file file Yes The file to upload
prefix string No Storage key prefix (default: uploads)
target string No avatar to update the current user's avatar; orgLogo to update an org logo
organizationId string No Required when target = 'orgLogo'

Response 201:

{
  "data": {
    "fileId": "file-uuid",
    "sha256Hash": "abc123...",
    "storageKey": "uploads/uuid.pdf",
    "mimeType": "application/pdf",
    "sizeBytes": 204800
  }
}

Error responses:

Code Condition
400 file field missing or MIME type not allowed
413 File size exceeds the limit

POST /api/files/:fileId/attach

Attach a registered file to a mentorship object.

Min role: Coach

Access rules:

  • Coach can only attach files to their own mentorships.
  • Manager, OrganizationAdmin, PlatformAdmin can attach to any mentorship in their org.

Path parameters:

Parameter Type Description
fileId string File UUID

Request body:

{
  "objectType": "mentorship",
  "objectId": "mentorship-uuid"
}
Field Type Required Constraints
objectType string Yes Object type to attach to. Currently only mentorship is supported
objectId string Yes UUID of the target object

Response 201:

{
  "data": {
    "attachmentId": "attachment-uuid",
    "fileId": "file-uuid",
    "objectType": "mentorship",
    "objectId": "mentorship-uuid"
  }
}

Error responses:

Code Condition
403 Coach attaching to another coach's mentorship; org mismatch
404 File not found; or target mentorship not found
409 File is already attached to this object

DELETE /api/files/:fileId/attach/:objectType/:objectId

Detach a file from an object. If this was the last attachment for the file, the file record and the underlying storage object are deleted (orphan cleanup).

Min role: Coach

Path parameters:

Parameter Type Description
fileId string File UUID
objectType string Object type the file is attached to
objectId string UUID of the object

Response: 204 No Content

Error responses:

Code Condition
400 Missing path parameters
403 Caller does not have access to this attachment
404 Attachment not found

GET /api/files/:fileId

Get metadata for a file and a short-lived presigned URL to download it.

Access rules:

  • PlatformAdmin: can access any file.
  • Direct participants (coach or teacher of the mentorship the file is attached to) can access the file.
  • Manager / OrganizationAdmin can access files within their org.

Path parameters:

Parameter Type Description
fileId string File UUID

Response 200:

{
  "data": {
    "id": "file-uuid",
    "filename": "report.pdf",
    "mimeType": "application/pdf",
    "sizeBytes": 204800,
    "sha256Hash": "abc123...",
    "presignedUrl": "https://r2.example.com/bucket/mentorships/uuid.pdf?X-Amz-Signature=...",
    "createdAt": "2025-04-01T12:00:00.000Z"
  }
}

Error responses:

Code Condition
403 Caller does not have access to this file
404 File not found

GET /api/files/mentorship/:mentorshipId/attachments

List all files attached to a specific mentorship. Each file includes a short-lived presigned download URL.

Access rules:

  • PlatformAdmin: can list attachments for any mentorship.
  • Coach and teacher participants can access files from their own mentorship.
  • Manager / OrganizationAdmin can access files within their org.

Path parameters:

Parameter Type Description
mentorshipId string Mentorship UUID

Response 200:

{
  "data": [
    {
      "attachmentId": "attachment-uuid",
      "fileId": "file-uuid",
      "filename": "report.pdf",
      "mimeType": "application/pdf",
      "sizeBytes": 204800,
      "presignedUrl": "https://...",
      "createdAt": "2025-04-01T12:00:00.000Z"
    }
  ]
}

Error responses:

Code Condition
403 Caller does not have access to this mentorship's files
404 Mentorship not found

R2 / S3 CORS Configuration

When using the presigned upload flow, the storage bucket must allow PUT requests from the frontend origin. Configure this in the Cloudflare R2 dashboard:

Cloudflare R2 → Bucket → Settings → CORS policy:

[
  {
    "AllowedOrigins": ["https://yourapp.com", "http://localhost:8081"],
    "AllowedMethods": ["PUT"],
    "AllowedHeaders": ["Content-Type"],
    "MaxAgeSeconds": 3600
  }
]