Architecture Note: Building a Reliable Trello Webhook Receiver
A practical architecture note for implementing a trustworthy Trello webhook endpoint covering signature verification idempotency operational monitoring and failure-mode resilience.
10 Jul 2025, 02:00 UTC

Requirements
When Trello must notify an external service about board activity such as a new card a comment or a label change you rely on Trello's webhook feature. The receiving service must accept HTTPS POST requests verify request authenticity using the HMAC-SHA256 signature in the Trello-signature header parse the JSON payload to extract action.type and action.id process the event idempotently and respond with a 2xx status within Trello's 3-second timeout. It must also protect internal systems from malformed or malicious input.
Minimal Viable Design
The smallest implementation that satisfies the requirements consists of three logical components arranged in a pipeline:
- Network listener – an HTTPS endpoint (e.g., a route in a web framework) that terminates TLS and forwards the raw request body to the verifier.
- Signature verifier – computes HMAC-SHA256 of the request body using the shared secret obtained when registering the webhook and compares the result to the value in the Trello-signature header. A mismatch results in an immediate 401 rejection; no retry is attempted by Trello.
- Event handler – parses the verified JSON ensures action.id is present checks an idempotency store (such as a Redis set or a database table with a unique index on action.id) to determine if the event was already processed and if not performs the desired business logic before acknowledging with 200 OK.
Below is a concise example in Python (Flask) that illustrates the flow. Replace YOUR_SECRET with the shared secret you obtained when registering the webhook.
import os, hashlib, hmac as hmac_mod, json
from flask import Flask, request, abort
app = Flask(__name__)
SHARED_SECRET = os.getenv('TRELLO_WEBHOOK_SECRET', 'YOUR_SECRET')
processed_ids = set()
@app.route('/trello/webhook', methods=['POST'])
def trello_webhook():
signature = request.headers.get('Trello-signature')
if not signature:
abort(401, 'Missing Trello-signature header')
mac = hmac_mod.new(SHARED_SECRET.encode(), request.data, hashlib.sha256)
expected = 'sha256=' + hmac_mod.hexdigest()
if not hmac_mod.compare_digest(expected, signature):
abort(401, 'Invalid signature')
try:
payload = request.get_json(force=True)
action = payload.get('action', {})
action_type = action.get('type')
action_id = action.get('id')
if not action_id:
abort(400, 'Missing action.id')
except (AttributeError, TypeError, json.JSONDecodeError):
abort(400, 'Invalid JSON payload')
if action_id in processed_ids:
return '', 200
processed_ids.add(action_id)
if action_type == 'createCard':
card_name = action.get('data', {}).get('card', {}).get('name')
app.logger.info(f'New card created: {card_name}')
return '', 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)Trust and Data Boundaries
The trust boundary sits precisely at the webhook endpoint. Any request that fails HMAC verification is considered untrusted and must be discarded before any further parsing. The raw request body is treated as untrusted input; only after verification do we deserialize JSON. Fields such as action.data may contain free-form user text (card descriptions, comments). Before storing or displaying this data, apply appropriate sanitisation HTML-escaping for UI output parameterised queries for database writes to avoid injection attacks. Do not persist the raw webhook body long-term without a documented sanitisation pipeline as user-generated content could later be rendered in an admin UI and introduce security risks.
Operational Checks
To keep the webhook healthy in production monitor the following metrics less than 500 ms aim well under Trello's 3-second limit. less than 1 percent over a 5-minute window. Trello retries the POST up to three times on network errors or 5xx responses. Implement exponential back-off in any downstream API calls made from the handler and ensure the handler itself is idempotent so duplicate retries cause no side effects. Idempotency store health if using Redis monitor memory usage and eviction policies if using a database table index the action_id column and purge old entries based on your retention policy. Operational verification can be performed with a temporary HTTPS tunnel. Start ngrok or similar to expose your local development port register a test webhook via the Trello API and trigger actions on the board. The incoming request will include a Trello-signature header verify that your service returns 200 OK and that the JSON body contains the expected action.type and a unique action.id. Introduce a deliberate 500 response you should see Trello retry the same POST up to three times before marking the webhook as failed. Restoring a 200 response should resume normal delivery.
Failure Modes and Conditions That Redesign the Architecture
Understanding how Trello reacts to problems helps you design resilient handling. The following failure modes have been observed: Network timeout or connection reset Trello retries the POST up to three times spaced a few seconds apart. If all attempts fail the webhook is marked failed and an email notification is sent if configured. Signature mismatch Trello discards the request immediately no retry is attempted. This typically occurs if the shared secret has rotated or the signature computation uses the wrong encoding. Payload greater than 1 MB Trello responds with HTTP 413 the webhook is considered failed. Rate limit 429 Trello includes a Retry-After header Honor this interval and pause further calls to the Trello API not the webhook receiver until the interval elapses. TLS version mismatch If your endpoint only supports TLS 1.0 and Trello enforces TLS 1.2+ the handshake fails and Trello treats it as a network error triggering retries. The current minimal design assumes the webhook contract remains stable. Revisit the architecture if any of the following occur: Trello adds new top-level fields to the payload that your logic depends on (e.g., a new action.display object). Your code should treat missing fields as validation errors unless the API documentation explicitly marks them optional. The HMAC algorithm is replaced e.g., a shift to HMAC-SHA512 or a JWT-based signature. You would need to update the verifier component accordingly. Trello enforces stricter TLS requirements e.g., deprecating TLS 1.1 or mandating specific cipher suites. Ensure your TLS termination proxy complies. Webhook delivery guarantees change e.g., Trello moves from at-most-once to at-least-once with a different retry schedule. Adjust idempotency handling and back-off strategies.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.