File Management
Endpoints for uploading, listing, reading, writing, and deleting files within libraries.
Files can be addressed two ways: by id (/files/{fileId}) or by path (/contents/{path*}). The path-based routes create parent directories on demand and accept JSON, which makes them convenient for programmatic and AI-generated content.
An App stores its files in an ordinary Library, so these routes reach it like any
other. While that App has a staged deploy in progress, every write route on
this page answers 409 Conflict, whichever library id you used to get there. The
deploy is about to replace that library's entire file set, so a write accepted
now would be destroyed on promotion, under a 2xx and with nothing to tell you.
The error names the App and the way out. Either wait for the deploy to finish, or
discard the staged upload with
DELETE /api/apps/{id}/files/_stage.
The full contract, including which App routes redirect into the staging library
instead of refusing, is on
App File Management.
Reads are never affected, and libraries that belong to no App never see this.
For a library holding an App's files (its live library, a deploy's staging
library, or a snapshot of either), these routes also require write permission on
the App (app:write), the same rule the App's own routes apply:
- Every change: writing, uploading, deleting, bulk edits, indexing, sync, snapshots, sharing, and ownership or connection changes.
- Reading a server-side file: the manifest and server code, the same paths
GET /api/apps/{id}/contents/{path*}guards.
Without it the answer is 403 Forbidden. Asset files stay readable with read
access to the library, and libraries that belong to no App are unaffected.
POST /api/_library-upload
Upload a previously-staged file into a library.
Authentication: Required
Permissions: Requires write access to the target library (defaults to the caller's personal uploads library when libraryId is omitted)
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
uploadId | string | Yes | Id of a staged upload (from the upload flow) |
libraryId | string (UUID) | No | Target library id. Omit to use the caller's uploads library |
path | string | No | File path within the library (e.g. "assets/index.js"). Creates parent directories |
progress | string | No | Progress-tracking identifier |
Response:
Uploaded files are stored as embedded with a 90-day expiresAt. For non-embedded target libraries the file is queued for vector indexing. Responds 201 Created with a Location header pointing at the new file, or 409 Conflict when the target library belongs to an App with a staged deploy.
{
"uploadId": "00000000-0000-4000-8000-000000000001",
"libraryId": "00000000-0000-4000-8000-000000000002"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/libraries/00000000-0000-4000-8000-000000000002/files/00000000-0000-4000-8000-000000000003"
}
},
"extension": "md",
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000003",
"contentType": "text/markdown",
"directory": false,
"filename": "runbook.md",
"parentId": null,
"libraryId": "00000000-0000-4000-8000-000000000002",
"embedded": true,
"expiresAt": "2026-01-15T16:20:00.000Z",
"indexed": true,
"indexingStartedAt": null,
"ownerId": "admin",
"modifiedAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z",
"lobId": "16400",
"data": {},
"templateId": null,
"role": null,
"createdById": null,
"letterheadId": null,
"jobActionAttachmentId": null,
"jobActionId": null,
"oauthClientId": null,
"description": null,
"assistantId": null,
"integrationId": null,
"remoteId": null,
"remoteUrl": null,
"size": 524288,
"indexedAt": "2026-01-15T16:20:00.000Z",
"indexingErroredAt": null,
"indexingErrorMessage": null,
"messageId": null,
"chatId": null
}
This route does not receive the file bytes directly. First stage the bytes through the upload flow to obtain an uploadId, then post that id here. See the Storage API for the staging step.
GET /api/libraries/{id}/files
List files under a directory with filtering, sorting, and pagination.
Authentication: Required
Permissions: Read access to the library. As in files-list, a caller who can't configure the library (anyone but its connection owner, or a local library's owner) sees only files that have finished indexing and the directories holding them, and no indexing counts. An embedded library (an App's or a chat's) shows every file.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
parentId | string | null | Filter by parent directory id |
dir | string | null | Filter by directory path (e.g. /docs); accepts a leading slash or not |
q | string | - | Filter by filename (case-insensitive substring) |
start | integer | 0 | First row to return |
end | integer | 100 | End row (exclusive); returns end - start rows |
sort | array | [{ colId: "filename", sort: "asc" }] | Sort model, sent as a JSON array (?sort=[{"colId":"modifiedAt","sort":"desc"}], URL-encoded); sortable columns: filename, indexed, modifiedAt, indexingStartedAt, indexedAt, remoteSize (the file's size) |
dirOnly | boolean | false | Return only directories |
Response:
A flat array of the immediate children matching the filter (directories sort first). Each row carries path, canIndex (eligible for vector indexing), and runningDescendantsCount (files being indexed at or below the row; a file being indexed counts itself).
[
{
"id": "00000000-0000-4000-8000-000000000001",
"filename": "docs",
"canIndex": true,
"remoteUrl": null,
"directory": true,
"contentType": "false",
"indexed": false,
"modifiedAt": null,
"indexingErroredAt": null,
"indexingErrorMessage": null,
"indexingStartedAt": null,
"indexedAt": null,
"path": "/docs",
"size": null,
"runningDescendantsCount": 0
},
{
"id": "00000000-0000-4000-8000-000000000002",
"filename": "README.md",
"canIndex": true,
"remoteUrl": null,
"directory": false,
"contentType": "text/markdown",
"indexed": false,
"modifiedAt": "2026-01-15T16:20:00.000Z",
"indexingErroredAt": null,
"indexingErrorMessage": null,
"indexingStartedAt": null,
"indexedAt": null,
"path": "/README.md",
"size": 524288,
"runningDescendantsCount": 0
}
]
runningDescendantsCount shows how many files within a directory are currently being indexed. Use it for progress indicators.
GET /api/libraries/{id}/files-list
Get the full, flat list of every file and directory in the library (a single non-paginated query, faster than the recursive listing above for large trees).
Authentication: Required
Permissions: Read access to the library. Non-configurers see only indexed files unless indexedOnly=false and they can configure.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
dirOnly | boolean | false | Return only directories |
indexedOnly | boolean | false | Restrict to indexed files |
Response:
A flat array ordered directories-first, then by filename.
[
{
"id": "00000000-0000-4000-8000-000000000001",
"parentId": null,
"filename": "docs",
"canIndex": true,
"remoteUrl": null,
"directory": true,
"contentType": "false",
"indexed": false,
"modifiedAt": null,
"indexingErroredAt": null,
"indexingErrorMessage": null,
"indexingStartedAt": null,
"indexedAt": null,
"size": null,
"libraryId": "00000000-0000-4000-8000-000000000002"
},
{
"id": "00000000-0000-4000-8000-000000000003",
"parentId": "00000000-0000-4000-8000-000000000001",
"filename": "overview.md",
"canIndex": true,
"remoteUrl": null,
"directory": false,
"contentType": "text/markdown",
"indexed": false,
"modifiedAt": "2026-01-15T16:20:00.000Z",
"indexingErroredAt": null,
"indexingErrorMessage": null,
"indexingStartedAt": null,
"indexedAt": null,
"size": "524288",
"libraryId": "00000000-0000-4000-8000-000000000002"
},
{
"id": "00000000-0000-4000-8000-000000000004",
"parentId": null,
"filename": "README.md",
"canIndex": true,
"remoteUrl": null,
"directory": false,
"contentType": "text/markdown",
"indexed": false,
"modifiedAt": "2026-01-15T16:20:00.000Z",
"indexingErroredAt": null,
"indexingErrorMessage": null,
"indexingStartedAt": null,
"indexedAt": null,
"size": "524288",
"libraryId": "00000000-0000-4000-8000-000000000002"
},
"… 1 more items (4 total) — omitted from docs"
]
PATCH /api/libraries/{id}/files
Bulk-update file metadata using JSON Patch replace operations with glob pattern matching on paths.
Authentication: Required
Permissions: Requires library:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
Request Body:
An array of JSON Patch operations. path is a glob over file paths followed by the property to set (e.g. /docs/*.md/modifiedAt).
| Field | Type | Description |
|---|---|---|
op | string | Only replace is supported |
path | string | Glob pattern over the file path plus the trailing property |
value | any | New value for the property |
Allowed properties: filename, directory, contentType, remoteUrl, modifiedAt, indexingErroredAt, indexingErrorMessage, indexingStartedAt, indexedAt, size
Response:
[
{
"op": "replace",
"path": "/docs/*.md/modifiedAt",
"value": "2026-01-15T16:20:00.000Z"
}
]
{
"_links": {
"self": {
"href": "https://informer.example.com/api/libraries/admin%3Aengineering-docs/files"
}
},
"status": "success"
}
indexed is intentionally not an allowed property here. To bulk update indexed status, use POST /api/libraries/{id}/files/_bulk-index instead.
GET /api/libraries/{id}/files/{fileId}
Get metadata for a specific file.
Authentication: Required
Permissions: Read access to the library
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
fileId | string (UUID) | File id |
Response:
{
"_links": {
"self": {
"href": "https://informer.example.com/api/libraries/admin%3Aengineering-docs/files/00000000-0000-4000-8000-000000000001"
}
},
"extension": "md",
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000001",
"filename": "overview.md",
"embedded": false,
"contentType": "text/markdown",
"lobId": "16400",
"role": null,
"description": null,
"data": {},
"createdById": null,
"letterheadId": null,
"jobActionAttachmentId": null,
"jobActionId": null,
"oauthClientId": null,
"assistantId": null,
"integrationId": null,
"directory": false,
"indexed": false,
"indexingStartedAt": null,
"indexingErroredAt": null,
"indexingErrorMessage": null,
"indexedAt": null,
"parentId": "00000000-0000-4000-8000-000000000002",
"libraryId": "00000000-0000-4000-8000-000000000003",
"remoteId": null,
"remoteUrl": null,
"size": "524288",
"modifiedAt": "2026-01-15T16:20:00.000Z",
"messageId": null,
"chatId": null,
"expiresAt": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"ownerId": null,
"templateId": null
}
PATCH /api/libraries/{id}/files/{fileId}
Edit a file's contents with exact search-and-replace edits.
Authentication: Required
Permissions: Requires library:write. The edit reads the file's current contents on the server, and a synced library reads a file through its connection owner's account, so until a synced file is indexed only that owner (or a member of a team that owns a connection) can edit it; anyone else gets 404.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
fileId | string (UUID) | File id |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
edits | array | Yes | One or more { oldText, newText } pairs |
edits[].oldText | string | Yes | Exact text to find. Must match exactly once in the file |
edits[].newText | string | No | Replacement text (empty string deletes the matched text) |
Example:
{
"edits": [
{ "oldText": "Welcome to the engineering documentation.", "newText": "Welcome to the Order Desk engineering documentation." }
]
}
Response:
Responds with the applied-edit count and a unified diff. Editing a directory returns 400; a library belonging to an App with a staged deploy returns 409.
{
"edits": [
{
"oldText": "Welcome to the engineering documentation.",
"newText": "Welcome to the Order Desk engineering documentation."
}
]
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/libraries/admin%3Aengineering-docs/files/00000000-0000-4000-8000-000000000001"
}
},
"file": {
"id": "00000000-0000-4000-8000-000000000001",
"filename": "overview.md"
},
"editsApplied": 1,
"diff": "Index: overview.md\n===================================================================\n--- overview.md\toriginal\n+++ overview.md\tmodified\n@@ -1,2 +1,2 @@\n # Overview\n-Welcome to the engineering documentation.\n+Welcome to the Order Desk engineering documentation.\n"
}
This route does not accept RFC 6902 JSON Patch. It applies literal search-and-replace edits, which is the contract the AI file tools rely on. To replace a whole file, use PUT .../contents; for path-based incremental edits, use PATCH .../contents/{path*}.
DELETE /api/libraries/{id}/files/{fileId}
Delete a file from the library.
Authentication: Required
Permissions: Requires library:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
fileId | string (UUID) | File id |
Response:
Responds 200 with an empty body, or 409 Conflict when the library belongs to an App with a staged deploy.
Deleting a directory also deletes every file and subdirectory within it.
GET /api/libraries/{id}/files/{fileId}/contents
Get file contents as a stream. Served inline by default; pass ?download=true for a Content-Disposition: attachment download.
Authentication: Required
Permissions: Read access to the library and file
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
fileId | string (UUID) | File id |
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
download | boolean | When true, download instead of inline |
Response:
The raw file bytes with the file's Content-Type. (Binary stream, not shown.)
PUT /api/libraries/{id}/files/{fileId}/contents
Replace a file's contents.
Authentication: Required
Permissions: Requires library:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
fileId | string (UUID) | File id |
Request Body:
The raw new file content (the request body is the file bytes, not a JSON envelope).
Response:
Responds with the updated file. Writing to a directory returns 400; a library belonging to an App with a staged deploy returns 409.
"# Overview\nReplaced contents.\n"
{
"_links": {
"self": {
"href": "https://informer.example.com/api/libraries/admin%3Aengineering-docs/files/00000000-0000-4000-8000-000000000001/contents"
}
},
"extension": "md",
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000001",
"filename": "overview.md",
"embedded": false,
"contentType": "text/markdown",
"lobId": "16400",
"role": null,
"description": null,
"data": {},
"createdById": null,
"letterheadId": null,
"jobActionAttachmentId": null,
"jobActionId": null,
"oauthClientId": null,
"assistantId": null,
"integrationId": null,
"directory": false,
"indexed": false,
"indexingStartedAt": null,
"indexingErroredAt": null,
"indexingErrorMessage": null,
"indexedAt": null,
"parentId": "00000000-0000-4000-8000-000000000002",
"libraryId": "00000000-0000-4000-8000-000000000003",
"remoteId": null,
"remoteUrl": null,
"size": 524288,
"modifiedAt": "2026-01-15T16:20:00.000Z",
"messageId": null,
"chatId": null,
"expiresAt": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"ownerId": null,
"templateId": null
}
GET /api/libraries/{id}/contents/{path*}
Read file contents by path (supports nested paths). This addresses a library file by path instead of id and serves identically to GET .../files/{fileId}/contents.
Authentication: Required
Permissions: Read access to the library and file
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
path | string | File path (e.g. assets/index.js) |
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
download | boolean | When true, download instead of inline |
Response:
The raw file bytes with the file's Content-Type. (Binary stream, not shown.)
Example:
GET /api/libraries/admin:engineering-docs/contents/assets/images/logo.png
PUT /api/libraries/{id}/contents/{path*}
Write file contents by path. Creates the file and any missing parent directories.
Authentication: Required
Permissions: Requires library:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
path | string | File path (e.g. docs/new-file.md) |
Request Body:
JSON, not raw bytes:
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes | File content (may be empty) |
contentType | string | No | MIME type (auto-detected from the filename when omitted) |
encoding | string | No | utf8 (default) or base64 |
Response:
Responds 201 Created when the file is new, 200 OK when it already existed, or 409 Conflict when the library belongs to an App with a staged deploy.
{
"content": "# Overview\nWelcome to the engineering documentation.\n"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/libraries/admin%3Aengineering-docs/contents/docs/overview.md"
}
},
"extension": "md",
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000001",
"embedded": false,
"directory": false,
"libraryId": "00000000-0000-4000-8000-000000000002",
"parentId": "00000000-0000-4000-8000-000000000003",
"filename": "overview.md",
"contentType": "text/markdown",
"modifiedAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z",
"lobId": "16400",
"ownerId": null,
"data": {},
"templateId": null,
"role": null,
"createdById": null,
"letterheadId": null,
"jobActionAttachmentId": null,
"jobActionId": null,
"oauthClientId": null,
"description": null,
"assistantId": null,
"integrationId": null,
"remoteId": null,
"remoteUrl": null,
"size": 524288,
"indexed": false,
"indexingStartedAt": null,
"indexedAt": null,
"indexingErroredAt": null,
"indexingErrorMessage": null,
"messageId": null,
"chatId": null,
"expiresAt": null
}
PATCH /api/libraries/{id}/contents/{path*}
Incrementally update file contents by path.
Authentication: Required
Permissions: Requires library:write. As with PATCH /api/libraries/{id}/files/{fileId}, an unindexed file of a synced library can be patched only by its connection owner; anyone else gets 404.
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
path | string | File path |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
operation | string | Yes | One of replace, append, prepend, insert |
search | string | replace | Text to find (replace) |
replacement | string | No | Replacement text (replace) |
replaceAll | boolean | No | Replace all occurrences instead of the first (replace) |
text | string | append/prepend/insert | Text to add |
insertAt | integer | insert | Character offset for insert |
Example:
{ "operation": "append", "text": "\n## Troubleshooting\nSee the runbook.\n" }
Response:
Responds with the updated file. The file must already exist (404 otherwise); patching a directory returns 400, and a library belonging to an App with a staged deploy returns 409.
{
"operation": "append",
"text": "\n## Troubleshooting\nSee the runbook.\n"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/libraries/admin%3Aengineering-docs/contents/docs/setup.md"
}
},
"extension": "md",
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000001",
"filename": "setup.md",
"embedded": false,
"contentType": "text/markdown",
"lobId": "16400",
"role": null,
"description": null,
"data": {},
"createdById": null,
"letterheadId": null,
"jobActionAttachmentId": null,
"jobActionId": null,
"oauthClientId": null,
"assistantId": null,
"integrationId": null,
"directory": false,
"indexed": false,
"indexingStartedAt": null,
"indexingErroredAt": null,
"indexingErrorMessage": null,
"indexedAt": null,
"parentId": "00000000-0000-4000-8000-000000000002",
"libraryId": "00000000-0000-4000-8000-000000000003",
"remoteId": null,
"remoteUrl": null,
"size": 524288,
"modifiedAt": "2026-01-15T16:20:00.000Z",
"messageId": null,
"chatId": null,
"expiresAt": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"ownerId": null,
"templateId": null
}
POST /api/libraries/{id}/files/_bulk-index
Opt multiple files into or out of vector indexing.
Authentication: Required
Permissions: Requires strict configure access on the library
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
files | array | Yes | File ids (UUIDs) to affect |
indexed | boolean | No | Mark them indexed (true, default) or not |
Example:
{ "files": ["00000000-0000-4000-8000-000000000001"], "indexed": true }
Response:
Opting in schedules the index-files job; database triggers cascade the change through directory subtrees and manage embeddings. Responds 200 with an empty body.
{
"files": [
"00000000-0000-4000-8000-000000000001"
],
"indexed": true
}
GET /api/libraries/{id}/files/{fileId}/indexed
Get the current indexed flag of a file.
Authentication: Required
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
fileId | string (UUID) | File id |
Response:
A bare boolean.
false
PUT /api/libraries/{id}/files/{fileId}/indexed
Opt a file into indexing.
Authentication: Required
Permissions: Requires strict configure access on the library
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
fileId | string (UUID) | File id |
Request Body:
None. The route always sets indexed: true and schedules the index-files job; for a directory the change cascades to its subtree.
Response:
Responds with the updated file.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/libraries/admin%3Aengineering-docs/files/00000000-0000-4000-8000-000000000001/indexed"
}
},
"extension": "md",
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000001",
"filename": "overview.md",
"embedded": false,
"contentType": "text/markdown",
"lobId": "16400",
"role": null,
"description": null,
"data": {},
"createdById": null,
"letterheadId": null,
"jobActionAttachmentId": null,
"jobActionId": null,
"oauthClientId": null,
"assistantId": null,
"integrationId": null,
"directory": false,
"indexed": true,
"indexingStartedAt": null,
"indexingErroredAt": null,
"indexingErrorMessage": null,
"indexedAt": null,
"parentId": "00000000-0000-4000-8000-000000000002",
"libraryId": "00000000-0000-4000-8000-000000000003",
"remoteId": null,
"remoteUrl": null,
"size": "524288",
"modifiedAt": "2026-01-15T16:20:00.000Z",
"messageId": null,
"chatId": null,
"expiresAt": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"ownerId": null,
"templateId": null
}
DELETE /api/libraries/{id}/files/{fileId}/indexed
Opt a file out of indexing and delete its embeddings.
Authentication: Required
Permissions: Requires library:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
fileId | string (UUID) | File id |
Response:
Responds 200 with an empty body. A database trigger clears indexedAt / indexingErroredAt and removes embeddings.
POST /api/libraries/{id}/files/{fileId}/_retry-indexing
Clear a file's indexing error and re-schedule indexing.
Authentication: Required
Permissions: Requires library:write
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Library id or natural id |
fileId | string (UUID) | File id |
Response:
Responds with the updated file.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/libraries/admin%3Aengineering-docs/files/00000000-0000-4000-8000-000000000001/_retry-indexing"
}
},
"extension": "md",
"tenant": "acme",
"id": "00000000-0000-4000-8000-000000000001",
"filename": "overview.md",
"embedded": false,
"contentType": "text/markdown",
"lobId": "16400",
"role": null,
"description": null,
"data": {},
"createdById": null,
"letterheadId": null,
"jobActionAttachmentId": null,
"jobActionId": null,
"oauthClientId": null,
"assistantId": null,
"integrationId": null,
"directory": false,
"indexed": true,
"indexingStartedAt": null,
"indexingErroredAt": null,
"indexingErrorMessage": null,
"indexedAt": "2026-01-15T16:20:00.000Z",
"parentId": "00000000-0000-4000-8000-000000000002",
"libraryId": "00000000-0000-4000-8000-000000000003",
"remoteId": null,
"remoteUrl": null,
"size": "524288",
"modifiedAt": "2026-01-15T16:20:00.000Z",
"messageId": null,
"chatId": null,
"expiresAt": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"ownerId": null,
"templateId": null
}
Use this when indexingErroredAt is set and you want to retry after fixing the underlying issue.