API REFERENCE
Upload a public file
On this page
/api/uploadUpload one file of at most 4 MiB (4,194,304 bytes) to public Supabase Storage. Authenticate with exactly one x-api-key or Authorization: Bearer credential for an existing Recoup account; authentication precedes multipart parsing. The uploader is derived from that account and recorded as uploaded_by metadata, never a client-supplied account override.
The returned URL is public: anyone with it can read the file without authentication. Use only content intended for public access. This replaces the public upload destination; it does not move private user-files, release-pack source audio, or existing files. Previously issued Arweave URLs remain readable; no new Arweave write is part of this operation.
Send multipart/form-data with exactly one file field and let your HTTP client set the multipart boundary. Supported MIME types are image/jpeg, image/png, image/gif, image/webp, application/pdf, text/csv, text/markdown, text/x-markdown, text/plain, application/json, audio/mpeg, audio/wav, audio/x-m4a, audio/webm. The server checks binary signatures, UTF-8 text and JSON syntax as appropriate; a filename extension or MIME declaration alone does not establish a valid file. HTML, SVG, executable files and unspecified binary types are not supported. The streamed request body is limited to 4,259,840 bytes (4 MiB plus 64 KiB); the file itself remains limited to 4 MiB.
Authentication
x-api-key in header
bearerAuth bearer
Request
curl --request POST \
--url 'https://api.recoupable.dev/api/upload' \
--header 'x-api-key: YOUR_API_KEY' \
--form 'file=@YOUR_FILE_PATH'Try it
Fill in the fields, send the request from your browser, and read the live response. The curl below updates as you type.
Kept in this browser tab only and cleared when it closes.
curl --request POST \
--url 'https://api.recoupable.dev/api/upload' \
--header 'Content-Type: multipart/form-data' \
--data '{
"file": "string"
}'Run this one from the terminal with the curl above.
Request body required
multipart/form-data
filestringrequiredExactly one nonempty file, at most 4 MiB (4,194,304 bytes). The MIME type must be supported; filename extensions do not override validation.
format: binary
Responses
200The file was stored in public Supabase Storage. The returned URL does not require authentication to read.+
application/json
successbooleanrequiredfileNamestringrequiredName of the uploaded file.
fileTypestring · enumrequiredAccepted MIME type of the uploaded file.
Values: "image/jpeg", "image/png", "image/gif", "image/webp", "application/pdf", "text/csv", "text/markdown", "text/x-markdown", "text/plain", "application/json", "audio/mpeg", "audio/wav", "audio/x-m4a", "audio/webm"
fileSizeintegerrequiredUploaded file size in bytes.
minimum: 1 · maximum: 4194304
urlstringrequiredPublic Supabase Storage URL. Anyone with this URL can read the object without authentication; this is not a signed private URL.
format: uri
{
"success": true,
"fileName": "cover.png",
"fileType": "image/png",
"fileSize": 1024,
"url": "https://exampleproject.supabase.co/storage/v1/object/public/public-uploads/example/cover.png"
}400Invalid multipart request (including a non-multipart Content-Type), missing file, empty file, or unsupported form fields. Submit exactly one nonempty file field.+
application/json
successbooleanrequirederrorstringrequiredHuman-readable error. Storage-provider details are not returned.
401Missing, invalid or conflicting credentials, or no existing account for the credential. Authentication is checked before multipart parsing.+
application/json
successbooleanrequirederrorstringrequiredHuman-readable error. Storage-provider details are not returned.
413The file exceeds 4 MiB (4,194,304 bytes), or the full streamed request exceeds 4,259,840 bytes (4 MiB plus 64 KiB of multipart overhead).+
application/json
successbooleanrequirederrorstringrequiredHuman-readable error. Storage-provider details are not returned.
415Unsupported file MIME type, mismatched binary signature, invalid UTF-8 text, or invalid JSON.+
application/json
successbooleanrequirederrorstringrequiredHuman-readable error. Storage-provider details are not returned.
500Upload failed. The response does not expose storage-provider details.+
application/json
successbooleanrequirederrorstringrequiredHuman-readable error. Storage-provider details are not returned.
Full specification
Download the OpenAPI file for complete schemas, constraints, and examples.
Download uploads.jsonView operation source
{
"operationId": "uploadPublicFile",
"summary": "Upload a public file",
"description": "Upload one file of at most 4 MiB (4,194,304 bytes) to public Supabase Storage. Authenticate with exactly one x-api-key or Authorization: Bearer credential for an existing Recoup account; authentication precedes multipart parsing. The uploader is derived from that account and recorded as uploaded_by metadata, never a client-supplied account override.\n\nThe returned URL is public: anyone with it can read the file without authentication. Use only content intended for public access. This replaces the public upload destination; it does not move private user-files, release-pack source audio, or existing files. Previously issued Arweave URLs remain readable; no new Arweave write is part of this operation.\n\nSend multipart/form-data with exactly one file field and let your HTTP client set the multipart boundary. Supported MIME types are `image/jpeg`, `image/png`, `image/gif`, `image/webp`, `application/pdf`, `text/csv`, `text/markdown`, `text/x-markdown`, `text/plain`, `application/json`, `audio/mpeg`, `audio/wav`, `audio/x-m4a`, `audio/webm`. The server checks binary signatures, UTF-8 text and JSON syntax as appropriate; a filename extension or MIME declaration alone does not establish a valid file. HTML, SVG, executable files and unspecified binary types are not supported. The streamed request body is limited to 4,259,840 bytes (4 MiB plus 64 KiB); the file itself remains limited to 4 MiB.",
"security": [
{
"apiKeyAuth": []
},
{
"bearerAuth": []
}
],
"requestBody": {
"required": true,
"content": {
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/PublicUploadRequest"
}
}
}
},
"responses": {
"200": {
"description": "The file was stored in public Supabase Storage. The returned URL does not require authentication to read.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PublicUploadResponse"
},
"example": {
"success": true,
"fileName": "cover.png",
"fileType": "image/png",
"fileSize": 1024,
"url": "https://exampleproject.supabase.co/storage/v1/object/public/public-uploads/example/cover.png"
}
}
}
},
"400": {
"description": "Invalid multipart request (including a non-multipart Content-Type), missing file, empty file, or unsupported form fields. Submit exactly one nonempty file field.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PublicUploadError"
}
}
}
},
"401": {
"description": "Missing, invalid or conflicting credentials, or no existing account for the credential. Authentication is checked before multipart parsing.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PublicUploadError"
}
}
}
},
"413": {
"description": "The file exceeds 4 MiB (4,194,304 bytes), or the full streamed request exceeds 4,259,840 bytes (4 MiB plus 64 KiB of multipart overhead).",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PublicUploadError"
}
}
}
},
"415": {
"description": "Unsupported file MIME type, mismatched binary signature, invalid UTF-8 text, or invalid JSON.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PublicUploadError"
}
}
}
},
"500": {
"description": "Upload failed. The response does not expose storage-provider details.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PublicUploadError"
}
}
}
}
}
}Use this endpoint for files intended to be publicly accessible, including public profile images and attachments that may be shared by URL. It accepts one file up to 4 MiB (4,194,304 bytes) and returns its name, MIME type, byte size and URL.
Send a file
Send exactly one credential: an API key or an existing Bearer token. The uploader
comes from the authenticated account; do not include account_id, uploaded_by
or another ownership override. This route requires an existing account.
curl --request POST 'https://api.recoupable.dev/api/upload' \
--header "x-api-key: $RECOUP_API_KEY" \
--form 'file=@cover.png;type=image/png'Let your HTTP client set Content-Type and its multipart boundary. Send exactly
one file part and no other form fields. The streamed request body is limited to
4,259,840 bytes (4 MiB plus 64 KiB of multipart overhead); the 4 MiB limit
applies to the file itself.
Supported types are JPEG, PNG, GIF, WebP, PDF, CSV, Markdown, plain text, JSON, MP3, WAV, M4A and audio WebM. The OpenAPI reference lists the exact accepted MIME types. HTML, SVG, executables and unspecified binary types are not supported. Server signature, UTF-8 and JSON checks still apply even if a browser accepts the file locally.
Existing files and private media
New uploads use Supabase Storage, not Arweave. Previously issued Arweave links
remain readable; this endpoint does not migrate or delete existing objects.
Private Files in user-files and release-pack source audio
continue to use their separate private-storage workflows.
The public response includes success, fileName, fileType, fileSize and
url. A handled error includes success: false and error. A failed request
does not establish whether a previous upload succeeded; do not treat this as an
idempotent replacement API.