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:
- Built-in retry logic with configurable timeouts
- Full observability in Temporal UI
- Durability guarantees
- Consistent error handling
- Activity history and replay capability
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 cancelled
Webhooks are sent after the step status is committed to the database.
In both cases:
- ✅ Webhook failures do not prevent step completion
- ✅ Step is always marked as complete in the database first
- ✅ Webhook delivery is logged for debugging
- ✅ Same authentication and payload format
Set the following environment variables to enable webhook notifications:
STEP_COMPLETE_WEBHOOK: The URL endpoint that will receive POST requests when steps complete.- Example:
https://your-app.com/api/webhooks/step-complete
- Example:
STEP_COMPLETE_KEY: A secret key used for authentication.- When set, the webhook service will include this in an
Authorizationheader asBearer {key} - Example:
your-secret-webhook-key-12345
- When set, the webhook service will include this in an
When 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.serverWebhooks are triggered when a step reaches a terminal state:
- Success - Step completed successfully (
StepStatus.SUCCESS) - Error - Step failed with an error (
StepStatus.FAILED) - Cancelled - Step was cancelled (
StepStatus.CANCELLED)
All three states trigger the webhook with the same payload containing just the step_id.
- No webhook URL configured: The service will skip sending notifications (logged at debug level)
- Webhook call succeeds: Returns status 200-299, logged at info level
- Webhook timeout: Returns error, logged at warning level (does not fail the step)
- HTTP error: Returns non-2xx status, logged at warning level (does not fail the step)
- Other errors: Logged at error level (does not fail the step)
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:
- Set up a webhook endpoint (you can use webhook.site for testing)
- Configure the environment variables
- Run an agent and observe webhook calls when steps complete
# 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 -vThe webhook notification is sent after:
- The step is persisted to the database
- Step metrics are recorded
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:
- Start-to-close timeout: 15 seconds
- Schedule-to-close timeout: 30 seconds
- Retry behavior: Wrapped in try-catch to prevent workflow failure on webhook errors
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 delivery
Temporal 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 constants
Non-Temporal Integration:
apps/core/letta/services/step_manager.py- Direct calls in update_step_* methods
Tests:
apps/core/letta/services/webhook_service_test.py- Unit tests