Skip to content
colis

Your storage · A code · Your client’s answer

Deliver. Get it signed off.

colis sends your deliverable to a bucket you own under an eight-character code. Your client opens it, previews it, then approves it or asks for changes. You know when it was opened and what they answered, and every step can start an automation.

● Shipping labelyour bucket7 days
From
You, freelance
To
Your client
Contents
maquette-v2.pdf · 1.8 MB
Parcel no. K7QP2M4X
Tracking
  1. ✓ sent
  2. ✓ opened
  3. ✓ approved
webhook → parcel.approved
  • colis-site.vercel.app
  • Your bucket
  • No account for your client
  • Preview
  • Approve or ask for changes
  • Signed webhooks
  • n8n
  • Resumable large files
  • AWS S3
  • Cloudflare R2
  • MinIO
  • Scaleway
  • Wasabi
  • A CLI in French
  • MIT

01Send. Open. Sign off.

A delivery in three steps.

The file goes from your machine to your bucket, and from your bucket to your client. Nobody else stores it, and nobody signs up for anything.

  1. send

    Send it to your bucket.

    Drop the file on your delivery page, or send it from a terminal with colis envoyer. It lands in your bucket under a fresh code, with a lifetime, and if you want a password, a note, or burn after the first download.

  2. open

    Your client opens it.

    A link, a QR code, or eight characters typed on the page, typos repaired. They see the file before downloading anything: an image, a PDF, a video, audio, text.

  3. ok

    They approve, or ask for changes.

    One click to approve, or a comment on what should change. The answer is final and timestamped, and it shows up on your sending page, in colis statut and in a webhook.

02Three ways in

A page for your client, a terminal for you, a webhook for the rest.

One protocol under all three. The delivery page holds the keys to your bucket; the terminal and the automations talk to it.

The delivery page

templates/drop

A Next.js template you deploy on your bucket: the sending page, the pickup page with its preview and its two answers, and your tracking.

https://drop.example.com/K7QP2M4X

  maquette-v2.pdf · 1.8 MB · PDF
  « La version avec le logo corrigé »
  [ the PDF, previewed in the page ]

  Le fichier correspond à ce qui était attendu ?
  [ Valider la livraison ✓ ]  [ Demander des corrections → ]

From a terminal

@mdemb/colis

envoyer, recevoir, statut, annuler, verifier: the commands are in French, the code alone goes to stdout so it composes, and statut says where a delivery stands.

# colis.config.json points at your delivery page (colis init --provider remote)
$ colis envoyer ./maquette-v2.pdf
maquette-v2.pdf · 1.8 MB · expires in 1 day
K7QP2M4X

# later: where does it stand?
$ colis statut K7QP2M4X
validé
envoyé  2026-09-22 10:00
ouvert  2026-09-22 11:00
validé  2026-09-22 12:00
Every command

Automations

n8n-nodes-colis

A signed webhook at every step, from parcel.sent to parcel.approved, and an n8n node that starts a workflow on any of them.

// app/api/colis/route.ts, on your side
import { verifyWebhook } from '@colis/protocol'

export async function POST(request: Request) {
  const body = await request.text() // the raw body, before any JSON.parse
  const result = await verifyWebhook(process.env.DROP_WEBHOOK_SECRET!, request.headers, body)
  if (!result.valid) return new Response(null, { status: 401 })

  const event = JSON.parse(body)
  if (event.type === 'parcel.approved') await sendInvoice(event.data.code) // your code
  return new Response(null, { status: 204 })
}
Webhooks and n8n

03Your storage

Nobody in the middle.

colis is a thin layer over object storage you already pay for. The files stay in your bucket, and the page runs on your deployment.

Your storage

The file goes from you to your bucket, and from your bucket to your client. Nothing else is in the picture.

Your bucket

S3, R2, MinIO, Scaleway, Wasabi, or any S3-compatible storage. Your provider, your region, your bill.

No account for the client

A code or a link is the whole handshake. A password on the parcel when you want one.

Straight to the bucket

Above a few megabytes, the browser sends the file to the bucket itself, in parts, and resumes if the connection drops.

Expires on its own

Every parcel has a lifetime, checked on every read. A lifecycle rule deletes the object, and colis verifier checks you have one.

04The code

A code your client can type.

Eight characters printed on a shipping label. Read out on a call, typed on a phone, scanned off a QR code: it still finds the parcel.

What codes.normalize() looks up

Complete. Separators dropped, case folded, and O, I and L read as 0, 1 and 1, because Crockford base32 has no O, I or L to confuse them with.

Alphabet 0123456789ABCDEFGHJKMNPQRSTVWXYZ · 8 chars · 40 bits

Eight characters of Crockford base32: no I, L, O or U, so a code survives paper, a phone keyboard and a phone call. What the client types stays as typed; only the lookup is repaired.

The code is also the link: /K7QP2M4X on your delivery page is the pickup page, and the page shows a QR code of it for a phone.

A code is a bearer token. When the file is sensitive, give the parcel a short lifetime, a password, or burn it at the first download.

05Automations

Every step can start something.

The delivery page posts a signed event when a parcel is sent, opened, approved, sent back for changes, or burned. n8n, or any route that takes a POST, picks it up.

webhook · type

parcel.sent
A parcel was created, from the page or from the CLI.
parcel.opened
The client opened it. The first time only.
parcel.approved
The client approved it.
parcel.changes_requested
The client asked for changes; data.comment says which.
parcel.deleted
The code was burned.

Signed, the Standard Webhooks way

HMAC-SHA256 over the id, the timestamp and the body. verifyWebhook from @colis/protocol checks it with the same code that signs, and refuses anything more than five minutes old.

An n8n node

Colis Trigger starts a workflow on the events you pick and checks the signature for you. The Colis node sends, reads and burns parcels.

08Questions

The ones that come up.

01What is colis?

A delivery tool for freelancers. You send a file to storage you own (S3, R2, MinIO…) under an eight-character code; your client opens a pickup page, previews the file, then approves it or asks for changes with a comment. Every step can fire a signed webhook. There is a delivery page to deploy, a CLI with French commands, and an n8n node. How it works

02Does my client need an account?

No. They get a link, or type the code on the pickup page, typos included: lower case, dashes and an O for a 0 are repaired. If the parcel has a password, they type it once. That is all. On the sending side, an account is optional too: it ties your uploads to your address, signed in by e-mail link.

03Where are the files stored?

In your bucket, and nowhere else. The delivery page runs on your deployment, with your credentials; large files even go straight from the browser to the bucket. colis has no hosted service in the middle. Storage providers

04How do I know the client opened or approved it?

The delivery page keeps a receipt for every parcel: sent, opened (the first opening by someone other than you), then approved or changes, with the time and the comment. You see it on your sending page, with colis statut in a terminal, or in the parcel.opened, parcel.approved and parcel.changes_requested webhooks. Webhooks and n8n

05Can the client change their mind?

No: a parcel gets one answer, and it is never overwritten. For a new version, send a new parcel; it gets its own code and its own receipt.

06How big can a file be?

From the delivery page, up to DROP_MAX_UPLOAD_MB, 2 GB by default: above DROP_MAX_SIZE_MB (4 MB by default, because of Vercel’s request limit), the browser sends straight to the bucket, in 8 MiB parts, resumable. That needs a CORS rule on the bucket. From the CLI or the n8n node, the upload goes through the function in one request and has to fit under DROP_MAX_SIZE_MB. Deliver heavy files

07Is a code secure?

A code is a bearer token: whoever has it can open that parcel while it lives. For a sensitive deliverable, add a password (hashed with scrypt, never recoverable), a short lifetime, or burn after the first download. Wrong passwords are limited: ten from one client, or fifty on one code, lock it for fifteen minutes. A confidential delivery

08What happens when a parcel expires?

It is never handed over again: the expiry is checked on every read, and an expired code answers like one that never existed. The object itself is deleted by a lifecycle rule on your bucket, which colis verifier checks you have. The delivery receipt outlives the file.

09Can I install it with npm?

Yes for the command: npm i -g @mdemb/colis, which works with nothing configured thanks to the public colis service. The n8n-nodes-colis node installs from n8n. The @colis/* packages are not published yet: build those from the repository (bun install, then bun run build), and deploy the delivery page on Vercel by importing the whole repository. Install the CLI

10What does it cost?

colis is free software, under the MIT license: on your own deployment, the only bills are your storage provider’s and your host’s. The public service has two plans. Free: no account, files up to 100 MB, 24-hour links, 20 uploads an hour. Pro, €6 a month or €60 a year, with an account: up to 2 GB, 30-day links, 200 uploads an hour. No trial, cancel any time. The Pro plan (in French)

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.