From 133223fbe3f4b33602f656b0a9f6839502b704be Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Wed, 29 Apr 2026 03:14:08 +0530 Subject: [PATCH 1/6] docs: add /api/upload (Arweave file upload) Documents the new dedicated `POST /api/upload` endpoint on api which is parity for chat's `/api/upload`. Adds an "Upload" group under the Content tab. --- api-reference/openapi/upload.json | 123 ++++++++++++++++++++++++++++++ api-reference/upload/file.mdx | 4 + docs.json | 6 ++ 3 files changed, 133 insertions(+) create mode 100644 api-reference/openapi/upload.json create mode 100644 api-reference/upload/file.mdx diff --git a/api-reference/openapi/upload.json b/api-reference/openapi/upload.json new file mode 100644 index 0000000..cafd8cd --- /dev/null +++ b/api-reference/openapi/upload.json @@ -0,0 +1,123 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Recoup API - Upload", + "description": "API documentation for the Recoup platform - an AI agent platform for the music industry", + "license": { + "name": "MIT" + }, + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://recoup-api.vercel.app" + } + ], + "paths": { + "/api/upload": { + "post": { + "description": "Uploads a file to the Arweave permanent storage network and returns a fetchable HTTPS gateway URL. Accepts a single file via multipart/form-data under the field name `file`. No authentication required.", + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": ["file"], + "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "The file bytes to upload." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "File uploaded successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadResponse" + } + } + } + }, + "500": { + "description": "Upload failed (no file provided or Arweave error)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadErrorResponse" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "UploadResponse": { + "type": "object", + "required": [ + "success", + "fileName", + "fileType", + "fileSize", + "url" + ], + "properties": { + "success": { + "type": "boolean", + "description": "Always `true` on a 200 response.", + "example": true + }, + "fileName": { + "type": "string", + "description": "The original file name from the upload.", + "example": "cover.png" + }, + "fileType": { + "type": "string", + "description": "The MIME type of the uploaded file. Falls back to `application/octet-stream` if the client did not provide one.", + "example": "image/png" + }, + "fileSize": { + "type": "integer", + "description": "The size of the uploaded file in bytes.", + "example": 24576 + }, + "url": { + "type": "string", + "description": "Fetchable HTTPS Arweave gateway URL.", + "example": "https://arweave.net/abc123" + } + } + }, + "UploadErrorResponse": { + "type": "object", + "required": [ + "success", + "error" + ], + "properties": { + "success": { + "type": "boolean", + "description": "Always `false` on an error response.", + "example": false + }, + "error": { + "type": "string", + "description": "Error message describing what went wrong.", + "example": "No file provided" + } + } + } + } + } +} diff --git a/api-reference/upload/file.mdx b/api-reference/upload/file.mdx new file mode 100644 index 0000000..137c943 --- /dev/null +++ b/api-reference/upload/file.mdx @@ -0,0 +1,4 @@ +--- +title: 'Upload File' +openapi: '/api-reference/openapi/upload.json POST /api/upload' +--- diff --git a/docs.json b/docs.json index c339033..6665665 100644 --- a/docs.json +++ b/docs.json @@ -235,6 +235,12 @@ "api-reference/sandboxes/file", "api-reference/sandboxes/upload" ] + }, + { + "group": "Upload", + "pages": [ + "api-reference/upload/file" + ] } ] }, From 9fd5fe4a66106ee2310fd99ac406878fdf25513e Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Sat, 9 May 2026 07:53:23 +0530 Subject: [PATCH 2/6] docs(api-reference): drop Arweave references from upload endpoint --- api-reference/openapi/upload.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api-reference/openapi/upload.json b/api-reference/openapi/upload.json index cafd8cd..aa594d3 100644 --- a/api-reference/openapi/upload.json +++ b/api-reference/openapi/upload.json @@ -16,7 +16,7 @@ "paths": { "/api/upload": { "post": { - "description": "Uploads a file to the Arweave permanent storage network and returns a fetchable HTTPS gateway URL. Accepts a single file via multipart/form-data under the field name `file`. No authentication required.", + "description": "Uploads a file and returns a permanent CDN-fronted HTTPS URL. Accepts a single file via multipart/form-data under the field name `file`. No authentication required.", "requestBody": { "required": true, "content": { @@ -47,7 +47,7 @@ } }, "500": { - "description": "Upload failed (no file provided or Arweave error)", + "description": "Upload failed (no file provided, unsupported MIME type, or storage error)", "content": { "application/json": { "schema": { @@ -94,8 +94,8 @@ }, "url": { "type": "string", - "description": "Fetchable HTTPS Arweave gateway URL.", - "example": "https://arweave.net/abc123" + "description": "Permanent fetchable HTTPS URL for the uploaded file.", + "example": "https://example.supabase.co/storage/v1/object/public/public-uploads/abc123.png" } } }, From d44f8601a3163dbb684948e5732f26898b83ec48 Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Sat, 9 May 2026 07:59:00 +0530 Subject: [PATCH 3/6] chore: refresh PR head ref after branch rename From 46ce85f36ae3625ccb4de2f8e82205b14a70b1de Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Wed, 27 May 2026 23:20:38 +0530 Subject: [PATCH 4/6] docs(upload): require auth, correct 4xx vs 500 status codes Documents the auth requirement on POST /api/upload (Bearer token or x-api-key, matching validateAuthContext on the server) via a global + per-operation security definition. Splits the previously over-broad 500 into: - 400 for malformed multipart / missing file - 401 for missing or invalid auth - 415 for unsupported / typeless MIME (no octet-stream fallback) - 500 only for genuine internal storage failures Adds clarification that the server stamps the caller's accountId onto the storage object's metadata. Co-Authored-By: Claude Opus 4.7 (1M context) --- api-reference/openapi/upload.json | 62 ++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/api-reference/openapi/upload.json b/api-reference/openapi/upload.json index aa594d3..938fb36 100644 --- a/api-reference/openapi/upload.json +++ b/api-reference/openapi/upload.json @@ -13,10 +13,18 @@ "url": "https://recoup-api.vercel.app" } ], + "security": [ + { "bearerAuth": [] }, + { "apiKeyAuth": [] } + ], "paths": { "/api/upload": { "post": { - "description": "Uploads a file and returns a permanent CDN-fronted HTTPS URL. Accepts a single file via multipart/form-data under the field name `file`. No authentication required.", + "description": "Uploads a file and returns a permanent CDN-fronted HTTPS URL. Accepts a single file via multipart/form-data under the field name `file`. The caller's accountId is stamped onto the storage object's metadata as `uploaded_by` for audit purposes.", + "security": [ + { "bearerAuth": [] }, + { "apiKeyAuth": [] } + ], "requestBody": { "required": true, "content": { @@ -28,7 +36,7 @@ "file": { "type": "string", "format": "binary", - "description": "The file bytes to upload." + "description": "The file bytes to upload. The file's Content-Type must be on the server allowlist (image/*, application/pdf, text/csv, text/markdown, text/plain, application/json, audio/mpeg, audio/wav, audio/x-m4a, audio/webm). Typeless / octet-stream uploads are rejected." } } } @@ -46,8 +54,38 @@ } } }, + "400": { + "description": "Bad request — multipart body could not be parsed or no `file` field was provided.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadErrorResponse" + } + } + } + }, + "401": { + "description": "Missing or invalid authentication. Provide either an `Authorization: Bearer ` header or `x-api-key`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadErrorResponse" + } + } + } + }, + "415": { + "description": "Unsupported file type — the file's Content-Type is not on the server allowlist or is empty / `application/octet-stream`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadErrorResponse" + } + } + } + }, "500": { - "description": "Upload failed (no file provided, unsupported MIME type, or storage error)", + "description": "Internal storage error. Full details are logged server-side.", "content": { "application/json": { "schema": { @@ -61,6 +99,20 @@ } }, "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT", + "description": "Privy-issued access token, passed as `Authorization: Bearer `." + }, + "apiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "x-api-key", + "description": "Account or organization API key." + } + }, "schemas": { "UploadResponse": { "type": "object", @@ -84,7 +136,7 @@ }, "fileType": { "type": "string", - "description": "The MIME type of the uploaded file. Falls back to `application/octet-stream` if the client did not provide one.", + "description": "The MIME type of the uploaded file, exactly as provided by the client. Must be on the server allowlist.", "example": "image/png" }, "fileSize": { @@ -113,7 +165,7 @@ }, "error": { "type": "string", - "description": "Error message describing what went wrong.", + "description": "Human-readable error message. For 500 errors this is intentionally generic; full details are logged server-side.", "example": "No file provided" } } From 1bedac3c4b6daf402946ce01f18f68b5989a60f8 Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Thu, 28 May 2026 00:34:40 +0530 Subject: [PATCH 5/6] docs(upload): drop servers override, use spec default Co-Authored-By: Claude Opus 4.7 (1M context) --- api-reference/openapi/upload.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/api-reference/openapi/upload.json b/api-reference/openapi/upload.json index 938fb36..d5dbe52 100644 --- a/api-reference/openapi/upload.json +++ b/api-reference/openapi/upload.json @@ -8,11 +8,6 @@ }, "version": "1.0.0" }, - "servers": [ - { - "url": "https://recoup-api.vercel.app" - } - ], "security": [ { "bearerAuth": [] }, { "apiKeyAuth": [] } From 3b2c8f6762dff44834b3e7750facb9163b929c4c Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Thu, 28 May 2026 00:45:00 +0530 Subject: [PATCH 6/6] docs(upload): align auth schemes with sandbox/sessions - list apiKeyAuth before bearerAuth (api key is the primary external auth method) - drop bearerFormat + Privy-specific description so the scheme matches the bare shape used by other specs - drop the redundant per-operation security override (top-level already covers it) Co-Authored-By: Claude Opus 4.7 (1M context) --- api-reference/openapi/upload.json | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/api-reference/openapi/upload.json b/api-reference/openapi/upload.json index d5dbe52..670fe30 100644 --- a/api-reference/openapi/upload.json +++ b/api-reference/openapi/upload.json @@ -9,17 +9,13 @@ "version": "1.0.0" }, "security": [ - { "bearerAuth": [] }, - { "apiKeyAuth": [] } + { "apiKeyAuth": [] }, + { "bearerAuth": [] } ], "paths": { "/api/upload": { "post": { "description": "Uploads a file and returns a permanent CDN-fronted HTTPS URL. Accepts a single file via multipart/form-data under the field name `file`. The caller's accountId is stamped onto the storage object's metadata as `uploaded_by` for audit purposes.", - "security": [ - { "bearerAuth": [] }, - { "apiKeyAuth": [] } - ], "requestBody": { "required": true, "content": { @@ -95,17 +91,14 @@ }, "components": { "securitySchemes": { - "bearerAuth": { - "type": "http", - "scheme": "bearer", - "bearerFormat": "JWT", - "description": "Privy-issued access token, passed as `Authorization: Bearer `." - }, "apiKeyAuth": { "type": "apiKey", "in": "header", - "name": "x-api-key", - "description": "Account or organization API key." + "name": "x-api-key" + }, + "bearerAuth": { + "type": "http", + "scheme": "bearer" } }, "schemas": {