---
title: "Restore several deleted records"
method: POST
path: "/knowledgeBase/records/restore"
tags: ["Knowledge Base"]
---

# Restore several deleted records

`POST /knowledgeBase/records/restore`

## Request

- Base URL: `https://app.pipeshub.com/api/v1`
- URL: `https://app.pipeshub.com/api/v1/knowledgeBase/records/restore`
- Auth: one of:
  - HTTP bearer
  - OAuth 2
    - Authorization code: `/api/v1/oauth2/authorize`
    - Client credentials
    - Token URL: `/api/v1/oauth2/token` scopes: `kb:delete`

Restore up to 100 records from the trash. Each id is restored as
`POST /knowledgeBase/record/{recordId}/restore` restores it, with everything
deleted along with it; ids from one delete are restored once. Every id gets its
own outcome in `results`, so one refusal does not hold the others back, and the
answer is 200 whatever the outcomes: with the trash turned off in Labs every id is
refused with `code: 403` inside a 200. An id listed twice is answered once. Body
fields other than `recordIds` are ignored.

## Request body

- RestoreRecordsRequestBody: Request body for POST /knowledgeBase/records/restore. Fields other than `recordIds` are dropped by the validator, not refused.
  - `recordIds` string[], required: Record ids to restore. An id listed more than once is restored, and answered, once.

## Response `200`

Outcome for each requested record

- RestoreRecordsResponseSchema: Response returned by POST /knowledgeBase/records/restore.
  - `success` boolean, required: True when every requested record was restored
  - `restoredCount` integer, required: Records restored, counting those restored along with them
  - `failedCount` integer, required
  - `results` RestoreRecordsResultItemSchema[], required: One entry per requested id: `recordId`, `success`, `code`, and either the restore response fields or a `reason` that says what to do next.
    - `recordId` string, required
    - `success` boolean, required
    - `code` integer: The status the single restore would have answered (200, 403, 404, 409)
    - `reason` string: Why it was not restored, and what to do next
    - `message` string: Present on success
    - `batchId` string, nullable: The delete batch that was restored; `null` when the record was not in the trash
    - `restoredRecords` RestoredRecordSchema[]: Present on success; empty when the record was not in the trash
      - `recordId` string, required
      - `name` string, nullable: The name it has now
      - `renamedFrom` string: Its name before restore, when another item next to it had taken that name
    - `reindexPending` boolean: Some restored files could not be queued for indexing
    - `reindexPendingRecordIds` string[]: Files to reindex by hand to make them searchable again
    - `reindexPendingReason` string
    - `renamePendingRecordIds` string[]: Items restored under a name another item next to them has; rename them
    - `renamePendingReason` string
    - `blockedRecordId` string: The item a refusal is about
    - `external_id` string: The source item two of the restored items came from
    - `conflicting_record_id` string: The live item that holds this item's place
    - `conflicting_record_name` string, nullable
    - `missing` string[]: Items of the batch that were no longer in the trash
    - `parentId` string, nullable: The folder still in the trash that must be restored first

## Other responses

- `400`: recordIds is missing, not a list, empty, or longer than 100, or one of the ids is not a string or is empty after trimming. Send between 1 and 100 record ids. Also returned, before authentication, when a query value, or any string in a JSON or form-encoded body, looks like markup to the app-wide XSS filter: an HTML tag, a script tag, `<iframe`, `<object`, `<embed` or `<svg` tag, a word starting with `on` followed by `=` (meant for inline event handlers such as `onerror=`, it also catches plain text such as `one=1`), a `javascript:` or `data:text/html` URL, an encoded script tag (`&lt;script`, `%3cscript`), or any string longer than 100,000 characters (`HTTP_BAD_REQUEST`, "HTML tags, scripts, and XSS content are not allowed in ...", naming the field or query parameter; a string over 100,000 characters is refused as too long instead). A JSON body that does not parse is refused the same way, also before authentication (`HTTP_BAD_REQUEST`, "The request body is not valid JSON.").
- `401`: Missing, invalid, expired, or revoked authentication
- `403`: OAuth token is missing the required kb:delete scope
- `409`: The knowledge base is being deleted (`error.code` is `HTTP_KB_BEING_DELETED`): it can no longer be changed. Reads still work and show `status: DELETING`; retry the delete instead.
- `500`: Internal server error while restoring records. A JSON body larger than 10 MB, or a form-encoded body larger than 100 KB or with more than 1000 fields, is answered `413` by the app-wide body parsers instead, also before authentication.
- `503`: Connector service unavailable or connection refused

## Changes

- **2026-10-28** `dbb5e203b445`: 1 breaking, 1 info
  - added the pattern `\S` to the request property `recordIds/items/`
  - added the non-success response with the status `409`
- **2026-10-13** `bb0c59adf026`: 1 info
  - endpoint added
- **2026-10-08** `c462bea9ad26`: 1 breaking
  - api path removed without deprecation
- **2026-10-08** `bb0c59adf026`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/pipeshub-ai/apis/pipeshub-api/changes/knowledgeBase/records/restore/post.md)

---

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