Roadmap Feature: Webhooks are currently in development. This documentation describes the planned implementation.
Overview
Webhooks allow you to receive real-time HTTP notifications when events occur in Day Copilot, eliminating the need for polling.
Benefits:
- Real-time updates
- Reduced API calls
- Lower latency
- Event-driven architecture
Supported Events (Planned)
Task Events
task.created - New task created
task.updated - Task modified
task.deleted - Task removed
task.completed - Task marked as complete
task.assigned - Task assigned to user
task.comment_added - Comment added to task
Event Events
event.created - New calendar event created
event.updated - Event modified
event.deleted - Event removed
event.attendee_added - Attendee added
event.rsvp_changed - RSVP status updated
Context Events
context.created - New context created
context.shared - Context shared with user
context.updated - Context modified
Webhook Setup
1. Create Webhook Endpoint
Your endpoint must:
- Accept POST requests
- Return 200 status quickly (under 5 seconds)
- Verify webhook signature (security)
- Process events asynchronously
// Example Express.js endpoint
app.post('/webhooks/daycopilot', async (req, res) => {
// Verify signature
const signature = req.headers['x-daycopilot-signature'];
if (!verifySignature(req.body, signature)) {
return res.status(401).send('Invalid signature');
}
// Acknowledge receipt immediately
res.status(200).send('OK');
// Process event asynchronously
processWebhookEvent(req.body).catch(console.error);
});
async function processWebhookEvent(event) {
console.log('Received event:', event.type);
switch (event.type) {
case 'task.created':
await handleTaskCreated(event.data);
break;
case 'task.updated':
await handleTaskUpdated(event.data);
break;
// Handle other events...
}
}
2. Register Webhook
POST /api/v1/webhooks
{
"url": "https://your-app.com/webhooks/daycopilot",
"events": [
"task.created",
"task.updated",
"task.completed"
],
"secret": "your-webhook-secret"
}
Response:
{
"data": {
"id": "webhook-uuid",
"url": "https://your-app.com/webhooks/daycopilot",
"events": ["task.created", "task.updated", "task.completed"],
"created_at": "2025-11-02T12:00:00Z",
"status": "active"
}
}
3. Verify Webhook
Day Copilot will send a verification request:
{
"type": "webhook.verification",
"challenge": "random-string-123"
}
Respond with:
{
"challenge": "random-string-123"
}
All webhook events follow this structure:
{
"id": "event-uuid",
"type": "task.created",
"created_at": "2025-11-02T12:00:00Z",
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"title": "New task from webhook",
"status": "pending",
"priority": "high",
"user_id": "user-uuid"
},
"actor": {
"user_id": "user-uuid",
"email": "user@example.com"
}
}
Fields:
id: Unique event identifier (for deduplication)
type: Event type
created_at: When event occurred
data: Event-specific data
actor: User who triggered the event
Event Examples
task.created
{
"id": "event-123",
"type": "task.created",
"created_at": "2025-11-02T12:00:00Z",
"data": {
"task_id": "task-uuid",
"title": "Complete documentation",
"description": "Write API docs",
"status": "pending",
"priority": "high",
"context_id": "project-uuid"
},
"actor": {
"user_id": "user-uuid",
"email": "alice@example.com"
}
}
task.updated
{
"id": "event-124",
"type": "task.updated",
"created_at": "2025-11-02T12:05:00Z",
"data": {
"task_id": "task-uuid",
"changes": {
"priority": {
"old": "medium",
"new": "urgent"
},
"status": {
"old": "pending",
"new": "in_progress"
}
}
},
"actor": {
"user_id": "user-uuid",
"email": "alice@example.com"
}
}
task.assigned
{
"id": "event-125",
"type": "task.assigned",
"created_at": "2025-11-02T12:10:00Z",
"data": {
"task_id": "task-uuid",
"assignee": {
"user_id": "assignee-uuid",
"email": "bob@example.com"
},
"assigned_by": {
"user_id": "user-uuid",
"email": "alice@example.com"
}
}
}
Security
Signature Verification
Day Copilot signs all webhook requests:
X-DayCopilot-Signature: sha256=abc123...
Verify signature:
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = 'sha256=' + hmac.update(JSON.stringify(payload)).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}
// Usage
const isValid = verifySignature(
req.body,
req.headers['x-daycopilot-signature'],
process.env.WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
Best Practices
Never process webhooks without verifying signatures. This prevents spoofing and ensures authenticity.
Acknowledge webhook receipt within 5 seconds. Process events asynchronously to avoid timeouts.app.post('/webhook', async (req, res) => {
res.status(200).send('OK'); // Respond immediately
await processLater(req.body); // Process async
});
Use event IDs for deduplication:const processedEvents = new Set();
async function handleEvent(event) {
if (processedEvents.has(event.id)) {
console.log('Duplicate event, skipping');
return;
}
processedEvents.add(event.id);
await processEvent(event);
}
If your endpoint fails, Day Copilot will retry with exponential backoff:
- Retry 1: After 1 minute
- Retry 2: After 5 minutes
- Retry 3: After 15 minutes
- Retry 4: After 1 hour
- Retry 5: After 6 hours (final attempt)
Managing Webhooks
List Webhooks
Update Webhook
PUT /api/v1/webhooks/{webhookId}
{
"events": ["task.created", "task.deleted"],
"url": "https://new-url.com/webhook"
}
Delete Webhook
DELETE /api/v1/webhooks/{webhookId}
Test Webhook
Manually trigger a test event:
POST /api/v1/webhooks/{webhookId}/test
{
"event_type": "task.created"
}
Webhook Logs
View delivery logs and retry history:
GET /api/v1/webhooks/{webhookId}/logs?limit=50
Response:
{
"data": [
{
"id": "log-uuid",
"event_id": "event-123",
"event_type": "task.created",
"delivered_at": "2025-11-02T12:00:00Z",
"status_code": 200,
"response_time_ms": 145,
"attempts": 1
},
{
"id": "log-uuid-2",
"event_id": "event-124",
"event_type": "task.updated",
"delivered_at": "2025-11-02T12:05:00Z",
"status_code": 500,
"response_time_ms": 5000,
"attempts": 2,
"last_error": "Connection timeout"
}
]
}
Common Patterns
Sync to External System
async function syncTaskToExternal(event) {
if (event.type === 'task.created' || event.type === 'task.updated') {
await externalSystem.upsertTask({
id: event.data.task_id,
title: event.data.title,
status: event.data.status
});
} else if (event.type === 'task.deleted') {
await externalSystem.deleteTask(event.data.task_id);
}
}
Notification System
async function sendNotification(event) {
if (event.type === 'task.assigned') {
const assignee = event.data.assignee;
const task = event.data;
await sendEmail(assignee.email, {
subject: `Task assigned: ${task.title}`,
body: `You've been assigned to: ${task.title}`
});
}
}
Analytics Tracking
async function trackAnalytics(event) {
await analytics.track(event.actor.user_id, event.type, {
task_id: event.data.task_id,
context_id: event.data.context_id,
timestamp: event.created_at
});
}
Testing Locally
Use ngrok for Local Development
# Start your local server
npm run dev # Running on localhost:3000
# Expose via ngrok
ngrok http 3000
# Forwarding: https://abc123.ngrok.io -> localhost:3000
# Register webhook with ngrok URL
curl -X POST "https://app.daycopilot.ai/api/v1/webhooks" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"url": "https://abc123.ngrok.io/webhooks/daycopilot",
"events": ["task.created"]
}'
Mock Webhook Events
// For testing, simulate webhook events
function mockWebhookEvent(type, data) {
return {
id: `mock-${Date.now()}`,
type,
created_at: new Date().toISOString(),
data,
actor: {
user_id: 'test-user',
email: 'test@example.com'
}
};
}
// Test your handler
const mockEvent = mockWebhookEvent('task.created', {
task_id: 'test-task',
title: 'Test Task'
});
await handleEvent(mockEvent);
Current Status
Webhooks are planned for release in Q1 2026. Until then, use polling:
// Polling alternative (use until webhooks available)
async function pollForChanges() {
let lastCheck = new Date();
setInterval(async () => {
const tasks = await fetch(
`https://app.daycopilot.ai/api/v1/tasks?updated_after=${lastCheck.toISOString()}`,
{ headers: { 'Authorization': `Bearer ${token}` } }
).then(r => r.json());
for (const task of tasks.data) {
await handleTaskUpdate(task);
}
lastCheck = new Date();
}, 60000); // Poll every minute
}
Next Steps
Feedback
We’re designing webhooks based on developer feedback. Let us know what events you need:
Request Webhook Features
Share your webhook requirements with our API team