# React to Plan changes with webhooks

> Receive Dailybot Plan events on your own endpoint: the 25 tasks.* events, the payload envelope without task titles, verifying X-BEARER, and fetching details with your own credential.

Language: en
Canonical: https://www.dailybot.com/developers/plan/recipes/webhooks
Markdown: send header `Accept: text/markdown` on any URL to receive Markdown instead of HTML.
Last Updated: 2026-09-25

---

> **Beta** — Plan is in beta. Everything under `/plan` in the web app, the CLI and agent skill commands for projects, goals, boards and tasks, and the `/v1/plan/` public API may change before general availability. Want to try it with your team? Write to **support@dailybot.com**.

Polling is right for a screen someone is looking at. For a server that must react to every change (sync to another tool, notify a channel, update a report), subscribe to **Plan events** and let Dailybot call you.

<h2 id="subscribe">1. Subscribe through your organization's webhooks</h2>

Plan events are delivered through the same outgoing webhooks as every other Dailybot event. There is no separate webhook endpoint under `/v1/plan/`. Create the subscription in the web app or with the [Webhooks API](/developers/api/webhooks), choose the `tasks.*` events you need, and set a secret for the `X-BEARER` header. See [Webhooks & events](/developers/webhooks) for the setup.

<h2 id="events">2. The 25 Plan events</h2>

| Object | Events |
|---|---|
| Task | `tasks.task.created` · `tasks.task.updated` · `tasks.task.state_changed` · `tasks.task.owner_changed` · `tasks.task.moved` · `tasks.task.comment_created` · `tasks.task.archived` · `tasks.task.participant_added` · `tasks.task.participant_removed` |
| Board | `tasks.board.created` · `tasks.board.updated` · `tasks.board.archived` · `tasks.board.restored` · `tasks.board.member_added` · `tasks.board.member_removed` |
| Project | `tasks.project.created` · `tasks.project.updated` · `tasks.project.member_added` · `tasks.project.member_removed` · `tasks.project.archived` · `tasks.project.restored` |
| Goal | `tasks.goal.created` · `tasks.goal.updated` · `tasks.goal.archived` · `tasks.goal.restored` |

- There is **no `tasks.task.deleted`**: archive is the delete, so listen for `tasks.task.archived`.
- New events are added over time. **Ignore events you do not recognise** instead of failing.
- Some changes appear only in the activity feed, not as webhooks (for example a retired milestone, recorded as `project.milestone_deleted`).

<h2 id="payload">3. What arrives: identifiers, never titles</h2>

Every delivery is a JSON `POST` with the standard envelope. For Plan, `body` is the event record:

```json
{
  "event": "tasks.task.state_changed",
  "event_timestamp": "2026-09-25T10:14:02Z",
  "hook": { "id": "wh-1234-abcd", "name": "Tasks sync" },
  "body": {
    "event": "tasks.task.state_changed",
    "occurred_at": "2026-09-25T10:14:02.113954Z",
    "observed_at": "2026-09-25T10:14:02.113954Z",
    "organization_uuid": "00000000-0000-4000-8000-000000000101",
    "actor": { "kind": "user", "uuid": "00000000-0000-4000-8000-00000000000c" },
    "correlation_id": "00000000-0000-4000-8000-000000000102",
    "entity": { "type": "task", "uuid": "00000000-0000-4000-8000-000000000005", "key": "ENG-142" },
    "data": {
      "from_state_uuid": "00000000-0000-4000-8000-000000000003",
      "to_state_uuid": "00000000-0000-4000-8000-000000000004"
    }
  }
}
```

**No title, description, comment text or label name is ever in a payload.** That is deliberate: a webhook URL is the least trusted place an event can go. The task `key` is the only human-readable field. When you need the content, fetch it with a credential of your own, which is refused if that credential may not see it:

```bash
curl -sS "https://api.dailybot.com/v1/plan/tasks/ENG-142/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY" | jq '{key, title, state: .state.name}'
```

<h2 id="verify">4. Verify every delivery</h2>

Deliveries are not signed. Instead, each request carries an **`X-BEARER` header with the secret you set** on the subscription. Reject anything else, compare in constant time, and only accept HTTPS:

```js
import { timingSafeEqual } from 'node:crypto';

function isFromDailybot(req) {
  const received = Buffer.from(req.headers['x-bearer'] ?? '');
  const expected = Buffer.from(process.env.DAILYBOT_WEBHOOK_SECRET);
  return received.length === expected.length && timingSafeEqual(received, expected);
}
```

OAuth 2.0 is also available for webhook authentication; see [Webhooks & events](/developers/webhooks).

<h2 id="handle">5. Handle it well</h2>

- **Answer quickly** with a `2xx` and do the work asynchronously.
- **Make processing idempotent**: key it on the event record (`entity.uuid`, `event`, `occurred_at`) so a repeated delivery or your own retry does no harm.
- **Order by `occurred_at`**, not by arrival time.
- **Re-read before acting** when the decision depends on the current state; the event says what changed, the API says what is true now.

<h2 id="reference">Reference</h2>

- [Webhooks & events](/developers/webhooks): subscriptions, envelope and authentication
- [`GET /v1/plan/tasks/{task_id}/`](/developers/api/plan-tasks#plan-task-detail)
- [`GET /v1/plan/tasks/{task_id}/events/`](/developers/api/plan-collaboration#plan-task-events-list): a task's event log

---

## Developer portal navigation

**Getting Started**

- [Overview](/developers)
- [Quick start](/developers/getting-started)
- [Authentication](/developers/authentication)

**API Reference**

- [API Overview](/developers/api)
- [Users](/developers/api/users)
- [Organization](/developers/api/organization)
- [Teams](/developers/api/teams)
- [Invitations](/developers/api/invitations)
- [Check-ins](/developers/api/check-ins)
- [Forms](/developers/api/forms)
- [Labels](/developers/api/labels)
- [Report channels](/developers/api/report-channels)
- [Templates](/developers/api/templates)
- [Kudos](/developers/api/kudos)
- [Mood tracking](/developers/api/mood)
- [Important dates](/developers/api/important-dates)
- [Messaging](/developers/api/messaging)
- [Automations](/developers/api/workflows)
- [Webhooks](/developers/api/webhooks)
- [Commands platform](/developers/api/commands-platform)
- [Agents](/developers/api/agents)
- [OAuth2](/developers/api/oauth2)
- [Integrations](/developers/api/integrations)
- [CLI](/developers/api/cli)
- [Plan · Projects](/developers/api/plan-projects)
- [Plan · Goals](/developers/api/plan-goals)
- [Plan · Boards](/developers/api/plan-boards)
- [Plan · Tasks](/developers/api/plan-tasks)
- [Plan · Comments & files](/developers/api/plan-collaboration)
- [Plan · Home & search](/developers/api/plan-home)
- [Plan · Notifications & reports](/developers/api/plan-notifications)

**Dailybot Plan**

- [Overview](/developers/plan)
- [Concepts](/developers/plan/concepts)
- [Quickstart](/developers/plan/quickstart)
- [Authentication & scopes](/developers/plan/authentication)
- [Agents on Plan](/developers/plan/agents)
- [Conventions](/developers/plan/conventions)
- [Errors](/developers/plan/errors)
- [CLI for Plan](/developers/plan/cli)
- [Agent skill](/developers/plan/agent-skill)
- [Recipe: live board](/developers/plan/recipes/board-live-updates)
- [Recipe: home in one request](/developers/plan/recipes/home-in-one-request)
- [Recipe: bulk create](/developers/plan/recipes/bulk-create)
- [Recipe: move on PR merge](/developers/plan/recipes/move-on-pr-merge)
- [Recipe: goal progress](/developers/plan/recipes/goal-progress)
- [Recipe: webhooks](/developers/plan/recipes/webhooks) (this page)

**API guides**

- [Errors & Status Codes](/developers/errors)
- [Rate Limits](/developers/rate-limits)
- [Conventions](/developers/conventions)
- [API Changelog](/developers/api-changelog)
- [Recipes](/developers/recipes)

**Developer Features**

- [Custom commands](/developers/custom-commands)
- [Serverless commands](/developers/serverless)
- [Webhooks & events](/developers/webhooks)
- [Automation API trigger](/developers/workflow-trigger)
- [Activity API](/developers/activity-api)

**CLI**

- [Overview](/developers/cli)
- [Authentication](/developers/cli-authentication)
- [Command reference](/developers/cli-reference)
- [CI/CD recipes](/developers/cli-ci-cd)
- [Configuration](/developers/cli-configuration)
- [Troubleshooting](/developers/cli-troubleshooting)

**Agent Skill**

- [Overview](/developers/agent-skill)
- [Skills catalog](/skills)

---

## Site navigation

**Product:**
- [Home](/)
- [Product](/product)
- [Pricing](/pricing)
- [Enterprise](/enterprise)
- [Integrations](/integrations)
- [Templates](/templates)

**Resources:**
- [Blog](/blog)
- [Academy](/academy)
- [Changelog](/changelog)
- [Help Center](/help)
- [Developers](/developers)
- [Agents](/agents)

**Company:**
- [About](/about)
- [Careers](/careers)
- [Security](/security)
- [Contact Sales](/demo)

**Connect:**
- [LinkedIn](https://www.linkedin.com/company/dailybot/)
- [X/Twitter](https://twitter.com/dailybot)
- [GitHub](https://github.com/Dailybot-Inc)
- [YouTube](https://www.youtube.com/channel/UC3uM9V52vwX7e3vQpCc4qvA)

