Skip to main content

POST /api/upload

Uploads an image file to Supabase Storage and returns its public URL. Use this URL when submitting a challenge completion via POST /api/completions.
This endpoint requires a valid Bearer token in the Authorization header.

Request

Send the request as multipart/form-data with the image attached under the field name image.
The maximum file size is 10 MB. Only image files are accepted (image/* MIME types). Supported formats include JPEG, PNG, GIF, WebP, and HEIC. Requests with non-image files are rejected with a 400 error.
image
file
required
The image file to upload. Must use the form field name image. Accepted MIME types: image/jpeg, image/png, image/gif, image/webp, image/heic. Maximum size: 10 MB.

Response

200 OK
success
boolean
required
true when the upload succeeded.
data
object
required

Error responses

StatusDescription
400No file was included in the request, or the file’s MIME type is not an image.
401Missing or invalid Bearer token.
500Supabase Storage upload failed. The response includes the upstream error message.

Example

curl -X POST http://localhost:3000/api/upload \
  -H "Authorization: Bearer <accessToken>" \
  -F "image=@/path/to/photo.jpg"
{
  "success": true,
  "data": {
    "imageUrl": "https://<project>.supabase.co/storage/v1/object/public/completion-images/42/1743170700000-abc123xyz.jpg"
  }
}
Alternative: direct Supabase Storage uploadThe frontend SDK (frontend/lib/storage.ts) uploads images directly to the same completion-images bucket in Supabase Storage, bypassing this endpoint. Both paths produce a public URL in the same format. Use this API endpoint when uploading from a server-side context or a custom client that does not have direct Supabase Storage access.

Build docs developers (and LLMs) love