Guide
History API
Use the History API to access a full, paginated log of every change made in your Fibery workspace. Common use cases include:
Building custom audit trails and compliance reports
Generating activity digests or changelogs ("what changed this week?")
Debugging automation behavior by inspecting what triggered a change
Powering external change-notification workflows
This API does not include changes to Documents content (rich text fields).
Authentication
All requests require a bearer token. Generate one in your workspace under Settings → API Tokens.
Include the following headers in every request:
Authorization: Bearer {your-api-token}
Content-Type: application/json
Base URL: https://{your-workspace}.fibery.io
POST /api/history/v2/search
Returns a paginated list of workspace history entries matching the given filters.
Request body
Parameters
Parameter | Required | Description |
|---|
where
| No | Array of filter objects. All filters are combined with logical AND. Pass [] for no filters. |
timeframe
| No | Date range to search. Maximum span is 1 year. If omitted, defaults to last 30 days. Values are ISO 8601 datetime strings in UTC. |
limit
| Yes | Number of items per page. The API may return fewer items than requested even when more pages exist — always check nextPage.hasNext. |
excludeAutomaticChanges
| No | Specifies which types of automatic changes to exclude from processing. |
sinceItem
| No | ID of the last item from the previous page. Pass this to retrieve the next page. |
Sample request
{
"where": [
{
"field": "typeId",
"operator": "=",
"value": "{database-uuid}"
},
{
"field": "action",
"operator": "in",
"value": ["fibery.entity/create", "fibery.entity/delete"]
}
],
"timeframe": {
"start": "{start-datetime}",
"end": "{end-datetime}"
},
"excludeAutomaticChanges": ["automations", "integrations", "formulas", "auto-linking"],
"limit": 50,
"sinceItem": "{since-item-id}"
}
Filters (where)
Field | Description | Operators | Value |
|---|
id
| History entry ID | = in
| Number |
action
| Type of change performed | = in
| See action values below |
entityPublicId
| Entity short public ID | = in
| String |
entityState
| Current state of the entity | = in
| EXIST DELETED ARCHIVED
|
author
| User who made the change | = in empty not-empty
| User UUID. For empty/not-empty, omit the value key entirely. |
typeId
| Database ID — returns entity changes only, no schema changes | = in
| Database UUID |
entityTypeId
| Database ID — returns entity changes and all related schema changes | = in
| Database UUID |
entityName
| Entity name (partial match) | contains
| Text (minimum 3 characters) |
entityId
| Entity UUID | = in
| UUID |
field
| Exclude specific field types from results | not-in
| See field filter section below |
Available action values:
fibery.entity/create
fibery.entity/delete
fibery.entity/update
fibery.entity/add-collection-items
fibery.entity/remove-collection-items
history/fibery.entity/archived
history/fibery.entity/restored
history/permissions-changed
The field filter (not-in):
Use this filter to exclude noisy system-generated changes and get a clean log of meaningful, human-made edits. It supports specific field names and wildcard aliases:
Alias | What it excludes |
|---|
:hidden
| All hidden fields |
:private
| All private fields |
:deleted
| All deleted fields |
:createdBy
| Created-by fields |
:system
| All system fields |
:name
| All title fields |
Example — exclude hidden fields and rank fields:
{
"field": "field",
"operator": "not-in",
"value": [
{
"field": ":hidden",
"options": { "includeSoftDeleted": true }
},
{ "field": "fibery/rank" },
{ "field": "fibery/menu-rank" },
{ "type": "Collaboration~Documents/Document" },
{ "type": "fibery/rich-text" },
{ "field": "fibery/public-id" },
{ "field": ":createdBy" }
]
}
Exclude automatic changes (excludeAutomaticChanges)
Use this option to exclude noisy changes made by system automatically
Alias | What it excludes |
|---|
integrations
| All integration changes |
automations
| Changes made by automation rules |
auto-linking
| Automatically linked relations |
formulas
| All formula fields |
Response body
Example response
{
"items": [
{
"id": "120659039",
"action": "fibery.entity/update",
"date": "{ISO 8601 datetime}",
"schemaVersion": 22782,
"sequenceId": "18740668",
"type": {
"id": "1e1d96c0-5dcb-11e8-90b6-c6e140253257",
"name": "Product Management/feature",
"title": "Feature",
"exist": true,
"softDeleted": false
},
"entity": {
"id": "d7cea490-354d-11eb-9bfa-a53706ca0a40",
"name": "History API",
"exist": true,
"softDeleted": false,
"publicId": "2797",
"capabilities": { "restore": false }
},
"author": {
"id": "79f2f29b-9a45-4e30-9ba6-b785f10c78ec",
"name": "Eugene Kisel",
"exist": true,
"softDeleted": false,
"publicId": "1210"
},
"order": "120659039",
"fromService": null,
"caller": null,
"changedValues": [
{
"field": {
"id": "56cba110-5dcb-11e8-90b6-c6e140253257",
"name": "workflow/state",
"title": "State"
},
"currentValue": {
"id": "5715c970-5dcb-11e8-90b6-c6e140253257",
"name": "In Progress"
},
"previousValue": {
"id": "c9cfb7f0-a45d-11ec-b0b9-197891c72881",
"name": "Next"
}
}
]
}
],
"nextPage": {
"hasNext": true,
"sinceItem": "120550073"
}
}
items — array of history entries:
Field | Description |
|---|
id
| Unique ID of this history entry |
action
| The type of change that was made |
date
| ISO 8601 datetime of when the change occurred |
schemaVersion
| Workspace schema version at the time of the change — useful for correlating changes with schema migrations |
sequenceId
| Monotonically increasing event sequence ID — use this to establish ordering when processing events across pages |
type
| The Database the changed entity belongs to (id, name, title) |
entity
| The entity that was changed (id, name, publicId, exist, softDeleted) |
author
| The user who made the change. If null, the change was made by the system or an automation — see fromService. |
fromService
| Internal Fibery service that triggered the change (set when author is null) |
caller
| Additional source information related to fromService |
changedValues
| List of field changes. Each item contains field (which field changed), currentValue (value after), and previousValue (value before). |
nextPage — pagination metadata:
Field | Description |
|---|
hasNext
| true if more results exist on the next page
|
sinceItem
| Pass this as sinceItem in the next request to retrieve the following page |
hasNextLimited
| true when a workspace plan limit has been reached. Upgrade the workspace to access further history entries.
|
Datetime format
All timeframe values must be ISO 8601 datetime strings in UTC.
Boundary | Example value |
|---|
Start of a day (midnight) | 2026-03-01T00:00:00.000Z
|
End of a day (last millisecond) | 2026-03-01T23:59:59.999Z
|
Start of an hour | 2026-03-01T14:00:00.000Z
|
Start of a minute | 2026-03-01T14:30:00.000Z
|
The maximum allowed span between start and end is 1 year.
For the "Get all changes from a specific day" sample below:
For all other samples, replace {start-datetime} and {end-datetime} with ISO 8601 values matching your desired range.
Query samples
Get all changes from a specific day
{
"where": [],
"timeframe": {
"start": "{start-of-day}",
"end": "{end-of-day}"
},
"limit": 50
}
Get changes to a specific Database
Replace {feature-type-uuid} with the Database UUID (find it in Database settings).
{
"where": [
{
"field": "typeId",
"operator": "=",
"value": "{feature-type-uuid}"
}
],
"timeframe": {
"start": "{start-datetime}",
"end": "{end-datetime}"
},
"limit": 50,
"sinceItem": "{next-page-since-item}"
}
To include schema changes (field additions, renames, deletions) alongside entity changes, replace typeId with entityTypeId.
Get all changes by a specific user
{
"where": [
{
"field": "author",
"operator": "=",
"value": "{user-uuid}"
}
],
"timeframe": {
"start": "{start-datetime}",
"end": "{end-datetime}"
},
"limit": 50,
"sinceItem": "{next-page-since-item}"
}
Get all changes to a specific entity
By entity UUID:
{
"where": [
{
"field": "typeId",
"operator": "=",
"value": "{type-uuid}"
},
{
"field": "entityId",
"operator": "=",
"value": "{entity-uuid}"
}
],
"timeframe": {
"start": "{start-datetime}",
"end": "{end-datetime}"
},
"limit": 50
}
By public ID (the short numeric ID shown in the Fibery UI):
{
"where": [
{
"field": "typeId",
"operator": "=",
"value": "{type-uuid}"
},
{
"field": "entityPublicId",
"operator": "=",
"value": "{entity-public-id}"
}
],
"timeframe": {
"start": "{start-datetime}",
"end": "{end-datetime}"
},
"limit": 50
}
Exclude automatic and system-generated changes
The log includes many system-generated changes by default (rank updates, formula recalculations, automation triggers). Use field not-in to filter them out:
{
"where": [
{
"field": "field",
"operator": "not-in",
"value": [
{
"field": ":hidden",
"options": { "includeSoftDeleted": true }
},
{ "field": "fibery/rank" },
{ "field": "fibery/menu-rank" },
{ "type": "Collaboration~Documents/Document" },
{ "type": "fibery/rich-text" },
{ "field": "fibery/public-id" },
{ "field": ":createdBy" },
]
}
],
"excludeAutomaticChanges": ["integrations", "automations", "auto-linking", "formulas"],
"timeframe": {
"start": "{start-datetime}",
"end": "{end-datetime}"
},
"limit": 50
}
Paginating through results
The API uses cursor-based pagination via sinceItem. To retrieve all pages:
Make an initial request without sinceItem.
If nextPage.hasNext is true, copy the value of nextPage.sinceItem.
Pass it as sinceItem in the next request.
Repeat until hasNext is false.
The API may return fewer items per page than your limit value, even when hasNext is true. This can happen when long-running internal tasks are interrupted mid-page. Always use hasNext to determine whether more results exist — do not assume the last page has been reached just because a partial result was returned.
Use cases
Feel free to use the scripts below as is, fork them, or simply feed them to your coding agent as an example when working with History API.
Find maximum historical value of a Number Field
For each entity in a Database, get the maximum historical value of a certain Number Field. For example, understand what was the peak revenue for each customer.
Here's the solution overview (typed by a human):
Filter entities to avoid querying everything in a Database (History API is quite slow by design).
For each entity, find the Field’s maximum historical value.
Write the results into a CSV file for manual inspection.
Take the CSV file and update another Field with the maximum historical value for each entity.
And here's the source code (co-created with :claude:):
https://gitlab.com/fibery-community/api-examples/-/tree/master/extract-historical-max-value
Find when a Number Field value crossed a threshold
For each entity in a Database, understand when the value of a certain Number Field crossed a threshold.
For example, for each Event, understand when the number of registrations crossed the 100 people mark — the point when an Event typically breaks even and is worth organizing.
Here's the solution:
Find threshold crossings (e.g., when Subscription.Users became ≥ 2).
Enrich them (if the Number Field is on Subscriptions DB but the Date Field should be on Workspaces DB, we have to to map IDs).
Set the dates (→ Workspace.[Second Purchase Date]).
Source code in our community repo:
https://gitlab.com/fibery-community/api-examples/-/tree/master/find-threshold-crossings-via-history
Related guides