FriendChise Docs
Images and File Uploads
Endpoints for uploading, listing, and managing images attached to tasks and organizations
FriendChise stores images in Supabase Storage. The upload flow is two-step: first obtain a presigned upload URL, then PUT the file directly to that URL from the client. Reading signed URLs for existing images is handled separately.
Get a presigned upload URL
POST /api/orgs/[orgId]/images/upload-url
Returns a short-lived presigned URL that the client uses to upload an image directly to Supabase Storage. The response includes the storage path to use in subsequent task create/update requests.
Authentication
Requires PermissionAction.MANAGE_TASKS in the org.
Request body
{ "mimeType": "image/jpeg" }
| Field | Type | Required | Description |
|---|---|---|---|
mimeType | string | Yes | MIME type of the file to upload (e.g. image/jpeg, image/png, image/webp) |
Response
{
"uploadUrl": "https://supabase.co/storage/v1/object/sign/...",
"storagePath": "orgs/org_01abc/images/3d2d2f5a-7b7f-4c0f-92a1-1b4f1f9f3f58.jpg"
}
Use uploadUrl to PUT the file. Pass storagePath as imageStoragePath when creating or updating a task.
Errors
| Status | Reason |
|---|---|
400 | mimeType is missing from the request body |
401 | Not authenticated |
403 | Insufficient permission |
Get a signed read URL
POST /api/orgs/[orgId]/storage/read-url
Returns a short-lived signed URL for reading a private storage object. Use this to load images that are not served via public URLs.
Authentication
Requires org membership.
Request body
{ "storagePath": "orgs/org_01abc/tasks/tsk_01abc/photo.jpg" }
| Field | Type | Required | Description |
|---|---|---|---|
storagePath | string | Yes | The storage path of the object to read |
Response
{
"signedUrl": "https://supabase.co/storage/v1/object/sign/..."
}
Errors
| Status | Reason |
|---|---|
400 | storagePath is missing |
401 | Not authenticated |
403 | Object does not belong to this org |
404 | Object not found |
List org images
GET /api/orgs/[orgId]/images
Returns a paginated list of images saved to the organization's image library, with signed read URLs.
Authentication
Requires PermissionAction.MANAGE_TASKS in the org.
Query parameters
| Param | Type | Default | Max | Description |
|---|---|---|---|---|
page | integer | 1 | 1000 | Page number |
pageSize | integer | 24 | (internal max) | Items per page |
search | string | — | — | Filter by image name |
Response
{
"images": [
{
"id": "img_01abc",
"name": "Doughnut glaze photo",
"storagePath": "orgs/org_01abc/images/img_01abc.jpg",
"signedUrl": "https://supabase.co/storage/v1/object/sign/..."
}
],
"totalCount": 4,
"totalPages": 1,
"page": 1,
"pageSize": 24,
"omittedCount": 0
}
Save image to org library
POST /api/orgs/[orgId]/images
Saves an uploaded image to the organization's image library.
Authentication
Requires org image management permission.
Request body
Accepts JSON or FormData.
| Field | Type | Required | Description |
|---|---|---|---|
storagePath | string | Yes | Storage path from the upload-url step |
name | string | No | Display name for the image |
Response
{ "id": "img_01abc" }
Delete org image
DELETE /api/orgs/[orgId]/images/[imageId]
Removes an image from the organization's image library and deletes it from storage.
Authentication
Requires org image management permission.
Path parameters
| Param | Description |
|---|---|
orgId | Organization ID |
imageId | Image ID |
Response
{ "ok": true }
Errors
| Status | Reason |
|---|---|
401 | Not authenticated |
403 | Insufficient permission |
404 | Image not found |
Upload flow (end to end)
- Call
POST /api/orgs/[orgId]/images/upload-urlwith{ mimeType }to receive{ uploadUrl, storagePath }. - PUT the file directly to
uploadUrlwith the correctContent-Typeheader. - Use
storagePathas theimageStoragePathfield when creating or updating a task, or asstoragePathwhen saving to the org image library. - Call
POST /api/orgs/[orgId]/storage/read-urlwith thestoragePathto get a signed read URL for display.
