MCPcopy
hub / github.com/dataelement/Clawith / receive_webhook

Function receive_webhook

backend/app/api/webhooks.py:46–165  ·  view source on GitHub ↗

Receive a webhook POST from an external service. Public endpoint — no authentication required. Security is provided by: - Unique, unguessable URL token - Optional HMAC signature verification - Rate limiting (5 requests/minute per token) - Payload size limit (64KB)

(token: str, request: Request)

Source from the content-addressed store, hash-verified

44
45@router.post("/t/{token}")
46async def receive_webhook(token: str, request: Request):
47 """Receive a webhook POST from an external service.
48
49 Public endpoint — no authentication required.
50 Security is provided by:
51 - Unique, unguessable URL token
52 - Optional HMAC signature verification
53 - Rate limiting (5 requests/minute per token)
54 - Payload size limit (64KB)
55 """
56 # Rate limiting — use per-agent limit if available
57 hit_count = await _record_and_count_hits(token)
58
59 # We'll check per-agent rate limit after finding the trigger below.
60 # For now, apply a generous global ceiling to prevent memory abuse.
61 if hit_count >= 60: # hard ceiling: 60/min regardless of config
62 logger.warning(f"Webhook hard rate limit exceeded for token {token[:8]}...")
63 return JSONResponse({"ok": True}, status_code=429)
64
65 # Payload size check
66 body = await request.body()
67 if len(body) > MAX_PAYLOAD_SIZE:
68 logger.warning(f"Webhook payload too large for token {token[:8]}...: {len(body)} bytes")
69 return JSONResponse({"ok": True}, status_code=413)
70
71 # Look up trigger
72 async with async_session() as db:
73 result = await db.execute(
74 select(AgentTrigger).where(
75 AgentTrigger.type == "webhook",
76 AgentTrigger.is_enabled,
77 )
78 )
79 triggers = result.scalars().all()
80
81 # Find the trigger matching this token
82 target = None
83 for trigger in triggers:
84 cfg = trigger.config or {}
85 if cfg.get("token") == token:
86 target = trigger
87 break
88
89 if not target:
90 # Return 200 OK to avoid leaking whether the token exists
91 return JSONResponse({"ok": True})
92
93 # Per-agent rate limit check
94 agent_result = await db.execute(select(Agent).where(Agent.id == target.agent_id))
95 agent_obj = agent_result.scalar_one_or_none()
96 agent_rate_limit = (agent_obj.webhook_rate_limit if agent_obj else None) or RATE_LIMIT
97
98 # Retrieve all needed scalar fields and expunge from db session to prevent MissingGreenlet errors.
99 target_name = target.name
100 target_agent_id = target.agent_id
101 target_config = target.config or {}
102 db.expunge(target)
103 if agent_obj:

Callers

nothing calls this directly

Calls 12

AuditLogClass · 0.90
_record_and_count_hitsFunction · 0.85
whereMethod · 0.80
expungeMethod · 0.80
executeMethod · 0.45
allMethod · 0.45
scalarsMethod · 0.45
getMethod · 0.45
scalar_one_or_noneMethod · 0.45
addMethod · 0.45
commitMethod · 0.45

Tested by

no test coverage detected