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
First API Call
Get started with the API
Error Handling
Handle webhook delivery failures
Authentication
Secure your webhook endpoint
API Reference
Explore all endpoints
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