# Guide to Trello Webhooks: Features and Best Practices

Trello webhooks notify your application when a board or card changes: a card is created, a card is updated, a comment is added. If you're syncing Trello with another system or automating off board activity, webhooks are how you react without polling the API.

This guide covers how Trello webhooks work, the actions you can watch, how to verify the `x-trello-webhook` signature (which is SHA1 over the body concatenated with your callback URL), the creation HEAD check, the retry behavior, and the best practices for production.

## What are Trello webhooks?

A Trello webhook watches a model (a board, list, card, or member) and POSTs to your callback URL when an action occurs on that model. The payload contains an `action` (with `action.type`), the `model` being watched, and the `webhook` object. You create webhooks through the REST API, and Trello signs each delivery so you can confirm it came from Trello.

The signature construction is unusual: it covers the request body plus the callback URL, and it uses SHA1.

## Trello webhook features

| Feature | Details |
| --- | --- |
| Configuration | REST API: `POST /1/tokens/{token}/webhooks` with `idModel` + `callbackURL` |
| Signature header | `x-trello-webhook` |
| Signature scheme | base64 HMAC-SHA1 over `raw body + callbackURL`, keyed with the OAuth application secret |
| Creation check | HTTP HEAD to the callback URL; must return 200 (invalid SSL also fails) |
| Payload | `action` (with `action.type`), `model`, `webhook` |
| Retries | 3 attempts with backoff (30s, 60s, 120s) |
| Auto-disable | After ~30 days of consecutive failures (>1,000 failures) |
| SDK | No official server-side npm/pip SDK |

## Common actions

Trello events are identified by `action.type`. Card actions are the common ones:

| Action type | Fires when |
| --- | --- |
| `createCard` | A card is created |
| `updateCard` | A card is updated (moved, renamed, due date changed) |
| `commentCard` | A comment is added to a card |
| `deleteCard` | A card is deleted |
| `addMemberToCard` | A member is added to a card |
| `addChecklistToCard` | A checklist is added to a card |

Branch on `action.type`, and consult Trello's action-types reference for the full list.

## Setting up Trello webhooks

Create a webhook via the API, pointing it at a model (`idModel`) and your `callbackURL`:

```bash
curl -X POST "https://api.trello.com/1/tokens/{token}/webhooks/?key={apiKey}" \
  -H "Content-Type: application/json" \
  -d '{ "idModel": "{boardOrCardId}", "callbackURL": "https://your-app.example.com/webhook" }'

```

At creation, Trello sends an HTTP HEAD request to your `callbackURL` and won't create the webhook unless it returns 200. An invalid SSL certificate also fails creation (a missing certificate does not). Make sure your endpoint answers HEAD with a 200.

## Securing Trello webhooks

Each delivery carries an `x-trello-webhook` header: a base64-encoded HMAC-SHA1 where the signed content is the raw request body concatenated with the exact `callbackURL` you registered, keyed with your application's OAuth secret. Two things to get right: it's SHA1 (not SHA256), and the callback URL is appended to the body before hashing.

```javascript
const crypto = require("crypto");

const SECRET = process.env.TRELLO_OAUTH_SECRET;
const CALLBACK_URL = process.env.TRELLO_CALLBACK_URL; // exactly as registered

function verify(rawBody, signature) {
  const content = Buffer.concat([rawBody, Buffer.from(CALLBACK_URL)]);
  const expected = crypto.createHmac("sha1", SECRET).update(content).digest("base64");
  const a = Buffer.from(expected);
  const b = Buffer.from(signature || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.head("/webhook", (req, res) => res.sendStatus(200)); // creation HEAD check

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(req.body, req.headers["x-trello-webhook"])) {
    return res.sendStatus(401);
  }

  res.sendStatus(200); // acknowledge fast
  processQueue.add(JSON.parse(req.body)); // process asynchronously
});

```

The same check in Python:

```python
import base64
import hashlib
import hmac
import os

SECRET = os.environ["TRELLO_OAUTH_SECRET"].encode()
CALLBACK_URL = os.environ["TRELLO_CALLBACK_URL"].encode()

def verify(raw_body: bytes, signature: str) -> bool:
    content = raw_body + CALLBACK_URL
    digest = hmac.new(SECRET, content, hashlib.sha1).digest()
    expected = base64.b64encode(digest).decode()
    return hmac.compare_digest(expected, signature or "")

```

Trello's own example hashes `JSON.stringify(request.body) + callbackURL`; verifying against the raw body plus the callback URL avoids re-serialization mismatches.

## Trello webhook limitations and pain points

### The signature covers body plus callback URL, with SHA1

The Problem: Two easy mistakes: using SHA256 instead of SHA1, and hashing only the body instead of body + callbackURL. Either one makes every delivery fail verification.

Why It Happens: Trello's scheme predates SHA256 conventions and binds the signature to the registered callback URL.

Workarounds:

* Use SHA1, base64, and concatenate the exact registered `callbackURL` after the raw body.
* Keep the `callbackURL` value byte-identical to what you registered.

How Hookdeck Can Help: Hookdeck verifies the Trello signature at the edge, including the body-plus-callback-URL construction, so your app receives pre-verified events without reproducing the scheme.

### The creation HEAD check

The Problem: If your endpoint doesn't answer a HEAD request with 200, or has an invalid SSL cert, webhook creation fails outright, which is confusing if you only implemented POST.

Why It Happens: Trello verifies the endpoint is reachable and valid before creating the webhook.

Workarounds:

* Implement a HEAD handler that returns 200.
* Ensure a valid TLS certificate on the callback URL.

How Hookdeck Can Help: Hookdeck provides a stable, valid HTTPS URL that answers Trello's HEAD check, then forwards verified events to your endpoint.

### Limited retries then auto-disable

The Problem: Trello retries a failed delivery only 3 times (30s, 60s, 120s), and after roughly 30 days of consecutive failures (over 1,000) the webhook is auto-disabled. A short outage can drop events; a sustained one loses the webhook.

Why It Happens: Trello's retry budget is small, and it prunes long-dead webhooks.

Workarounds:

* Acknowledge fast so transient issues don't consume the 3-attempt budget.
* Monitor webhook status and recreate a disabled webhook.
* Reconcile against the API for anything you can't afford to miss.

How Hookdeck Can Help: Hookdeck accepts every delivery and retries to your downstream on a schedule you control, so Trello's 3-attempt limit isn't your only safety net and a sustained outage doesn't disable the webhook.

### Duplicates and thin actions

The Problem: The same action can be delivered more than once, and the payload is an action summary rather than full state.

Why It Happens: At-least-once delivery plus compact action payloads.

Workarounds:

* Dedupe on `action.id`.
* Fetch full state from the API when the action summary isn't enough.

How Hookdeck Can Help: Hookdeck deduplicates deliveries at the edge, so retries don't double-process. See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

## Best practices

### Verify SHA1 over body + callbackURL

Use `sha1`, base64, concatenate the raw body with the exact registered `callbackURL`, key with your OAuth secret, and compare in constant time.

### Answer the HEAD check

Return 200 to HEAD requests and serve a valid TLS certificate so webhook creation succeeds.

### Acknowledge fast, process asynchronously

Return 200 quickly and defer work to a queue so transient slowness doesn't burn the 3-attempt retry budget. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

### Dedupe on `action.id`

Use `action.id` as an idempotency key so retries don't double-process.

### Monitor for auto-disable

Watch webhook status and recreate a webhook that's been disabled after sustained failures.

## Conclusion

Trello webhooks put board and card activity on a push channel your automation can act on, but the details are specific: a HEAD check at creation, and an `x-trello-webhook` signature that's base64 HMAC-SHA1 over the raw body concatenated with your callback URL. The delivery model is thin too: only 3 retries, auto-disable after sustained failures, and compact action payloads that often need a follow-up read.

That leaves the HEAD handler, correct SHA1 verification, deduplication, and monitoring on you. [Hookdeck](https://hookdeck.com) answers the HEAD check, verifies the signature, deduplicates, and durably queues every action at the edge, so your app only ever processes verified, unique events.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Trello webhooks reliably in minutes.