Skip to main content
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"
}

Webhook Payload Format

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

GET /api/v1/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