NEW AI agents now first-class: authorize · audit · revoke in one click — your agents submit cleanly, bots stay blocked. Read agent docs →
file uploads

Accept files without a storage backend.

Uploads go straight from the browser to object storage with a presigned URL — the bytes never transit our API — and link themselves to the submission. You add an upload field; we handle the storage.

How it works

A file field on a form is backed by a three-step presigned flow. The client asks for a short-lived upload URL, PUTs the file directly to storage, confirms it, then references the upload id in the submission. The file is validated against the form's limits (size, MIME type) before the URL is ever issued.

Enable uploads

Add a file field in the builder and set the per-form limits (max size, max files, allowed MIME types). Object storage must be enabled on the server — the presign call returns 503 if it isn't.

The upload flow

1. Ask for a presigned URL.

$ curl -X POST https://login.ollastack.com/api/forms/<formId>/uploads \
     -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
     -d '{ "fieldId": "resume", "filename": "cv.pdf", "mimeType": "application/pdf", "sizeBytes": 84211 }'
{"uploadId": "...", "uploadUrl": "https://...", "storageKey": "...", "expiresAt": "..."}

2. PUT the bytes straight to uploadUrl (not to our API).

$ curl -X PUT "<uploadUrl>" --data-binary @cv.pdf -H "Content-Type: application/pdf"

3. Confirm the upload completed.

$ curl -X POST https://login.ollastack.com/api/forms/<formId>/uploads/<uploadId>/confirm \
     -H "Authorization: Bearer <token>"

Include the upload ids in the submission payload (e.g. an uploadIds array). When the submission is stored, those uploads are automatically attached to it, so they show up alongside the lead in the inbox and aren't garbage-collected as orphans.

List + delete

GET /api/forms/{formId}/uploads lists a form's uploads; DELETE /api/forms/{formId}/uploads/{uploadId} removes one (and its stored object). Both need the forms:read / forms:write scope respectively.