POST /api/node/restore
Restore a node from the trash. Equivalent to node:restore in WebSocket.
Visibility & Permissions
- User must have editor access to the node.
- If ACL check fails →
forbiddenerror.
- If ACL check fails →
- Node must already be in trash.
- If not trashed →
not_trashed_nodeerror.
- If not trashed →
Request
Endpoint
POST /api/node/restore
Payload (JSON)
{
"id": "<uuid>" // required, node id
}
Success (200)
{
"ok": true,
"diff": {
"batchId": "uuid",
"actor": {
"username": "alice",
"email": "alice@example.com"
},
"ts": "2025-09-25T11:00:00Z",
"nodes": [
{
"op": 4, // NODE_RESTORE=4
"before": {
"id": "uuid",
"inTrash": true,
"version": 4
},
"after": { // Hidrated
"id": "uuid",
"title": "string",
"description": "string",
"status": 0,
"dueDate": "2025-09-13T10:00:00Z",
"tags": ["backend", "urgent"],
"priority": 5,
"independent": false,
"volume": 5,
"type": 0,
"assignee": [],
"inTrash": false,
"version": 5,
"publicToken": "string",
"publicDue": "2026-09-13T10:00:00Z",
"ownerUsername": "BLACK",
"ownerEmail": "black@synaptask.space",
"createdTime": "2025-09-13T10:00:00Z",
"lastEditedTime": "2025-09-25T11:00:00Z",
"x": 0.0,
"y": 0.0,
"z": 0.0,
"access": 0,
"pinned": false,
"collapsed": false,
"shareRoots": ["uuid1", "uuid2"]
}
},
{ // side effect
"op": 2, // NODE_STATUS=2
"before": {
"id": "uuid",
"Status": 1, // Node status Available=1
"version": 4
},
"after": {
"id": "uuid",
"Status": 2, // Node status Blocked=2
"version": 5,
"lastEditedTime": "2025-09-25T11:00:00Z",
"shareRoots": ["uuid1", "uuid2"]
}
}
],
"links": [
{
"op": 13, // History operation type LINK_RESTORE=13
"before": {
"id": "uuid",
"inTrash": true,
"version": 4
},
"after": {
"id": "uuid",
"source": "node-uuid-A",
"target": "node-uuid-B",
"type": 0,
"wasBlocker": true,
"private": true,
"version": 1,
"shareRoots": ["root-uuid-1", "root-uuid-2"],
"createdTime": "2025-09-13T10:00:00Z",
"lastEditedTime": "2025-09-13T10:00:00Z",
"lastBatchID": "batch-uuid",
"version": 5,
"inTrash": false,
"shareRoots": ["root-uuid-1", "root-uuid-2"],
"lastEditedTime": "2025-09-13T10:00:00Z"
}
},
{ // side effect
"op": 13, // History operation type LINK_RESTORE=13
"before": {
"id": "uuid2",
"inTrash": true,
"version": 8
},
"after": {
"id": "uuid",
"source": "node-uuid-A",
"target": "node-uuid-B",
"type": 0,
"wasBlocker": true,
"private": true,
"version": 1,
"shareRoots": ["root-uuid-1", "root-uuid-2"],
"createdTime": "2025-09-13T10:00:00Z",
"lastEditedTime": "2025-09-13T10:00:00Z",
"lastBatchID": "batch-uuid",
"version": 9,
"inTrash": false,
"shareRoots": ["root-uuid-1", "root-uuid-2"],
"lastEditedTime": "2025-09-13T10:00:00Z",
}
}
],
"user": [],
"access": [],
}
}
info
The diff contains the restored node and any side effects
(e.g. links automatically restored when parent node is restored).
Consumers must merge diffs by id+version, not overwrite blindly.
Error Responses
{
"ok": false,
"error": "bad_request" | "forbidden" | "not_found" | "conflict" | "internal.exception",
"message": "<optional human-readable>"
}
Errors: see error codes
Examples
JavaScript (fetch)
async function restoreNode(nodeId) {
const resp = await fetch("https://synaptask.space/api/node/restore", {
method: "POST",
headers: {
"Authorization": "Bearer <API_TOKEN>",
"Content-Type": "application/json"
},
body: JSON.stringify({ id: nodeId })
});
const data = await resp.json();
if (data.ok) {
console.log("Node restored:", data.diff);
} else {
console.error("Restore failed:", data.error, data.message);
}
}
restoreNode("f81d4fae-7dec-11d0-a765-00a0c91e6bf6");
Python (requests)
import requests
url = "https://synaptask.space/api/node/restore"
headers = {"Authorization": "Bearer <API_TOKEN>"}
payload = {"id": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"}
resp = requests.post(url, json=payload, headers=headers)
data = resp.json()
if data.get("ok"):
print("Node restored:", data["diff"])
else:
print("Failed:", data["error"], data.get("message"))
See also Node concept and Trash concept