Accept file uploads in a form (multipart), no storage
Add file uploads to a form without S3, signed URLs, or an upload server — point a multipart form at one endpoint, set size and type limits, and files arrive.
File uploads are the part of forms that usually drags in real backend work: a place to store the file, signed URLs, size and type validation, virus-of-the-week paranoia. If the upload is attached to a form submission (a résumé on a job form, a screenshot on a bug report), a hosted form backend can take all of it — you just need a multipart form.
The form
The only requirement is enctype="multipart/form-data" and a file input:
<form action="https://login.ollastack.com/api/submit/your-slug"
method="POST" enctype="multipart/form-data">
<input name="name" required />
<input name="email" type="email" required />
<input name="resume" type="file" accept=".pdf,.doc,.docx" required />
<input type="text" name="_gotcha" style="display:none" tabindex="-1" />
<button>Apply</button>
</form>
That’s it — the file arrives attached to the submission, alongside the text fields. No JavaScript required, no separate upload endpoint.
From fetch()
Send FormData directly — don’t JSON-stringify it. The browser sets the multipart boundary for you:
const fd = new FormData(formEl); // includes the <input type="file">
await fetch("https://login.ollastack.com/api/submit/your-slug", {
method: "POST",
body: fd, // no Content-Type header — let the browser set it
});
A common mistake: setting Content-Type: application/json (or any explicit Content-Type) on a FormData body. Don’t — the browser must set multipart/form-data; boundary=… itself.
Limits and validation
Two settings worth configuring on the form:
- Max file size — reject oversized uploads at the edge rather than after transfer.
- Allowed types — restrict to the extensions/MIME types you actually want (
.pdf, images, etc.). Theacceptattribute on the input is a client-side hint; the server-side limit is the real boundary.
Multiple files work too — use multiple on the input or several file inputs; they all arrive with the submission.
What you get without building it
- The file is stored and attached to the submission — visible in the inbox.
- The text fields and the file arrive together as one submission, so you don’t reconcile an upload with a form post.
- The same spam and rate-limit protections apply to the submission as a whole.
When you’d still build your own
If you need very large files (hundreds of MB), resumable uploads, or direct-to-storage with your own bucket and lifecycle rules, a dedicated upload pipeline is the right tool. For the common case — a document or image attached to a form — the multipart endpoint removes the whole project.
Create a form with uploads — set the size and type limits in settings; the free tier includes uploads.
Frequently asked questions
How do I accept file uploads on a form?
Use a multipart/form-data form with a file input pointed at the endpoint. The file arrives attached to the submission — no S3, signed URLs, or upload server needed.
Can I limit file size and type?
Yes — set max size and allowed types in the form settings; the server-side limit is the real boundary (the accept attribute is only a client hint).
When should I build my own upload pipeline?
For very large files, resumable uploads, or direct-to-your-own-bucket with custom lifecycle rules. For documents and images attached to a form, the multipart endpoint is enough.
Last updated June 19, 2026. Spotted something out of date? Email hello@ollastack.com.