Upload without declaring a content type
Send a file to POST /assets without a contentType and the API derives one from the filename, so a client stops carrying its own MIME table.
If you upload through the API, you have had to answer a question the API already knew the answer to.
POST /assets returns a presigned URL, and the PUT that follows carries a Content-Type header. That header is not signed, so whatever the client sends is what storage keeps. Send nothing and most HTTP clients default to application/json, which storage stores as the object’s real type. The upload returns success, the asset reads as ready, and the failure only appears one step later, when a job reads that asset and refuses it as an input.
The workaround was for every client to carry its own filename-to-MIME table.
Now the API derives it. Omit contentType and the response tells you what to send:
{ "status": "presigned", "data": { "id": "ast_...", "contentType": "video/mp4" }, "upload": { "method": "PUT", "url": "https://..." }}Echo that value back on the PUT and the stored object is correct.
curl -X POST https://api.rendobar.com/assets \ -H "Authorization: Bearer $RENDOBAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"filename":"clip.mp4","size":1048576}'An explicit contentType still wins. If you know better than the filename, send it and the API keeps yours untouched.
Two details worth knowing. An unrecognised extension resolves to application/octet-stream rather than a guess, and .svg resolves there too: outputs serve from a CDN, and an SVG served as image/svg+xml executes script in the browser, so it downloads instead.
Nothing changes for assets you already uploaded. The download path has always derived the type when the stored one was missing, so existing objects serve exactly as before.
Connectors benefit first, because they upload on your behalf and cannot see the file. The Activepieces piece carried its own table for exactly this reason and no longer needs one.
