# Webhooks and n8n

> Every step of a delivery as a signed webhook: parcel.sent, opened, approved, changes_requested, deleted. Standard Webhooks signatures, verifyWebhook in @colis/protocol, and an n8n node that starts a workflow on any of them.

Canonical: https://colis-site.vercel.app/en/webhooks · Markdown: https://colis-site.vercel.app/en/webhooks.md · Français: https://colis-site.vercel.app/webhooks

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.

- Webhooks in the README: https://github.com/mamadouwhile/colis/blob/main/templates/drop/README.md#webhooks
- The n8n node: https://github.com/mamadouwhile/colis/blob/main/packages/n8n-nodes-colis/README.md

## 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.

- `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.

**parcel.approved**
```json
{
  "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"
  }
}
```
**on the delivery page**
```sh
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.

## Standard Webhooks, HMAC-SHA256.
Three headers on every request. Check them with the same function the deployment signs with.

**what arrives**
```text
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**
```ts
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.

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

- `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. Create Colis API credentials: the deployment’s base URL, its upload password for Send, and its webhook secret for the trigger.
2. Add a Colis Trigger, pick the events, and copy its production URL.
3. Paste 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**
```sh
# 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.

## What to plug in.
- [Invoice on approval: An n8n trigger on parcel.approved: you are told, and the invoice goes out with the parcel’s code as its reference.](https://colis-site.vercel.app/en/use-cases/facturer-a-la-validation)
- [Change requests in your own tools: Every request for changes arrives signed on your route, with the client’s comment, ready to become a ticket.](https://colis-site.vercel.app/en/use-cases/corrections-dans-vos-outils)
- [An AI summary of every deliverable: When a document is sent, n8n downloads it, extracts the text and writes a five-line summary for the project log.](https://colis-site.vercel.app/en/use-cases/resume-ia-des-livrables)
