Agent skill
Missive Webhooks Skill
Receive and verify Missive (team inbox) rule webhooks. Use when setting up a Missive webhook handler, debugging X-Hook-Signature verification (sha256= HMAC-SHA256 hex of the raw body), or handling rule payloads such as label_change with their conversation and message objects.
Install this skill
npx skills add hookdeck/webhook-skills --skill missive-webhooks
Missive is a shared team inbox. A Webhook action on a Missive rule POSTs a JSON payload (the rule, the conversation and the latest message or comment) to your endpoint whenever the rule triggers, for example on incoming email or a label change.
When to Use This Skill
- How do I receive Missive webhooks from a rule?
- How do I verify the Missive
X-Hook-Signatureheader? - How do I handle a Missive
label_changerule webhook? - Why is my Missive webhook signature verification failing?
- How do I validate the request Missive sends when I save a rule?
Verification (core)
If you set a validation secret on the rule's webhook action, Missive sends X-Hook-Signature: sha256=<hex>: HMAC-SHA256 of the raw request body, keyed with that secret, hex encoded. Require and strip the sha256= prefix, check the rest is exactly 64 hex characters before decoding (Buffer.from(x, 'hex') silently truncates invalid hex), then compare in constant time:
const crypto = require('crypto');
function verifyMissiveSignature(rawBody, header, secret) {
if (typeof header !== 'string' || !header.startsWith('sha256=')) return false;
const sig = header.slice('sha256='.length);
if (!/^[0-9a-f]{64}$/i.test(sig)) return false;
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
return crypto.timingSafeEqual(expected, Buffer.from(sig, 'hex'));
}
Python (hmac.compare_digest on bytes, so a non-ASCII header gives 401, not 500):
import hashlib, hmac, re
def verify_missive_signature(raw_body: bytes, header: str | None, secret: str) -> bool:
if not header or not header.startswith("sha256="):
return False
sig = header[len("sha256="):]
if not re.fullmatch(r"[0-9a-fA-F]{64}", sig):
return False
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest().encode()
return hmac.compare_digest(expected, sig.lower().encode())
Read the raw body (express.raw({ type: '*/*' }) so any content type verifies, request.text(), await request.body()), not re-serialized JSON. Respond 401 on a bad signature.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Setup (including Hookdeck)
- In Missive go to Settings > Rules, create a rule and choose Webhook as the action (creating rules needs an admin or owner of an organization on the Productive or Business plan).
- Enter your endpoint URL and set a validation secret on the webhook action. Without one, deliveries are unsigned and fail verification, here and in a Hookdeck source.
- Save the rule. Missive sends a validation POST to make sure your endpoint works, so the endpoint must be reachable (and accept POST) before you save.
Using Hookdeck: create the source with the same validation secret as the rule's webhook action; Hookdeck verifies X-Hook-Signature before forwarding. Details in references/setup.md.
Event Types (rule types)
Missive has no event header. Each payload has a rule object (id, description, type) plus conversation and latest_message (or a comment object for comment rules). Dispatch on rule.type.
rule.type | Description |
|---|---|
label_change | A shared label was added or removed (the example in Missive's docs) |
Missive's docs show only label_change; other rule triggers (incoming email, outgoing email, new comment and so on) are chosen in the rule UI, so route unknown types to a default branch and log them.
Important Headers
| Header | Description |
|---|---|
X-Hook-Signature | sha256= + HMAC-SHA256 hex of the raw body (only sent when a validation secret is set) |
Delivery behaviour
- Endpoint must accept
POSTand respond within 15 seconds. - Errors or timeouts are retried up to 5 times over 8 minutes.
- A rule that fails more than 50 times in a row is disabled automatically; re-enable it in the Rules settings.
Environment Variables
MISSIVE_WEBHOOK_SECRET=your_validation_secret # The validation secret set on the rule's webhook action
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 missive --path /webhooks/missive
Reference Materials
- references/overview.md - What Missive webhooks are, rule types and payload
- references/setup.md - Create the rule webhook action, validation secret, Hookdeck
- references/verification.md - Signature verification details and gotchas