Caddi
Sign inSign up

File uploads

Form attachments go straight from the browser to Cloudflare R2 via presigned URLs. Your Vercel function never proxies bytes.

The flow

caddi.flow
1. browser POSTs to /api/f/<id> with files-metadata
2. caddi returns presigned R2 URLs (one per file)
3. browser PUTs each file directly to R2
4. browser POSTs to /api/f/<id>/finalize
5. caddi marks the submission complete, fires webhooks

The SDK does all five steps for you. Pass files and that’s it.

SDK

ts
import { submit } from '@caddi/forms-sdk';

await submit({
  endpoint: process.env.NEXT_PUBLIC_CADDI_FORM_URL!,
  fields: { name, email },
  files: Array.from(input.files ?? []),
});

Limits

  • 10 files per submission.
  • 25 MB per file by default — raise per-project up to 200 MB on Pro.
  • Accepted MIME types defaulted to images and documents. Configure at Forms → settings → uploads.

Where the files live

R2 bucket scoped to your agency: caddi-uploads-<agency>. Caddi owns the bucket and rotates the access keys. You can mirror to your own bucket via webhook if you want long-term storage on your side.

Reading a file

The inbox shows file names and content types. Click to download — Caddi issues a short-lived read URL. Public sharing requires explicit “Make public” per file (writes an audit log entry).

Files inherit the submission’s retention. Default is indefinite. Set per-project at Forms → settings → retention if you want auto-purge after N days.

Next

@caddi/forms-sdk reference →