DocsUploads & storage
Uploads and storage
Send respondent files directly to Google Drive, S3-compatible storage, or Box.
Choose a storage provider
Fillo does not turn uploads into a separate file silo. Connect storage once at the workspace level, then choose a destination per form. The browser sends bytes to that provider while Fillo keeps the response-safe file reference and verifies completion.
| Provider | Use it when | Transfer path |
|---|---|---|
| Google Drive | Your team already works from Drive and wants OAuth-managed access. | Browser-direct, resumable Drive upload. |
| Amazon S3 / Cloudflare R2 | You want bucket ownership, lifecycle rules, and infrastructure-level control. | Browser-direct multipart upload with scoped signed requests. |
| Box | Your organization keeps operational files in Box. | Browser-direct upload; larger files use Box upload sessions. |
Connection and destination are separate
A workspace connection makes a provider available. A form's File storage setting decides which connected provider receives that form's files.
Prepare an S3 or R2 bucket for recoverable deletion
Fillo accepts an S3-compatible connection only after it proves the permissions and provider behavior needed for browser-direct multipart uploads, crash recovery, and permanent file deletion. The same erasure capability is checked again before respondent bytes are accepted or materialized.
- Use a network-reachable HTTPS endpoint and keep the bucket private. Scope credentials to the
fillo/prefix with object read, write, and delete; multipart create, list, and abort; bucket-version listing; and lifecycle-read permissions. - Allow browser
PUTrequests from every hosted or embedded form origin in the bucket CORS policy. - For AWS S3, use a bucket that has never had Versioning enabled. Enabled and suspended versioning are rejected. MFA Delete and Object Lock must also be off because they can prevent immediate erasure.
- Cloudflare R2 does not implement object versioning. Fillo accepts that behavior only for recognized Cloudflare R2 endpoints; an unknown S3-compatible endpoint that skips the check fails closed.
- Add an enabled
AbortIncompleteMultipartUploadlifecycle rule coveringfillo/with 7 days or fewer. - Connection verification creates an empty multipart upload, proves it can be rediscovered and aborted, then creates empty sentinel objects and proves permanent deletion. It never uploads respondent content during the check.
Connect, assign, and publish
Connect the provider
Open Settings → Connections. Connect Google, Box, or an S3-compatible bucket. Keep OAuth grants and bucket credentials limited to the workspace that needs them.
Assign it to the form
Open the form's Settings → File storage and select the destination. A connection can serve several forms, but each form chooses its own active provider.
Add file fields and publish
Use the builder or a code-defined
file_uploadfield (<Fillo.FileUpload>in JSX). A form that collects files cannot be published without a supported destination. A live file form also cannot remove its destination; unpublish it or select another destination first.Test the real path
Submit a representative file from the hosted form or embed, confirm it appears in the provider, then open it from the response drawer as a workspace member.
What happens during an upload
- The SDK asks Fillo for an upload session bound to the form, field, provider, filename, MIME type, and expected size.
- The browser transfers bytes to Drive, S3/R2, or Box. Provider credentials and OAuth refresh tokens stay server-side; the browser receives only the scoped capability needed for that upload.
- The SDK reports completion to Fillo. The storage adapter verifies the provider result before the file reference can be submitted with the response.
- The response stores file metadata and a provider reference, not a public provider credential. Opening a response file through Fillo requires signed-in workspace access.
Do not treat a client callback as proof
A custom uploader still needs Fillo's completion call. The server-side adapter verifies the object or provider file before accepting the reference.
Custom upload UI and resume handles
The default React and DOM renderers already run the upload protocol. If your headless screen owns the file picker, call client.uploadFile() and store the returned file value in the form answer.
For reload-safe recovery, persist the sessionId and uploadToken supplied to onSession, then pass them back with the same file. Resume behavior follows the provider protocol; do not reuse a handle for a different file or field.
const fileValue = await client.uploadFile(formId, file, {
fieldId: "attachment",
onProgress: ({ fraction }) => setProgress(fraction),
onSession: ({ sessionId, uploadToken }) => {
saveResumeHandle({ sessionId, uploadToken });
},
});Switch or disconnect storage
Changing a form's destination affects new upload sessions. Existing responses keep references to the provider that accepted their files, so switching providers does not migrate historical objects.
- Let active uploads finish or ask respondents to restart after the change; completion rejects a session if the form's storage changed mid-upload.
- Unpublish a live form with file fields before removing its destination, or choose another connected destination in the same settings panel.
- Keep the old provider connection while historical files still need to open or be erased. If access is revoked externally, reconnect the same account or bucket before attempting cleanup.
- Provider lifecycle rules are yours. For S3/R2, retain Fillo's object prefix and incomplete-multipart cleanup requirements when changing bucket policy.
Troubleshoot uploads
| Symptom | Check |
|---|---|
| The form cannot publish | A file field exists but the form has no connected storage destination. |
| S3/R2 fails before transfer | Reopen the connection and verify endpoint, region, bucket permissions, CORS, and multipart lifecycle access. |
| Completion says storage changed | The form switched providers during the session. Restart the upload against the current destination. |
| A historical file will not open | Confirm the viewer is a workspace member and the original provider connection can still read that object. |
| A resumed upload is rejected | Use the original file, field, session id, and upload token; expired or completed sessions must restart. |