Skip to main content

DocsUploads & storage

Uploads and storage

Send respondent files directly to Google Drive, S3-compatible storage, or Box.

View as Markdownllms.txt for agents

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.

ProviderUse it whenTransfer path
Google DriveYour team already works from Drive and wants OAuth-managed access.Browser-direct, resumable Drive upload.
Amazon S3 / Cloudflare R2You want bucket ownership, lifecycle rules, and infrastructure-level control.Browser-direct multipart upload with scoped signed requests.
BoxYour 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 PUT requests 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 AbortIncompleteMultipartUpload lifecycle rule covering fillo/ 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

  1. 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.

  2. 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.

  3. Add file fields and publish

    Use the builder or a code-defined file_upload field (<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.

  4. 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.

headless upload
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

SymptomCheck
The form cannot publishA file field exists but the form has no connected storage destination.
S3/R2 fails before transferReopen the connection and verify endpoint, region, bucket permissions, CORS, and multipart lifecycle access.
Completion says storage changedThe form switched providers during the session. Restart the upload against the current destination.
A historical file will not openConfirm the viewer is a workspace member and the original provider connection can still read that object.
A resumed upload is rejectedUse the original file, field, session id, and upload token; expired or completed sessions must restart.

Next steps

Was this page useful?

Was this page helpful?
Powered by Fillo