Feature guide

File uploads

Accept file attachments (résumés, screenshots, documents) right on your form. Uploads are stored privately and surfaced as secure download links in your dashboard and email notifications. Available on paid plans.

The one thing to get right: A form that uploads files must set enctype="multipart/form-data" on the <form>tag. Without it the browser sends only the file's name, not its contents.

Add a file input

Add an <input type="file">and set the form encoding. That's the whole change. Everything else works like a normal submission.

apply.html
<form action="https://shipmyform.com/f/frm_8Kx2mQ9pL4vN" method="POST" enctype="multipart/form-data">
  <label>Name
    <input type="text" name="name" required>
  </label>
  <label>Email
    <input type="email" name="email" required>
  </label>

  <!-- The file input -->
  <label>Résumé
    <input type="file" name="resume">
  </label>

  <!-- Spam honeypot: keep it hidden and empty -->
  <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off">

  <button type="submit">Apply</button>
</form>

Accept several files

Add multipleto let one field take a few files, or use several file inputs, up to your plan's per-submission limit.

apply.html
<label>Attachments
  <input type="file" name="attachments" multiple>
</label>

Submitting with JavaScript

If you post with fetch(), send a FormData body,notJSON. JSON can't carry files. Building the body straight from the form keeps the file parts intact.

submit.js
const form = document.querySelector("form");

const res = await fetch(form.action, {
  method: "POST",
  body: new FormData(form),          // includes the files
  headers: { Accept: "application/json" },
});

const data = await res.json();       // { ok: true }

The generators in our form generator already add the multipart encoding and a FormData submit when you include a file field.

Where the files go

Uploaded files are stored in private object storage, never on a public URL. In your dashboard, each file appears as a download link on the submission. Email notifications include a signed link that works without a login and expires after a few days. Every download is served as an attachment, so nothing can render or execute in the browser.

Limits by plan

PlanPer filePer submissionTotal storage
FreeNo uploads
Starter10 MB5 files2 GB
Pro25 MB10 files25 GB

Storage frees up as submissions age out under your retention policy. You can watch usage against your plan on the Usage page.

Error responses

If an upload is rejected, the response body includes an error code:

file_uploads_unavailable
The form is on the Free plan (or uploads aren't configured on the server). Upgrade to Starter or Pro to accept files.
file_too_large
One of the files is bigger than your plan's per-file limit.
too_many_files
The submission has more files than your plan allows.
too_large
The whole request exceeded the body size limit, usually several large files at once.
Security: The storage bucket is private, downloads are authorized (a dashboard session or a signed, expiring link), and files are always served as attachments from the storage domain, never inline from your form's origin. We don't virus-scan uploads, so treat files from untrusted submitters with the usual care.