Complete documentation for the webhook endpoint that receives alerts and triggers Devin sessions.
- Endpoint Overview
- Authentication
- Request Format
- Response Format
- Error Handling
- Integration Examples
- Environment Variables
- Customization
POST /api/trigger-devin
| Property | Value |
|---|---|
| Method | POST |
| Content-Type | application/json |
| Authentication | None (API key stored server-side) |
| Environment | URL |
|---|---|
| Local Development | http://localhost:3000/api/trigger-devin |
| Production | https://your-webhook-endpoint/api/trigger-devin |
The endpoint doesn't require client authentication by default — the Devin API key is stored as a server-side environment variable.
To secure your endpoint, add a webhook secret:
Set WEBHOOK_SECRET in your deployment platform:
# Local .env.local
WEBHOOK_SECRET=your-secret-key-here
# Azure Functions: Configuration → Application settings// src/app/api/trigger-devin/route.ts
export async function POST(request: Request) {
// Verify webhook secret
const authHeader = request.headers.get('Authorization');
const expectedAuth = `Bearer ${process.env.WEBHOOK_SECRET}`;
if (process.env.WEBHOOK_SECRET && authHeader !== expectedAuth) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
);
}
// ... rest of handler
}Add the Authorization header to your webhook configuration:
Authorization: Bearer your-secret-key-here
{
"alertName": "mcp-token-expiration",
"severity": 1,
"description": "Azure AD access token has expired",
"affectedResource": "aks-mcp-server-prod",
"signalType": "log",
"firedTime": "2024-02-06T12:00:00Z",
"source": "azure-monitor",
"logs": "Error: TokenCredentialAuthenticationError...\n at auth.ts:14",
"file": "src/tools/auth.ts",
"line": 14,
"dimensions": {
"cluster": "aks-mcp-server-prod",
"namespace": "mcp-system",
"pod": "mcp-server-7d4f8b9c6-x2j4k"
}
}| Field | Type | Required | Description |
|---|---|---|---|
alertName |
string | Yes | Name of the alert rule that fired |
severity |
number | Yes | 0 (Critical) to 4 (Verbose) |
description |
string | Yes | Human-readable description of the issue |
affectedResource |
string | No | Resource name (cluster, service, etc.) |
signalType |
string | No | log, metric, or activity |
firedTime |
string | No | ISO 8601 timestamp |
source |
string | No | azure-monitor, elastic, datadog, etc. |
logs |
string | No | Error logs, stack traces |
file |
string | No | Source file where error occurred |
line |
number | No | Line number in source file |
dimensions |
object | No | Additional context (key-value pairs) |
When Azure Monitor sends an alert with the common schema enabled:
{
"schemaId": "azureMonitorCommonAlertSchema",
"data": {
"essentials": {
"alertId": "/subscriptions/{sub}/providers/Microsoft.AlertsManagement/alerts/{id}",
"alertRule": "mcp-error-detection",
"severity": "Sev1",
"signalType": "Log",
"monitorCondition": "Fired",
"monitoringService": "Log Alerts V2",
"alertTargetIDs": [
"/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.ContainerService/managedClusters/{cluster}"
],
"configurationItems": ["aks-mcp-server-prod"],
"originAlertId": "{guid}",
"firedDateTime": "2024-02-06T12:00:00.0000000Z",
"description": "Error detected in MCP server"
},
"alertContext": {
"conditionType": "LogQueryCriteria",
"condition": {
"windowSize": "PT5M",
"allOf": [{
"searchQuery": "ContainerLog | where LogEntry contains 'Error'",
"metricValue": 3
}]
},
"SearchResults": {
"tables": [{
"name": "PrimaryResult",
"columns": [
{"name": "TimeGenerated", "type": "datetime"},
{"name": "LogEntry", "type": "string"}
],
"rows": [
["2024-02-06T11:58:23.456Z", "Error: TokenCredentialAuthenticationError..."]
]
}]
}
}
}
}When Kibana sends an alert:
{
"alertName": "{{rule.name}}",
"alertId": "{{alert.id}}",
"severity": 1,
"description": "{{context.reason}}",
"source": "kibana",
"signalType": "log",
"firedTime": "{{date}}",
"groupByField": "{{context.group}}",
"matchedDocuments": "{{context.matchingDocuments}}",
"logs": "{{#context.hits}}{{_source.message}}\n{{/context.hits}}"
}{
"success": true,
"sessionId": "session_1707228000000",
"sessionUrl": "https://app.devin.ai/sessions/session_1707228000000",
"message": "Devin session created successfully"
}| Field | Type | Description |
|---|---|---|
success |
boolean | true if session was created |
sessionId |
string | Unique Devin session identifier |
sessionUrl |
string | Direct link to watch Devin work |
message |
string | Human-readable status message |
| Code | Meaning | Body |
|---|---|---|
| 200 | Success | Success response with session details |
| 400 | Bad Request | Missing required fields |
| 401 | Unauthorized | Invalid or missing webhook secret |
| 500 | Server Error | Devin API error or internal error |
{
"success": false,
"error": "Missing required field: alertName"
}Common causes:
- Missing
alertNamefield - Missing
severityfield - Invalid JSON body
{
"success": false,
"error": "Unauthorized"
}Causes:
- Missing Authorization header
- Invalid webhook secret
{
"success": false,
"error": "Failed to create Devin session",
"details": "API rate limit exceeded"
}Causes:
- Invalid Devin API key
- Devin API rate limit
- Network error to Devin API
- Devin API outage
curl -X POST https://your-webhook-endpoint/api/trigger-devin \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-webhook-secret" \
-d '{
"alertName": "manual-test-alert",
"severity": 1,
"description": "Testing the webhook integration",
"source": "curl-test",
"logs": "Error: Test error at test.ts:42"
}'import requests
response = requests.post(
"https://your-webhook-endpoint/api/trigger-devin",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer your-webhook-secret"
},
json={
"alertName": "python-test-alert",
"severity": 1,
"description": "Testing from Python",
"source": "python-script",
"logs": "Error: Test error"
}
)
print(response.json())
# {'success': True, 'sessionId': 'session_...', 'sessionUrl': 'https://app.devin.ai/...'}const response = await fetch("https://your-webhook-endpoint/api/trigger-devin", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer your-webhook-secret"
},
body: JSON.stringify({
alertName: "nodejs-test-alert",
severity: 1,
description: "Testing from Node.js",
source: "nodejs-script",
logs: "Error: Test error"
})
});
const data = await response.json();
console.log(data);$body = @{
alertName = "powershell-test-alert"
severity = 1
description = "Testing from PowerShell"
source = "powershell-script"
logs = "Error: Test error"
} | ConvertTo-Json
$response = Invoke-RestMethod -Uri "https://your-webhook-endpoint/api/trigger-devin" `
-Method Post `
-Headers @{
"Content-Type" = "application/json"
"Authorization" = "Bearer your-webhook-secret"
} `
-Body $body
$response| Variable | Description | Example |
|---|---|---|
DEVIN_API_KEY |
Your Devin API key | apk_user_abc123... |
TARGET_REPO |
GitHub repo for Devin to analyze | https://github.com/org/repo |
| Variable | Description | Default |
|---|---|---|
WEBHOOK_SECRET |
Secret for webhook authentication | None (no auth) |
JIRA_PROJECT |
JIRA project key for ticket creation | None |
SLACK_CHANNEL |
Slack channel for notifications | None |
DEVIN_PLAYBOOK_ID |
Playbook name to use | devin-triage-workflow |
Configure in Azure Functions:
- Go to Azure Portal → Function App → Configuration
- Click + New application setting
- Add each variable
- Click Save
- Restart the Function App
Modify the session prompt in route.ts:
const prompt = `
You are an SRE triaging a production alert.
## Alert Details
- **Name**: ${alertName}
- **Severity**: Sev ${severity}
- **Description**: ${description}
- **Time**: ${firedTime}
## Error Logs
\`\`\`
${logs}
\`\`\`
## Your Tasks
1. Clone the repository: ${process.env.TARGET_REPO}
2. Analyze the error and identify root cause
3. Create a fix with proper error handling
4. Write tests for the fix
5. Create a Pull Request
6. Create a JIRA ticket: ${process.env.JIRA_PROJECT}
7. Post to Slack: ${process.env.SLACK_CHANNEL}
Begin triage.
`;Extend the request handler to accept additional fields:
interface AlertPayload {
alertName: string;
severity: number;
description: string;
// Add custom fields
team?: string;
runbook?: string;
priority?: 'P1' | 'P2' | 'P3';
}Route different alert types to different Devin playbooks:
function getPlaybookId(alertName: string): string | undefined {
const playbooks: Record<string, string> = {
'auth-error': 'playbook_auth_123',
'timeout': 'playbook_perf_456',
'null-reference': 'playbook_bug_789',
};
for (const [pattern, id] of Object.entries(playbooks)) {
if (alertName.includes(pattern)) return id;
}
return undefined; // Use default
}| Plan | Requests/minute | Concurrent Sessions |
|---|---|---|
| Starter | 10 | 1 |
| Team | 60 | 5 |
| Enterprise | 300 | 20 |
The endpoint implements exponential backoff:
async function createSessionWithRetry(payload: any, retries = 3): Promise<any> {
for (let i = 0; i < retries; i++) {
try {
return await createDevinSession(payload);
} catch (error: any) {
if (error.status === 429 && i < retries - 1) {
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(r => setTimeout(r, delay));
continue;
}
throw error;
}
}
}This workflow uses the Devin v1 API to create sessions. Below is a summary of the key endpoints.
| Key Type | Prefix | Use Case | Where to Create |
|---|---|---|---|
| Personal API Key | apk_user_* |
Testing, personal automation | Settings → API Keys |
| Service API Key | apk_* |
Team automation, CI/CD | Settings → API Keys |
| Service User (v3) | cog_* |
Enterprise with RBAC | Enterprise → Service Users |
📚 Full details: Authentication & API Keys
POST https://api.devin.ai/v1/sessions
Authorization: Bearer {DEVIN_API_KEY}
Content-Type: application/json
Request:
{
"prompt": "Your task description here...",
"idempotent": true
}Response:
{
"session_id": "session_1707228000000",
"url": "https://app.devin.ai/sessions/session_1707228000000",
"status": "running"
}📚 Full details: v1 Create Session
GET https://api.devin.ai/v1/sessions/{session_id}
Authorization: Bearer {DEVIN_API_KEY}
Response:
{
"session_id": "session_1707228000000",
"status_enum": "running",
"url": "https://app.devin.ai/sessions/session_1707228000000"
}Status values: running, blocked, finished
POST https://api.devin.ai/v1/sessions/{session_id}/message
Authorization: Bearer {DEVIN_API_KEY}
Content-Type: application/json
Request:
{
"message": "Make sure to write unit tests when done."
}Pass temporary credentials that are only available for a single session:
{
"prompt": "Deploy to staging",
"session_secrets": [
{
"key": "DEPLOY_API_KEY",
"value": "temp-key-12345",
"sensitive": true
}
]
}📚 Full details: v1 Usage Examples
import os
import requests
import time
DEVIN_API_KEY = os.getenv("DEVIN_API_KEY")
# Create a new session
response = requests.post(
"https://api.devin.ai/v1/sessions",
headers={"Authorization": f"Bearer {DEVIN_API_KEY}"},
json={
"prompt": "Triage production alert: auth token expired...",
"idempotent": True
}
)
session = response.json()
print(f"Session created: {session['url']}")
# Monitor session status
while True:
status = requests.get(
f"https://api.devin.ai/v1/sessions/{session['session_id']}",
headers={"Authorization": f"Bearer {DEVIN_API_KEY}"}
).json()
print(f"Status: {status['status_enum']}")
if status["status_enum"] in ["blocked", "finished"]:
break
time.sleep(5)| Resource | Link |
|---|---|
| Authentication & API Keys | docs.devin.ai/api-reference/authentication |
| API Overview | docs.devin.ai/api-reference/overview |
| v1 Usage Examples | docs.devin.ai/api-reference/v1/usage-examples |
| v1 Create Session | docs.devin.ai/api-reference/v1/sessions/create-a-new-devin-session |
| Devin App | app.devin.ai |
| Generate API Keys | app.devin.ai/settings/api-keys |
- Azure Monitor Setup — Configure Azure alerts
- Elastic Setup — Configure Elastic alerts
- Devin Playbook — Customize triage behavior
- Deployment Guide — Production deployment options