MCPcopy Create free account
hub / github.com/Mnexa-AI/e2a / webhook

Function webhook

examples/adk-cloud-webhook/webhook.py:92–165  ·  view source on GitHub ↗
(request: Request)

Source from the content-addressed store, hash-verified

90
91@app.post("/webhook")
92async def webhook(request: Request) -> dict[str, str]:
93 body = await request.body()
94 # construct_event does parse + HMAC-verify in one call and raises
95 # E2AWebhookSignatureError on a bad signature or replay. Anyone can reach a
96 # public webhook URL; this verification is what proves the payload came from
97 # your e2a relay (the signature binds to the sender claim e2a verified via
98 # SPF/DKIM), so trust event.data only *after* it succeeds.
99 try:
100 event = construct_event(body, request.headers.get("X-E2A-Signature", ""), WEBHOOK_SECRET)
101 except E2AWebhookSignatureError:
102 raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="bad signature")
103
104 # We only act on inbound mail; ignore other event types (sent/approved/…).
105 if event.type != "email.received":
106 return {"status": "ignored", "type": event.type}
107
108 client: E2AClient = request.app.state.e2a
109 data = event.data # WebhookPayload: message_id, recipient, from, conversation_id, …
110 recipient = data["recipient"]
111 message_id = data["message_id"]
112
113 # The inbound webhook payload is metadata; fetch the parsed message for the
114 # subject + body to feed the agent.
115 inbound = await client.messages.get(recipient, message_id)
116
117 # First contact has no conversation_id — mint one so this thread has an
118 # ID we can echo back on every reply. The same id becomes the ADK
119 # session_id, keying multi-turn memory.
120 conversation_id = data.get("conversation_id") or f"conv_{uuid.uuid4().hex[:12]}"
121
122 # user_id scopes a session to a particular human counterpart. Different
123 # senders get isolated session histories even on the same agent.
124 user_id = data["from"]
125
126 sessions = request.app.state.sessions
127 session = await sessions.get_session(
128 app_name=APP_NAME, user_id=user_id, session_id=conversation_id
129 )
130 if session is None:
131 session = await sessions.create_session(
132 app_name=APP_NAME, user_id=user_id, session_id=conversation_id
133 )
134
135 prompt = types.Content(
136 role="user",
137 parts=[types.Part(text=_format_email_for_agent(inbound))],
138 )
139
140 runner: Runner = request.app.state.runner
141 reply_text = ""
142 async for event in runner.run_async(
143 user_id=user_id, session_id=conversation_id, new_message=prompt
144 ):
145 if event.is_final_response() and event.content and event.content.parts:
146 reply_text = event.content.parts[0].text or ""
147
148 if not reply_text:
149 # Agent produced no response (rare — usually means a tool call without

Callers

nothing calls this directly

Calls 5

construct_eventFunction · 0.90
_format_email_for_agentFunction · 0.85
getMethod · 0.45
infoMethod · 0.45
replyMethod · 0.45

Tested by

no test coverage detected