How CrossUpload works
A visitor on a computer needs a file that lives on their phone. Here is exactly what happens between the click and the file appearing in your form.
For your visitor
They click your upload button
The same button as before. CrossUpload opens a modal with two choices: Choose from this device (file picker and drag-and-drop) and Upload from your phone (a QR code). Allowed file types and the size limit are shown up front.
They scan the QR code
The phone camera opens a lightweight page that shows your website’s name and domain. No app, no login. The desktop modal shows “Phone connected”.
They pick a file and tap Send
From the gallery, the Files app, or the camera where the browser supports it. The phone shows real transfer progress, then “Checking file…”.
The file appears on the computer
Once verified, the file shows as “File added” in the same list as local uploads. The phone says “Done. Continue on your computer.” Your form is never submitted automatically.
Under the hood
- Your backend creates a session. When the picker opens, the widget calls your session endpoint. Your server authenticates its own user and calls
POST /v1/sessionswith its secret API key, server-owned constraints and an opaqueexternalReference. - CrossUpload returns short-lived credentials. A desktop capability (read, upload, manage) and a QR pairing link containing a single-use secret in the URL fragment. Sessions last ten minutes by default.
- Upload slots are reserved transactionally. When a file is chosen, CrossUpload validates it against the session policy and reserves upload count and storage in one PostgreSQL transaction.
- Bytes go directly to private storage. The browser uploads to a private quarantine bucket with a signed POST policy bound to one object key, the declared content type, a size range and at most five minutes.
- Verification is independent. The browser’s “done” is only a hint. A worker checks the stored object, reads its real type, scans it with ClamAV and copies that exact version to a separate, immutable delivery key.
- State is durable. The file becomes ready, the reservation becomes one accepted upload, an event is stored and webhooks are queued — all in one transaction.
- The desktop updates. An authenticated event stream wakes the modal; if it drops, polling recovers the state from the durable event cursor.
- Your server validates and stores IDs. On form submit, your backend checks each file ID with
GET /v1/files/{id}and requests fresh signed URLs whenever it needs the file.