Template
The page your client opens.
Drop a file, get a code, a link and a QR code. Your client opens it, sees the file, and approves it or asks for changes, while your page shows the answer as it comes. A Next.js template on your own bucket, deployed on Vercel from the repository.
COLIS_BUCKET=livraisons
COLIS_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
COLIS_REGION=auto
AWS_ACCESS_KEY_ID=…
AWS_SECRET_ACCESS_KEY=…
# optional
DROP_PASSWORD=… # asked before an upload, never for a pickup
DROP_EXPIRES_IN=86400 # the default lifetime, in seconds
DROP_MAX_EXPIRES_IN=604800 # the longest a sender may choose
DROP_MAX_SIZE_MB=4 # through the function, under Vercel's limit
DROP_MAX_UPLOAD_MB=2048 # above that, straight to the bucket, in parts
DROP_WEBHOOK_URL=… # where the signed events go
DROP_WEBHOOK_SECRET=… # and what signs them01What you get
Two pages, and the routes between them.
The sending page, the pickup page, and the transfer routes with the receipt on top. Everything is yours to restyle.
Staged before it goes
Picking a file shows what it is and a form: the lifetime, a password for this parcel, burn after the first download, the name it downloads under, the sending device, and a note of up to 280 characters. Nothing leaves the machine until you press send.
A shipping label
The result prints the code on a label with a Code 39 barcode, the link to share, and a QR code of that link for a phone.
The pickup page
/K7QP2M4X shows the file before anything is saved: an image, a video, audio, a PDF, the first lines of a text. Then what is known about it: the size, the type, the time left, the sender’s note.
Approve or ask for changes
Under the preview, one question: does the file match what was expected? Approve the delivery, or ask for changes and say which, in up to 1,000 characters. One answer, final.
Your tracking
Your sending page follows the receipt on its own: sent, opened, then the verdict, stamped « Validé » or « À corriger », with the client’s comment.
Large files, resumable
Above DROP_MAX_SIZE_MB, the browser uploads straight to your bucket in 8 MiB parts, up to DROP_MAX_UPLOAD_MB, and picks an interrupted upload up where it stopped.
02Deploy
Five variables, one lifecycle rule.
Until the @colis packages are on npm, the page builds from the monorepo: import the whole repository on Vercel, not the template folder alone.
- 01Create a bucket on R2, S3, Scaleway, Wasabi or a MinIO you host, and a key pair with read and write on that bucket and nothing else.
- 02On Vercel, Add New → Project and pick the repository. Set the Root Directory to
templates/dropand leave “Include files outside the root directory” on: itsvercel.jsoninstalls with Bun at the root and builds the packages first. - 03Paste the five variables, and
DROP_PASSWORDso strangers cannot fill your bucket. - 04Add a lifecycle rule that deletes objects under the prefix (
drop/by default) after a day or two, and a CORS rule for large files. - 05Run
colis verifier --remote https://your-deployment.vercel.app/api/transfers --token <password>and watch it round-trip a real parcel.
git clone https://github.com/mamadouwhile/colis.git && cd colis
bun install && bun run build
cp templates/drop/.env.example templates/drop/.env.local # point it at a bucket
bun run --filter colis-drop dev # http://localhost:340003Worth knowing
What it does not do.
No accounts, no history
A code is a bearer token with an expiry. There is no list of past parcels; each one’s receipt lives as long as the deployment’s longest lifetime.
One file per code
Zip a folder first. From a terminal it is one line: tar cz ./dir | colis envoyer - --name dir.tar.gz.
A preview costs a download
The bytes come through the function either way, so DROP_PREVIEW_MAX_MB caps it and DROP_PREVIEW=false turns it off. Uploaded files are never served as a page this origin would run.
Send it. They sign off. You know.
Your storage, a delivery page under your name, a webhook at every step. Nothing hosted by someone else.