Skip to content
colis

Automations

Every step of a delivery, as an event.

Set two variables on your delivery page and it posts a signed event when a parcel is sent, opened, approved, sent back for changes or burned. Check it in one call, or let the n8n node do it for you.

parcel.approved
{
  "id": "5f0c6f7e-…",
  "type": "parcel.approved",
  "createdAt": "2026-09-22T12:00:00.000Z",
  "data": {
    "code": "K7QP2M4X",
    "url": "https://drop.example.com/K7QP2M4X",
    "filename": "maquette-v2.pdf",
    "size": 1843200,
    "contentType": "application/pdf",
    "expiresAt": "2026-09-29T10:00:00.000Z",
    "note": "La version avec le logo corrigé",
    "device": "Chrome sur macOS",
    "protected": false,
    "oneTime": false,
    "status": "approved",
    "sentAt": "2026-09-22T10:00:00.000Z",
    "openedAt": "2026-09-22T11:00:00.000Z",
    "decidedAt": "2026-09-22T12:00:00.000Z"
  }
}

01Events

Five events, one shape.

Each is a POST of JSON, sent after the response so nobody waits for it. `data` is the parcel as it stands when the event leaves.

type

parcel.sent
A parcel was created, from the page, a completed large upload, or the CLI.
parcel.opened
The client opened it: the pickup page, a download or an answer. 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: by you, or by the download of a one-time parcel.
on the delivery page
DROP_WEBHOOK_URL=https://n8n.example.com/webhook/…,https://your.app/api/colis
DROP_WEBHOOK_SECRET=…   # 32 characters or more: openssl rand -base64 32

data never holds the password, its hash, or the sender’s token. The note is gone from events once the parcel is; the name, size and type are kept with the receipt, so an approval that comes after a one-time download still names the file.

02Signature

Standard Webhooks, HMAC-SHA256.

Three headers on every request. Check them with the same function the deployment signs with.

what arrives
POST https://your.receiver/colis
content-type: application/json
webhook-id: 5f0c6f7e-…
webhook-timestamp: 1790078400
webhook-signature: v1,<base64 HMAC-SHA256 of "${id}.${timestamp}.${body}">
what you check
import { verifyWebhook } from '@colis/protocol'

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 }) // result.reason says why

Five minutes

A timestamp more than five minutes off is refused, so an old delivery cannot be replayed.

Three attempts

0.5 s then 2 s apart, five seconds each. A 4xx other than 408 and 429 is not retried, and a redirect is not followed. Deduplicate on webhook-id: a retry carries the same one.

Never in the way

A receiver that stays down misses the event, logged with its origin only. Nobody’s upload or download fails because of it.

03n8n

Two nodes.

`n8n-nodes-colis` starts a workflow at every step of a parcel’s life, and sends, reads or burns parcels from one.

n8n-nodes-colis

Colis Trigger
Starts the workflow on the events you pick. With the secret in its credentials, it answers 401 to anything unsigned, forged or too old, and 200 without starting anything to an event you did not pick.
Colis
Send a binary file with the page’s options, Get its metadata, Get Status of its delivery, Delete it. The code field takes a code as typed, or the pickup link.

Set it up

  1. 01Create Colis API credentials: the deployment’s base URL, its upload password for Send, and its webhook secret for the trigger.
  2. 02Add a Colis Trigger, pick the events, and copy its production URL.
  3. 03Paste that URL into the deployment’s DROP_WEBHOOK_URL, the same secret into DROP_WEBHOOK_SECRET, redeploy, and activate the workflow.
install it from n8n
# n8n: Settings → Community nodes → Install → n8n-nodes-colis
# self-hosted, without that screen:
cd ~/.n8n/nodes && npm install n8n-nodes-colis
# then restart n8n

Send uploads in one request, so the file has to fit the deployment’s DROP_MAX_SIZE_MB. Large files go from the page.

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.