File uploads.
File uploads are a two-step flow: upload the file to get an ID, then submit your form with that ID as the field value.
How the flow works
- Enable uploads for the form and add a media field to the schema.
- POST the file as
multipart/form-datato the upload endpoint. - Receive an
uploadId(plus a temporary preview URL). - Submit your JSON form data with the
uploadIdas the field value.
1. Upload the file
Send the file as a single file part. Do not set the Content-Type header yourself — the browser must add the multipart boundary.
const form = new FormData();
form.append("file", fileInput.files[0]);
const upload = await fetch("https://contact.kirandhakal.me/v1/forms/YOUR_PUBLIC_KEY/upload", {
method: "POST",
body: form // do not set Content-Type; the browser adds the multipart boundary
});
if (!upload.ok) {
const error = await upload.json();
throw new Error(error.detail);
}
const { uploadId } = await upload.json();2. Submit the upload ID
Send the returned uploadId as the value of your media field. Contact verifies that the ID belongs to this form, then attaches the file to the submission.
await fetch("https://contact.kirandhakal.me/v1/forms/YOUR_PUBLIC_KEY/submissions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID()
},
body: JSON.stringify({
name: "Alex Morgan",
email: "alex@example.com",
attachment: uploadId
})
});data: URLs, arbitrary URLs, and storage object keys are rejected with 422 (INVALID_UPLOAD_REFERENCE).Limits and defaults
| Setting | Default | Range |
|---|---|---|
| Max file size | 10 MB | 1–100 MB (server cap) |
| Files per request | 1 | 1–20 |
| Files per field | 1 | 1–20 |
| Workspace storage | 1 GB | Server-configured |
Allowed file types
Contact inspects the file’s magic bytes and extension and requires the declared MIME type to agree with them. Supported types are:
- Images: JPEG, PNG, GIF, WebP
- Documents: PDF, DOC, DOCX
- Audio: MP3, WAV, OGG, M4A/AAC, FLAC
- Video: MP4/MOV, WebM
Restrict a field further with allowedTypes, using tokens such as image/*, application/pdf, or .png. Files that fail validation return 400 (UNSUPPORTED_FILE_TYPE) or 413 (FILE_TOO_LARGE).
Storage and lifecycle
Uploaded files are stored in object storage. Uploads that are never attached to a submission expire automatically, and files are deleted according to your retention settings. See the Privacy Policy for retention details and keep your own copies of anything you need to keep.