---
title: "Recent mutations and their diffs, or everything since a watermark"
method: GET
path: "/v1/changes"
tags: ["changes"]
---

# Recent mutations and their diffs, or everything since a watermark

`GET /v1/changes`

## Request

- Base URL: `https://api.indraft.io`
- URL: `https://api.indraft.io/v1/changes`
- Auth: HTTP bearer

Inspect what changed and who changed it: the mutation, the actor, the access path, and the field-level diff. Use this to check your own work.

## Query parameters

- `limit` integer: How many to return. The page bound is published in the schema call, and asking for more is refused rather than quietly truncated.
- `cursor` string: The opaque marker from the previous page. Store it and hand it back; do not parse it. A cursor is only ever emitted when another row really exists, so a page you were given is never empty.
- `since` string: A watermark from a previous read. Everything that has happened after it, in order, which is what makes an outside copy possible without re-reading the world.
- `targetId` string: Only changes to this record. Use it to answer what happened to one account rather than to the workspace.
- `actorId` string: Only changes made by this actor.
- `targetType` string: Only changes to this record type, including a workspace-defined type key.
- `assertionKind` 'observed' | 'inferred' | 'imported' | 'user_supplied' | 'confirmed': Only changes carrying this claim kind.
- `reversible` 'true' | 'false': Only changes that can, or cannot, be undone.

## Response `200`

Success

- object
  - `items` object[], required
    - `id` string, required
    - `actorId` string, required
    - `operation` string, required
    - `targetType` string, required
    - `targetId` string, required
    - `idempotencyKey` string, required
    - `request` unknown
    - `outcome` 'created' | 'updated' | 'matched' | 'archived' | 'merged' | 'reverted' | 'noop', required: What the write did. `noop` means it was accepted and changed nothing, because every value it carried already matched what was stored. IT IS NOT METERED: a noop does not count against your monthly change allowance, so a nightly sync that finds nothing new costs nothing. Neither does a write that only REDUCES what a workspace holds. `matched` means an upsert found an existing record on real identity evidence rather than creating a second one; `reverted` and `merged` are the undo and duplicate paths, and both are ordinary writes that count.
    - `accessPath` 'rest' | 'mcp' | 'app' | 'system', required
    - `attributionLabel` string, required
    - `sourceId` string, required
    - `assertionKind` 'observed' | 'inferred' | 'imported' | 'user_supplied', required
    - `reversible` boolean, required
    - `overrideStanding` boolean, required
    - `revertedBy` string, required
    - `createdAt` string, required
    - `actor` object, required
      - `id` string, required
      - `kind` 'human' | 'agent' | 'api_token' | 'system', required
      - `label` string, required
    - `targetLabel` string, required
    - `sourceLabel` string, required
    - `changes` object[], required
      - `field` string, required
      - `before` unknown
      - `after` unknown
  - `nextCursor` string, required
  - `retention` object, required
    - `oldestRetainedAt` string, required
  - `requestId` string, required

## Other responses

- `401`: No credential, or one that has been revoked.
- `403`: Authenticated, and this capability is not held.
- `422`: The request does not satisfy the schema on this page.
- `429`: Infrastructure protection tripped. This is not a product quota.
- `500`: A defect on our side. The hint says so, and carries an incident id.

---

[API](https://skmtc.dev/indraft/apis/indraft-api.md) · [All operations](https://skmtc.dev/indraft/apis/indraft-api/llms.txt) · [OpenAPI document](https://skmtc.dev/indraft/apis/indraft-api/revisions/d9bb05346d66?raw)
