claude-managed-agents-webhooks
Receive and verify Anthropic Claude Managed Agents (CMA) webhooks. Use when setting up Claude Managed Agents webhook handlers, debugging signature verification, or handling agent session and vault events like session.status_idled, session.status_terminated, session.thread_created, vault.created, or vault_credential.refresh_failed.
What this skill does
# Claude Managed Agents Webhooks
## When to Use This Skill
- Setting up Claude Managed Agents (CMA) webhook handlers
- Debugging Anthropic webhook signature verification failures
- Handling agent session state changes (`session.status_idled`, `session.status_terminated`)
- Reacting to multiagent thread events (`session.thread_created`, `session.thread_idled`)
- Processing vault and credential events (`vault.created`, `vault_credential.refresh_failed`)
- Replacing long-poll loops on the Sessions API with push notifications
## Essential Code (USE THIS)
CMA webhooks follow the [Standard Webhooks](https://www.standardwebhooks.com/) spec. Every delivery carries three headers — `webhook-id`, `webhook-timestamp`, and `webhook-signature` — and is signed with HMAC-SHA256 over `{webhook-id}.{webhook-timestamp}.{raw-body}`. The signing secret is the `whsec_`-prefixed value shown once at endpoint creation. The Anthropic SDK exposes `client.beta.webhooks.unwrap()` which wraps the same verification. Manual verification is shown here because it works in every framework without an extra SDK dependency.
### Express Webhook Handler
```javascript
const express = require('express');
const crypto = require('crypto');
const app = express();
// Standard Webhooks signature verification for Claude Managed Agents
function verifyClaudeSignature(payload, webhookId, webhookTimestamp, webhookSignature, secret) {
if (!webhookId || !webhookTimestamp || !webhookSignature || !webhookSignature.includes(',')) {
return false;
}
// Reject payloads older than 5 minutes to prevent replay attacks
const currentTime = Math.floor(Date.now() / 1000);
const timestampDiff = currentTime - parseInt(webhookTimestamp);
if (timestampDiff > 300 || timestampDiff < -300) {
return false;
}
// webhook-signature can carry multiple space-separated "v1,<sig>" pairs
const payloadStr = payload instanceof Buffer ? payload.toString('utf8') : payload;
const signedContent = `${webhookId}.${webhookTimestamp}.${payloadStr}`;
// whsec_ prefix wraps a base64-encoded 32-byte key
const secretKey = secret.startsWith('whsec_') ? secret.slice(6) : secret;
const secretBytes = Buffer.from(secretKey, 'base64');
const expectedSignature = crypto
.createHmac('sha256', secretBytes)
.update(signedContent, 'utf8')
.digest('base64');
return webhookSignature.split(' ').some(pair => {
const [version, signature] = pair.split(',');
if (version !== 'v1' || !signature) return false;
try {
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
} catch {
return false;
}
});
}
// CRITICAL: Use express.raw() for webhook endpoint - signature is over raw bytes
app.post('/webhooks/claude-managed-agents',
express.raw({ type: 'application/json' }),
async (req, res) => {
const webhookId = req.headers['webhook-id'];
const webhookTimestamp = req.headers['webhook-timestamp'];
const webhookSignature = req.headers['webhook-signature'];
if (!verifyClaudeSignature(
req.body,
webhookId,
webhookTimestamp,
webhookSignature,
process.env.ANTHROPIC_WEBHOOK_SIGNING_KEY
)) {
return res.status(400).send('Invalid signature');
}
const event = JSON.parse(req.body.toString());
// CMA payloads carry the event type under data.type, not the top-level type
switch (event.data?.type) {
case 'session.status_idled':
console.log('Session idled:', event.data.id);
// Fetch the full session: client.beta.sessions.retrieve(event.data.id)
break;
case 'session.status_terminated':
console.log('Session terminated:', event.data.id);
break;
case 'session.thread_created':
console.log('Multiagent thread created:', event.data.id);
break;
case 'vault_credential.refresh_failed':
console.log('Vault credential refresh failed:', event.data.id);
break;
default:
console.log('Unhandled event:', event.data?.type);
}
res.status(200).json({ received: true });
}
);
```
### Python (FastAPI) Webhook Handler
```python
import os
import hmac
import hashlib
import base64
import time
from fastapi import FastAPI, Request, HTTPException, Header
app = FastAPI()
def verify_claude_signature(
payload: bytes,
webhook_id: str,
webhook_timestamp: str,
webhook_signature: str,
secret: str,
) -> bool:
if not webhook_id or not webhook_timestamp or not webhook_signature or ',' not in webhook_signature:
return False
# Reject payloads older than 5 minutes to prevent replay attacks
try:
timestamp_diff = int(time.time()) - int(webhook_timestamp)
except ValueError:
return False
if timestamp_diff > 300 or timestamp_diff < -300:
return False
signed_content = f"{webhook_id}.{webhook_timestamp}.{payload.decode('utf-8')}"
# whsec_ prefix wraps a base64-encoded 32-byte key
secret_key = secret[6:] if secret.startswith('whsec_') else secret
try:
secret_bytes = base64.b64decode(secret_key)
except Exception:
return False
expected_signature = base64.b64encode(
hmac.new(secret_bytes, signed_content.encode('utf-8'), hashlib.sha256).digest()
).decode('utf-8')
# webhook-signature can carry multiple space-separated "v1,<sig>" pairs
for pair in webhook_signature.split(' '):
parts = pair.split(',', 1)
if len(parts) != 2:
continue
version, signature = parts
if version == 'v1' and hmac.compare_digest(signature, expected_signature):
return True
return False
@app.post("/webhooks/claude-managed-agents")
async def claude_webhook(
request: Request,
webhook_id: str = Header(None, alias="webhook-id"),
webhook_timestamp: str = Header(None, alias="webhook-timestamp"),
webhook_signature: str = Header(None, alias="webhook-signature"),
):
payload = await request.body()
secret = os.environ.get("ANTHROPIC_WEBHOOK_SIGNING_KEY")
if not verify_claude_signature(payload, webhook_id, webhook_timestamp, webhook_signature, secret):
raise HTTPException(status_code=400, detail="Invalid signature")
event = await request.json()
# Handle event.data.type ...
return {"received": True}
```
### Anthropic SDK alternative
If you already use the Anthropic SDK, replace the manual verification with `client.beta.webhooks.unwrap()`. The SDK reads `ANTHROPIC_WEBHOOK_SIGNING_KEY` from the environment, verifies the signature, rejects payloads older than five minutes, and parses the event:
```typescript
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
// inside your handler, after reading the raw body:
const event = client.beta.webhooks.unwrap(rawBody, { headers });
```
```python
import anthropic
client = anthropic.Anthropic() # requires: pip install "anthropic[webhooks]"
# inside your handler, after reading the raw body:
event = client.beta.webhooks.unwrap(raw_body, headers=dict(request.headers))
```
> **For complete working examples with tests**, see:
> - [examples/express/](examples/express/) — Full Express implementation
> - [examples/nextjs/](examples/nextjs/) — Next.js App Router implementation
> - [examples/fastapi/](examples/fastapi/) — Python FastAPI implementation
## Common Event Types
CMA webhooks deliver only the event `type` and `id` — fetch the full object via the API (`client.beta.sessions.retrieve(event.data.id)`). The event type lives under `event.data.type`; the top-level `event.type` is always `"event"`.
### Session events
| Event | Description |
|-------|-------------|
| `session.status_run_started` | Agent execution started; fires on every transition to `running`. |
| `session.status_idled` | Agent is awaiting input (tool approval, new user message). |
| `session.status_rescheduled` | Transient error; the session is retrying automatically. |
| `session.statRelated in AI Agents
skill-development
IncludedComprehensive meta-skill for creating, managing, validating, auditing, and distributing Claude Code skills and slash commands (unified in v2.1.3+). Provides skill templates, creation workflows, validation patterns, audit checklists, naming conventions, YAML frontmatter guidance, progressive disclosure examples, and best practices lookup. Use when creating new skills, validating existing skills, auditing skill quality, understanding skill architecture, needing skill templates, learning about YAML frontmatter requirements, progressive disclosure patterns, tool restrictions (allowed-tools), skill composition, skill naming conventions, troubleshooting skill activation issues, creating custom slash commands, configuring command frontmatter, using command arguments ($ARGUMENTS, $1, $2), bash execution in commands, file references in commands, command namespacing, plugin commands, MCP slash commands, Skill tool configuration, or deciding between skills vs slash commands. Delegates to docs-management skill for official documentation.
reprompter
IncludedTransform messy prompts into well-structured, effective prompts — single or multi-agent. Use when: "reprompt", "reprompt this", "clean up this prompt", "structure my prompt", rough text needing XML tags and best practices, "reprompter teams", "repromptception", "run with quality", "smart run", "smart agents", multi-agent tasks, audits, parallel work, anything going to agent teams. Don't use when: simple Q&A, pure chat, immediate execution-only tasks. See "Don't Use When" section for details. Outputs: Structured XML/Markdown prompt, quality score (before/after), optional team brief + per-agent sub-prompts, agent team output files. Success criteria: Single mode quality score ≥ 7/10; Repromptception per-agent prompt quality score 8+/10; all required sections present, actionable and specific.
adaptive-compaction
IncludedAdaptive add-on policy and recovery layer that decides WHEN to compact, prune, snapshot, or fork -- replacing fixed-percent auto-compaction across Claude Code, Codex, and MCP-capable hosts. Trigger on auto-compact timing or damage: "when should I compact", "is it safe to compact now or start a fresh session", "auto-compact fires too early/mid-task", "switching to an unrelated task but the window still has space", "context rot", "answers get worse the longer the session runs", "the agent forgot the plan or my decisions after it summarized", "add a layer on top that manages context without changing the agent", raising autoCompactWindow to give the policy room, or installing/tuning a cross-tool compaction policy or PreCompact hook -- even when "compaction" is never said but the problem is context-window pressure or post-summarization memory loss. Do NOT use to summarize a conversation, build RAG, write a summarization prompt (decides WHEN not HOW), or answer max-context-length trivia.
agent-skill-creator
IncludedCreate cross-platform agent skills from workflow descriptions. Activates when users ask to create an agent, automate a repetitive workflow, create a custom skill, or need advanced agent creation. Triggers on phrases like create agent for, automate workflow, create skill for, every day I have to, daily I need to, turn process into agent, need to automate, create a cross-platform skill, validate this skill, export this skill, migrate this skill. Supports single skills, multi-agent suites, transcript processing, template-based creation, interactive configuration, cross-platform export, and spec validation.
llm-wiki
IncludedUse when building or maintaining a persistent personal knowledge base (second brain) in Obsidian where an LLM incrementally ingests sources, updates entity/concept pages, maintains cross-references, and keeps a synthesis current. Triggers include "second brain", "Obsidian wiki", "personal knowledge management", "ingest this paper/article/book", "build a research wiki", "compound knowledge", "Memex", or whenever the user wants knowledge to accumulate across sessions instead of being re-derived by RAG on every query.
skill-master
IncludedAgent Skills authoring, evaluation, and optimization. Create, edit, validate, benchmark, and improve skills following the agentskills.io specification. Use when designing SKILL.md files, structuring skill folders (references, scripts, assets), ingesting external documentation into skills, running trigger evals, benchmarking skill quality, optimizing descriptions, or performing blind A/B comparisons. Keywords: agentskills.io, SKILL.md, skill authoring, eval, benchmark, trigger optimization.