WEBHOOK_SETUP.md
This feature allows you to receive webhook notifications whenever an agent step completes in the Letta agent loop.
The webhook service integrates with Letta's execution architecture in two ways:
When using Temporal for agent workflows, webhook calls are wrapped as Temporal activities, providing:
Webhooks are triggered after the create_step activity completes in the Temporal workflow.
For direct agent execution (non-Temporal), webhooks are called directly from the StepManager service methods:
update_step_success_async() - When step completes successfullyupdate_step_error_async() - When step fails with an errorupdate_step_cancelled_async() - When step is cancelledWebhooks are sent after the step status is committed to the database.
In both cases:
Set the following environment variables to enable webhook notifications:
STEP_COMPLETE_WEBHOOK: The URL endpoint that will receive POST requests when steps complete.
https://your-app.com/api/webhooks/step-completeSTEP_COMPLETE_KEY: A secret key used for authentication.
Authorization header as Bearer {key}your-secret-webhook-key-12345When a step completes, the webhook service will send a POST request with the following JSON payload:
{
"step_id": "step-01234567-89ab-cdef-0123-456789abcdef"
}
If STEP_COMPLETE_KEY is configured, requests will include an Authorization header:
Authorization: Bearer your-secret-webhook-key-12345
Your webhook endpoint should validate this key to ensure requests are coming from your Letta instance.
Here's a simple example of a webhook endpoint (using FastAPI):
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
import os
app = FastAPI()
class StepCompletePayload(BaseModel):
step_id: str
WEBHOOK_SECRET = os.getenv("STEP_COMPLETE_KEY")
@app.post("/api/webhooks/step-complete")
async def handle_step_complete(
payload: StepCompletePayload,
authorization: str = Header(None)
):
# Validate the webhook key
if WEBHOOK_SECRET:
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Missing authorization")
token = authorization.replace("Bearer ", "")
if token != WEBHOOK_SECRET:
raise HTTPException(status_code=401, detail="Invalid authorization")
# Process the step completion
print(f"Step completed: {payload.step_id}")
# You can now:
# - Log the step completion
# - Trigger downstream processes
# - Update your application state
# - Send notifications
return {"status": "success"}
# Set environment variables
export STEP_COMPLETE_WEBHOOK="https://your-app.com/api/webhooks/step-complete"
export STEP_COMPLETE_KEY="your-secret-webhook-key-12345"
# Start your Letta server
python -m letta.server
Webhooks are triggered when a step reaches a terminal state:
StepStatus.SUCCESS)StepStatus.FAILED)StepStatus.CANCELLED)All three states trigger the webhook with the same payload containing just the step_id.
Important: Webhook failures do not prevent step completion. The step will be marked as complete in the database regardless of webhook delivery status. This ensures system reliability - your webhook endpoint being down will not block agent execution.
To test the webhook functionality:
# Example using webhook.site
export STEP_COMPLETE_WEBHOOK="https://webhook.site/your-unique-url"
export STEP_COMPLETE_KEY="test-key-123"
# Run tests
python -m pytest apps/core/letta/services/webhook_service_test.py -v
The webhook notification is sent after:
This ensures that the step data is fully committed before external systems are notified.
When using Temporal, the webhook call is executed as a separate activity (send_step_complete_webhook) with the following configuration:
This allows you to monitor webhook delivery in the Temporal UI and get detailed visibility into any failures.
Core Service:
apps/core/letta/services/webhook_service.py - HTTP client for webhook deliveryTemporal Integration:
apps/core/letta/agents/temporal/activities/send_webhook.py - Temporal activity wrapperapps/core/letta/agents/temporal/temporal_agent_workflow.py - Workflow integrationapps/core/letta/agents/temporal/constants.py - Timeout constantsNon-Temporal Integration:
apps/core/letta/services/step_manager.py - Direct calls in update_step_* methodsTests:
apps/core/letta/services/webhook_service_test.py - Unit tests