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

# Row-level change history and bulk upsert behavior

> View a dataset row's full edit history, understand how bulk API upserts affect that history, and export audit data with the SQL audit log option.

> [!NOTE]
>
> **Applies to:**
>
> * Plan - Any
> * Deployment - Any
> *
> * Use case - Retrieve a dataset row's edit history (what changed, when, and available actor attribution), and understand how bulk API upserts affect row history

## Summary

A bulk API upsert creates a new row version without erasing earlier Activity entries. Use the Trace view Activity tab or the `POST /btql` export with `audit_log: true` to retrieve row-level change history. API writes may be attributed only as `API user`.

## What is happening

Each dataset row has an Activity log with timestamps, change details, and available attribution. In the repro, a bulk API upsert created a new row version while earlier Activity entries remained available. The `_xact_id` returned by a current-row query identifies the current version. It does not list every prior transaction. UI edits can include field-level change details. API upsert audit entries may report only `upsert`, so compare successive row versions to determine which values changed.
Each dataset row has an Activity log with timestamps, change details, and available attribution. A bulk API upsert creates a new row version while earlier Activity entries remain available. The `_xact_id` returned by a current-row query identifies the current version. It does not list every prior transaction. UI edits can include field-level change details. API upsert audit entries may report only `upsert`, so compare successive row versions to determine which values changed.

## Fix or suggestion

### Option 1: Use the UI Activity tab

1. Open the dataset and open the row in Trace view.
2. Look at the Activity tab to see the change history, timestamps, available change details, and attribution.

Use this when you want a quick, human-readable audit trail for a single row.

### Option 2: Export row change history via the API

1. Call the `POST /btql` endpoint and set `audit_log` to `true` in the request body.
2. Query the specific dataset ID and an exact row ID.

Minimal request example:

```http theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
POST /btql
Authorization: Bearer <API_KEY>
Content-Type: application/json

{
  "query": "SELECT * FROM dataset('<DATASET_ID>') WHERE id = '<ROW_ID>' LIMIT 10000",
  "audit_log": true,
  "disable_limit": true,
  "fmt": "json"
}
```

3. Inspect the returned audit entries for timestamps, actions, and available actor metadata.
4. If an API upsert entry does not show which values changed, query the row at successive `_xact_id` values using the request's `version` parameter and compare the returned values.

Use this when you need automated exports, row-level forensic data, or to include history in downstream workflows. An entry attributed as `API user` does not, by itself, identify the individual caller.

## How to confirm it worked

* In the UI, the Activity tab shows the expected prior entries, timestamps, and available change details for the row.
* In the API response, `audit_log: true` returns separate activity entries for the row. Compare successive versions when an API upsert entry does not include field changes.

## Notes

* See [Export docs](https://docs.braintrust.dev/annotate/export.md) for `POST /btql` and the `audit_log` option for more examples.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.