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" }
FieldTypeRequiredDescription
mimeTypestringYesMIME 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

StatusReason
400mimeType is missing from the request body
401Not authenticated
403Insufficient 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" }
FieldTypeRequiredDescription
storagePathstringYesThe storage path of the object to read

Response

{
  "signedUrl": "https://supabase.co/storage/v1/object/sign/..."
}

Errors

StatusReason
400storagePath is missing
401Not authenticated
403Object does not belong to this org
404Object 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

ParamTypeDefaultMaxDescription
pageinteger11000Page number
pageSizeinteger24(internal max)Items per page
searchstringFilter 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.

FieldTypeRequiredDescription
storagePathstringYesStorage path from the upload-url step
namestringNoDisplay 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

ParamDescription
orgIdOrganization ID
imageIdImage ID

Response

{ "ok": true }

Errors

StatusReason
401Not authenticated
403Insufficient permission
404Image not found

Upload flow (end to end)

  1. Call POST /api/orgs/[orgId]/images/upload-url with { mimeType } to receive { uploadUrl, storagePath }.
  2. PUT the file directly to uploadUrl with the correct Content-Type header.
  3. Use storagePath as the imageStoragePath field when creating or updating a task, or as storagePath when saving to the org image library.
  4. Call POST /api/orgs/[orgId]/storage/read-url with the storagePath to get a signed read URL for display.