# Change data feed

> Follow every change to rows, the schema and notes as it happens: poll the change feed, or have the engine push it to a webhook.

The change feed, `list_changes`, lists every change as an event: writes through the engine, hand edits with the rules they break, schema changes and, when asked for, tool calls. It works without history. Poll it from a position, or wait for the next event with `wait_ms`; a webhook has the engine post each event to a web address instead.

[Change feed and webhooks](https://datanotes.dev/develop/change-feed-and-webhooks/) shows how a program follows the feed.

## Follow every change with the change feed

`list_changes` returns numbered row changes across all tables, oldest first. It includes engine writes and hand edits (`source: external`). Keep the returned `last` and pass it as `since` next time to get only what’s new. `wait_ms` waits for the next change, a technique called long polling.

**`list_changes`**

```json
{ "tables": ["film"], "limit": 3 }
```

Answer (74 lines)

```json
{
  "events": [
    {
      "seq": 3,
      "at": "2026-10-12T16:52:36.274Z",
      "table": "main.public.film",
      "kind": "insert",
      "op": "insert_row",
      "path": "datanotes/main/public/film/Nosferatu.md",
      "row_id": "01a13107-c92d-767d-9b60-f4e03575c27e",
      "changes": {
        "id": { "after": "01a13107-c92d-767d-9b60-f4e03575c27e" },
        "title": { "after": "Nosferatu" },
        "status": { "after": "to watch" }
      },
      "source": "engine",
      "row": {
        "id": "01a13107-c92d-767d-9b60-f4e03575c27e",
        "title": "Nosferatu",
        "status": "to watch"
      },
      "actor": { "session": "history", "harness": "docs-example", "auth": "owner" },
      "migration_id": "20261012T165236-insert_row-insert-into-film"
    },
    {
      "seq": 4,
      "at": "2026-10-12T16:52:36.280Z",
      "table": "main.public.film",
      "kind": "insert",
      "op": "insert_row",
      "path": "datanotes/main/public/film/Metropolis.md",
      "row_id": "01a13107-c934-747a-90f7-3becda86084f",
      "changes": {
        "id": { "after": "01a13107-c934-747a-90f7-3becda86084f" },
        "title": { "after": "Metropolis" },
        "status": { "after": "to watch" }
      },
      "source": "engine",
      "row": {
        "id": "01a13107-c934-747a-90f7-3becda86084f",
        "title": "Metropolis",
        "status": "to watch"
      },
      "actor": { "session": "history", "harness": "docs-example", "auth": "owner" },
      "migration_id": "20261012T165236-insert_row-insert-into-film-2"
    },
    {
      "seq": 5,
      "at": "2026-10-12T16:52:36.287Z",
      "table": "main.public.film",
      "kind": "update",
      "op": "update_row",
      "path": "datanotes/main/public/film/Nosferatu.md",
      "row_id": "01a13107-c92d-767d-9b60-f4e03575c27e",
      "changes": {
        "status": { "before": "to watch", "after": "watched" },
        "rating": { "after": 4 },
        "watched_on": { "after": "2026-10-03" }
      },
      "source": "engine",
      "row": {
        "id": "01a13107-c92d-767d-9b60-f4e03575c27e",
        "title": "Nosferatu",
        "status": "watched",
        "rating": 4,
        "watched_on": "2026-10-03"
      },
      "actor": { "session": "history", "harness": "docs-example", "auth": "owner" },
      "migration_id": "20261012T165236-update_row-update-film"
    }
  ],
  "last": 5,
  "head": 13
}
```

**`insert_row`**

```json
{ "table": "film", "values": { "title": "His Girl Friday" } }
```

With `since`, the answer holds only the newer changes:

**`list_changes`**

```json
{ "since": 13 }
```

Answer:

```json
{
  "events": [
    {
      "seq": 14,
      "at": "2026-10-12T16:52:40.870Z",
      "table": "main.public.film",
      "kind": "insert",
      "op": "insert_row",
      "path": "datanotes/main/public/film/His Girl Friday.md",
      "row_id": "01a13107-db21-7bf5-942a-c5107eb7fed3",
      "changes": {
        "id": { "after": "01a13107-db21-7bf5-942a-c5107eb7fed3" },
        "title": { "after": "His Girl Friday" },
        "status": { "after": "to watch" }
      },
      "source": "engine",
      "row": {
        "id": "01a13107-db21-7bf5-942a-c5107eb7fed3",
        "title": "His Girl Friday",
        "status": "to watch"
      },
      "actor": { "session": "history", "harness": "docs-example", "auth": "owner" },
      "migration_id": "20261012T165240-insert_row-insert-into-film"
    }
  ],
  "last": 14,
  "head": 14
}
```

## Push changes to a web address with webhooks

`create_webhook` makes the engine send row changes in batches to a web address (URL), as `POST` requests. The engine signs each batch with a hash-based message authentication code (HMAC) of the body, in `X-Datanotes-Signature`. It retries until the receiver answers with a 2xx status. Only this answer returns the `secret`: give it to the receiver so it can check the signature.

**`create_webhook`**

```json
{ "url": "http://127.0.0.1:39785/datanotes-hook", "tables": ["film"] }
```

Answer:

```json
{
  "id": "wh_bdde32fd8ee4",
  "url": "http://127.0.0.1:39785/datanotes-hook",
  "tables": ["film"],
  "enabled": true,
  "cursor": 14,
  "created_at": "2026-10-12T16:52:40.874Z",
  "failures": 0,
  "status": "ok",
  "secret": "<shown once>"
}
```

**`update_row`**

```json
{
  "table": "film",
  "path": "datanotes/main/public/film/His Girl Friday.md",
  "set": { "status": "watched" }
}
```

The receiver got this request:

```json
{
  "headers": {
    "x-datanotes-signature": "sha256=d0c9b0450dd3eaec5843de6883d0c16ad058985fa459cff136d0c5035a4ccc1e"
  },
  "body": {
    "webhook": "wh_bdde32fd8ee4",
    "events": [
      {
        "seq": 15,
        "at": "2026-10-12T16:52:40.883Z",
        "table": "main.public.film",
        "kind": "update",
        "op": "update_row",
        "path": "datanotes/main/public/film/His Girl Friday.md",
        "row_id": "01a13107-db21-7bf5-942a-c5107eb7fed3",
        "changes": { "status": { "before": "to watch", "after": "watched" } },
        "source": "engine",
        "row": {
          "id": "01a13107-db21-7bf5-942a-c5107eb7fed3",
          "title": "His Girl Friday",
          "status": "watched"
        },
        "actor": { "session": "history", "harness": "docs-example", "auth": "owner" },
        "migration_id": "20261012T165240-update_row-update-film"
      }
    ],
    "last": 15
  }
}
```

**`list_webhooks`** (no arguments)

Answer:

```json
{
  "webhooks": [
    {
      "id": "wh_bdde32fd8ee4",
      "url": "http://127.0.0.1:39785/datanotes-hook",
      "tables": ["film"],
      "enabled": true,
      "cursor": 15,
      "created_at": "2026-10-12T16:52:40.874Z",
      "last_delivery": "2026-10-12T16:52:40.886Z",
      "failures": 0,
      "status": "ok"
    }
  ]
}
```

**`delete_webhook`**

```json
{ "id": "wh_bdde32fd8ee4" }
```

Answer:

```json
{ "deleted": true }
```
