> ## Documentation Index
> Fetch the complete documentation index at: https://docs.daycopilot.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive real-time notifications when events occur in Day Copilot (Coming Soon)

<Note>
  **Roadmap Feature**: Webhooks are currently in development. This documentation describes the planned implementation.
</Note>

## 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

```javascript theme={null}
// 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

```bash theme={null}
POST /api/v1/webhooks
{
  "url": "https://your-app.com/webhooks/daycopilot",
  "events": [
    "task.created",
    "task.updated",
    "task.completed"
  ],
  "secret": "your-webhook-secret"
}
```

**Response:**

```json theme={null}
{
  "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:

```json theme={null}
{
  "type": "webhook.verification",
  "challenge": "random-string-123"
}
```

**Respond with:**

```json theme={null}
{
  "challenge": "random-string-123"
}
```

## Webhook Payload Format

All webhook events follow this structure:

```json theme={null}
{
  "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

```json theme={null}
{
  "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

```json theme={null}
{
  "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

```json theme={null}
{
  "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:**

```javascript theme={null}
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

<AccordionGroup>
  <Accordion title="Always Verify Signatures">
    Never process webhooks without verifying signatures. This prevents spoofing and ensures authenticity.
  </Accordion>

  <Accordion title="Respond Quickly">
    Acknowledge webhook receipt within 5 seconds. Process events asynchronously to avoid timeouts.

    ```javascript theme={null}
    app.post('/webhook', async (req, res) => {
      res.status(200).send('OK');  // Respond immediately
      await processLater(req.body); // Process async
    });
    ```
  </Accordion>

  <Accordion title="Handle Duplicate Events">
    Use event IDs for deduplication:

    ```javascript theme={null}
    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);
    }
    ```
  </Accordion>

  <Accordion title="Implement Retries">
    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)
  </Accordion>
</AccordionGroup>

## Managing Webhooks

### List Webhooks

```bash theme={null}
GET /api/v1/webhooks
```

### Update Webhook

```bash theme={null}
PUT /api/v1/webhooks/{webhookId}
{
  "events": ["task.created", "task.deleted"],
  "url": "https://new-url.com/webhook"
}
```

### Delete Webhook

```bash theme={null}
DELETE /api/v1/webhooks/{webhookId}
```

### Test Webhook

Manually trigger a test event:

```bash theme={null}
POST /api/v1/webhooks/{webhookId}/test
{
  "event_type": "task.created"
}
```

## Webhook Logs

View delivery logs and retry history:

```bash theme={null}
GET /api/v1/webhooks/{webhookId}/logs?limit=50
```

**Response:**

```json theme={null}
{
  "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

```javascript theme={null}
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

```javascript theme={null}
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

```javascript theme={null}
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

```bash theme={null}
# 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

```javascript theme={null}
// 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:

```javascript theme={null}
// 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

<CardGroup cols={2}>
  <Card title="First API Call" icon="rocket" href="/guides/first-api-call">
    Get started with the API
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/guides/error-handling">
    Handle webhook delivery failures
  </Card>

  <Card title="Authentication" icon="key" href="/authentication">
    Secure your webhook endpoint
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    Explore all endpoints
  </Card>
</CardGroup>

## Feedback

We're designing webhooks based on developer feedback. Let us know what events you need:

<Card title="Request Webhook Features" icon="envelope" href="mailto:api-feedback@daycopilot.ai">
  Share your webhook requirements with our API team
</Card>
