Skip to main content

Webhooks

Webhooks enable real-time notifications when events occur in Deeployd. Instead of polling the API, receive HTTP callbacks when agents complete tasks, conversations end, or workflows finish.

Why Webhooks?

  • Real-time updates: Know immediately when events happen
  • Efficient: No polling required
  • Reliable: Automatic retries on failure
  • Secure: Signature verification

Creating Webhooks

From the Dashboard

  1. Go to SettingsWebhooks
  2. Click + Add Webhook
  3. Enter your endpoint URL
  4. Select events to subscribe to
  5. Click Create

Via API

const webhook = await client.webhooks.create({
url: 'https://your-app.com/webhooks/deeployd',
events: [
'conversation.completed',
'task.completed',
'task.failed',
'workflow.completed'
],
description: 'Production webhook'
});

console.log(webhook.id); // webhook-123
console.log(webhook.secret); // whsec_abc123...

Webhook Events

Conversation Events

EventDescription
conversation.createdNew conversation started
conversation.messageNew message in conversation
conversation.completedConversation ended
conversation.errorConversation error occurred

Task Events

EventDescription
task.createdTask created
task.startedTask execution began
task.completedTask finished successfully
task.failedTask failed
task.cancelledTask was cancelled

Workflow Events

EventDescription
workflow.startedWorkflow execution began
workflow.completedWorkflow finished successfully
workflow.failedWorkflow failed
workflow.human_taskHuman task awaiting response

Agent Events

EventDescription
agent.createdAgent created
agent.updatedAgent configuration changed
agent.deletedAgent deleted
agent.tool_calledAgent called a tool

Webhook Payload

Standard Format

{
"id": "evt_abc123",
"type": "task.completed",
"createdAt": "2024-01-15T10:00:00Z",
"data": {
"taskId": "task-456",
"agentId": "agent-789",
"status": "completed",
"output": {
"result": "Analysis complete",
"summary": "..."
},
"duration": 15000,
"tokensUsed": 1500
}
}

Event-Specific Data

conversation.completed

{
"type": "conversation.completed",
"data": {
"conversationId": "conv-123",
"agentId": "agent-456",
"userId": "user-789",
"messageCount": 12,
"duration": 180000,
"tokensUsed": 3500,
"endReason": "user_ended"
}
}

task.failed

{
"type": "task.failed",
"data": {
"taskId": "task-123",
"agentId": "agent-456",
"status": "failed",
"error": {
"code": "TIMEOUT",
"message": "Task exceeded maximum execution time"
},
"retryCount": 3,
"willRetry": false
}
}

workflow.human_task

{
"type": "workflow.human_task",
"data": {
"workflowId": "workflow-123",
"runId": "run-456",
"taskId": "human-task-789",
"prompt": "Please approve this expense report",
"approvers": ["manager@company.com"],
"expiresAt": "2024-01-16T10:00:00Z"
}
}

Signature Verification

All webhook requests include a signature for verification:

X-Deeployd-Signature: sha256=abc123...
X-Deeployd-Timestamp: 1705312200

Verifying Signatures

import crypto from 'crypto';

function verifyWebhook(
payload: string,
signature: string,
timestamp: string,
secret: string
): boolean {
// Check timestamp to prevent replay attacks
const now = Math.floor(Date.now() / 1000);
const ts = parseInt(timestamp);
if (Math.abs(now - ts) > 300) { // 5 minute tolerance
return false;
}

// Compute expected signature
const signedPayload = `${timestamp}.${payload}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');

// Compare signatures
const expected = `sha256=${expectedSignature}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}

// Express.js example
app.post('/webhooks/deeployd', (req, res) => {
const signature = req.headers['x-deeployd-signature'];
const timestamp = req.headers['x-deeployd-timestamp'];
const payload = JSON.stringify(req.body);

if (!verifyWebhook(payload, signature, timestamp, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}

// Process the webhook
const event = req.body;
console.log('Received:', event.type);

res.status(200).send('OK');
});

SDK Verification

import { verifyWebhookSignature } from '@deeployd/sdk';

const isValid = verifyWebhookSignature({
payload: req.body,
signature: req.headers['x-deeployd-signature'],
timestamp: req.headers['x-deeployd-timestamp'],
secret: WEBHOOK_SECRET
});

Retry Policy

Failed webhook deliveries are retried automatically:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
68 hours

After 6 failed attempts, the webhook is marked as failed.

What Counts as Failure?

  • HTTP status code >= 400
  • Connection timeout (30 seconds)
  • Network error

Responding to Webhooks

Return a 2xx status code quickly:

app.post('/webhooks/deeployd', async (req, res) => {
// Acknowledge receipt immediately
res.status(200).send('OK');

// Process asynchronously
processWebhookAsync(req.body);
});

Managing Webhooks

List Webhooks

const webhooks = await client.webhooks.list();

for (const webhook of webhooks.data) {
console.log({
id: webhook.id,
url: webhook.url,
events: webhook.events,
status: webhook.status
});
}

Update Webhook

await client.webhooks.update('webhook-123', {
events: ['task.completed', 'task.failed', 'workflow.completed'],
status: 'active'
});

Disable Webhook

await client.webhooks.update('webhook-123', {
status: 'disabled'
});

Delete Webhook

await client.webhooks.delete('webhook-123');

Rotate Secret

const webhook = await client.webhooks.rotateSecret('webhook-123');
console.log(webhook.secret); // New secret

Testing Webhooks

Send Test Event

await client.webhooks.test('webhook-123', {
eventType: 'task.completed'
});

Webhook Logs

View recent deliveries in the Dashboard:

  1. Go to SettingsWebhooks
  2. Click on a webhook
  3. View Delivery History

Each delivery shows:

  • Timestamp
  • Event type
  • Response status
  • Response body
  • Duration

Local Development

Use a tunneling service for local development:

# Using ngrok
ngrok http 3000

# Your webhook URL becomes:
# https://abc123.ngrok.io/webhooks/deeployd

Best Practices

1. Verify Signatures

Always verify webhook signatures to prevent spoofing.

2. Respond Quickly

Return 200 immediately, process asynchronously:

// ✅ Good
res.status(200).send('OK');
await queue.add(processWebhook, event);

// ❌ Bad - blocks response
await processWebhook(event);
res.status(200).send('OK');

3. Handle Duplicates

Webhooks may be delivered multiple times. Use idempotency:

const eventId = event.id;

if (await alreadyProcessed(eventId)) {
return; // Skip duplicate
}

await processEvent(event);
await markAsProcessed(eventId);

4. Monitor Webhook Health

Set up alerts for:

  • High failure rates
  • Elevated latency
  • Disabled webhooks

5. Use Specific Events

Subscribe only to events you need:

// ✅ Good - specific events
events: ['task.completed', 'task.failed']

// ❌ Bad - too broad
events: ['*']

Next: Explore Tasks for background job execution.