---
**Acting as a user (`X-PS-Impersonate-User`).** Every endpoint accepts an optional `X-PS-Impersonate-User` request header whose value is the email of the instance user to act as. The effective user resolves in this order: the header if present, else the instance's configured default MCP user, else none. User-scoped paths additionally accept the literal `me` in place of an email, and Graph-style apps also serve `/me/…` twins of their `/users/{email}/…` paths that resolve the same way. This header is honored by all operations but is intentionally omitted from each operation's parameter list so it is not surfaced as a per-tool argument by MCP clients; see the `ImpersonateUser` entry under `components.parameters`.
---
**Acting as a user (`X-PS-Impersonate-User`).** Every endpoint accepts an optional `X-PS-Impersonate-User` request header whose value is the email of the instance user to act as. The effective user resolves in this order: the header if present, else the instance's configured default MCP user, else none. User-scoped paths additionally accept the literal `me` in place of an email, and Graph-style apps also serve `/me/…` twins of their `/users/{email}/…` paths that resolve the same way. This header is honored by all operations but is intentionally omitted from each operation's parameter list so it is not surfaced as a per-tool argument by MCP clients; see the `ImpersonateUser` entry under `components.parameters`.
Operations
GET /pages
List wiki pages in the Purple Pages workspace
Purple Pages is the Notion-style knowledge workspace: a Page is a rich-text document (title, emoji, block content, sharing) that can nest under a parent_id. Use this to find documentation, meeting notes or specs; structured tabular data lives in pages__list__databases / pages__list__db_records instead. Supports server-side OData querying — $filter, $search, $orderby, $select, $top, $skip, $count (MCP clients see these as _filter, _search, _orderby, _select, _top, _skip, _count because $ is not a legal tool-input key). Example: $filter=parent_id eq null for top-level pages, or $search=onboarding.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "List wiki pages in the Purple Pages workspace",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "query",
"name": "limit",
"schema": {
"type": "integer",
"default": 50
},
"description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
},
{
"in": "query",
"name": "cursor",
"schema": {
"type": "string"
},
"description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
},
{
"in": "query",
"name": "$filter",
"schema": {
"type": "string"
},
"description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
},
{
"in": "query",
"name": "$select",
"schema": {
"type": "string"
},
"description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
},
{
"in": "query",
"name": "$orderby",
"schema": {
"type": "string"
},
"description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
},
{
"in": "query",
"name": "$search",
"schema": {
"type": "string"
},
"description": "Free-text, case-insensitive substring match across the resource's searchable fields."
},
{
"in": "query",
"name": "$top",
"schema": {
"type": "integer"
},
"description": "Maximum number of items to return (page size)."
},
{
"in": "query",
"name": "$skip",
"schema": {
"type": "integer"
},
"description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
},
{
"in": "query",
"name": "$count",
"schema": {
"type": "boolean"
},
"description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
}
],
"responses": {
"200": {
"description": "Paginated list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Page"
}
},
"nextCursor": {
"type": "string",
"nullable": true,
"description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
},
"total": {
"type": "integer",
"description": "Total documents in the collection."
},
"@odata.count": {
"type": "integer",
"description": "Present only when $count=true: total matches after $filter/$search."
},
"@odata.truncated": {
"type": "boolean",
"description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
}
}
}
}
}
},
"400": {
"description": "Invalid OData query (e.g. malformed $filter)"
}
},
"description": "Purple Pages is the Notion-style knowledge workspace: a Page is a rich-text document (title, emoji, block content, sharing) that can nest under a parent_id. Use this to find documentation, meeting notes or specs; structured tabular data lives in pages__list__databases / pages__list__db_records instead. Supports server-side OData querying — $filter, $search, $orderby, $select, $top, $skip, $count (MCP clients see these as _filter, _search, _orderby, _select, _top, _skip, _count because $ is not a legal tool-input key). Example: $filter=parent_id eq null for top-level pages, or $search=onboarding."
}
POST /pages
Create a wiki page
Creates a new document in the Purple Pages workspace with a title and optional emoji, block content, parent_id (to nest it under another page) and sharing settings. Create a Database instead if the user wants rows and columns rather than prose.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Create a wiki page",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Page"
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Page"
}
}
}
}
},
"description": "Creates a new document in the Purple Pages workspace with a title and optional emoji, block content, parent_id (to nest it under another page) and sharing settings. Create a Database instead if the user wants rows and columns rather than prose."
}
GET /pages/{id}
Get one wiki page with its content
Fetches a single Page by id, including its full block content array — this is how you read what a page actually says. List operations return the same shape, so use this when you already have the page id.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Get one wiki page with its content",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Page"
}
}
}
},
"404": {
"description": "Not found"
}
},
"description": "Fetches a single Page by id, including its full block content array — this is how you read what a page actually says. List operations return the same shape, so use this when you already have the page id."
}
PATCH /pages/{id}
Edit a wiki page's title, content or sharing
Partially updates a Page: title, emoji, block content, lastEditedAt, shared/sharedWith and permissions. This is the tool for editing what a page SAYS. To change where a page sits in the hierarchy, use pages__move__pages rather than patching parent_id.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Edit a wiki page's title, content or sharing",
"description": "Partially updates a Page: title, emoji, block content, lastEditedAt, shared/sharedWith and permissions. This is the tool for editing what a page SAYS. To change where a page sits in the hierarchy, use pages__move__pages rather than patching parent_id.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"description": "Fields to update (partial)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Page"
}
}
}
},
"responses": {
"200": {
"description": "Updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Page"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
DELETE /pages/{id}
Delete a wiki page
DESTRUCTIVE and irreversible: permanently deletes the page and its content (there is no trash or undo). Child pages that reference it as parent_id are orphaned and its comments are left dangling. Confirm with the user before calling.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Delete a wiki page",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Deleted",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"description": "Not found"
}
},
"description": "DESTRUCTIVE and irreversible: permanently deletes the page and its content (there is no trash or undo). Child pages that reference it as parent_id are orphaned and its comments are left dangling. Confirm with the user before calling."
}
GET /databases
List structured databases in Purple Pages
A Database is a Notion-style structured table in the workspace (name, viewType table/board/gallery/list/calendar, and a schema listing its column names) — for example a Project Tracker or Vendor Directory. This returns the table definitions only; the rows inside them come from pages__list__db_records. Supports server-side OData querying — $filter, $search, $orderby, $select, $top, $skip, $count (MCP clients see these as _filter, _search, _orderby, _select, _top, _skip, _count because $ is not a legal tool-input key).
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "List structured databases in Purple Pages",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "query",
"name": "limit",
"schema": {
"type": "integer",
"default": 50
},
"description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
},
{
"in": "query",
"name": "cursor",
"schema": {
"type": "string"
},
"description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
},
{
"in": "query",
"name": "$filter",
"schema": {
"type": "string"
},
"description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
},
{
"in": "query",
"name": "$select",
"schema": {
"type": "string"
},
"description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
},
{
"in": "query",
"name": "$orderby",
"schema": {
"type": "string"
},
"description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
},
{
"in": "query",
"name": "$search",
"schema": {
"type": "string"
},
"description": "Free-text, case-insensitive substring match across the resource's searchable fields."
},
{
"in": "query",
"name": "$top",
"schema": {
"type": "integer"
},
"description": "Maximum number of items to return (page size)."
},
{
"in": "query",
"name": "$skip",
"schema": {
"type": "integer"
},
"description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
},
{
"in": "query",
"name": "$count",
"schema": {
"type": "boolean"
},
"description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
}
],
"responses": {
"200": {
"description": "Paginated list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Database"
}
},
"nextCursor": {
"type": "string",
"nullable": true,
"description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
},
"total": {
"type": "integer",
"description": "Total documents in the collection."
},
"@odata.count": {
"type": "integer",
"description": "Present only when $count=true: total matches after $filter/$search."
},
"@odata.truncated": {
"type": "boolean",
"description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
}
}
}
}
}
},
"400": {
"description": "Invalid OData query (e.g. malformed $filter)"
}
},
"description": "A Database is a Notion-style structured table in the workspace (name, viewType table/board/gallery/list/calendar, and a schema listing its column names) — for example a Project Tracker or Vendor Directory. This returns the table definitions only; the rows inside them come from pages__list__db_records. Supports server-side OData querying — $filter, $search, $orderby, $select, $top, $skip, $count (MCP clients see these as _filter, _search, _orderby, _select, _top, _skip, _count because $ is not a legal tool-input key)."
}
POST /databases
Create a structured database
Creates a new Notion-style table in the workspace: name, viewType and a schema array of column names. Creates the container only — add rows afterwards with pages__create__db_records.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Create a structured database",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Database"
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Database"
}
}
}
}
},
"description": "Creates a new Notion-style table in the workspace: name, viewType and a schema array of column names. Creates the container only — add rows afterwards with pages__create__db_records."
}
GET /databases/{id}
Get one database definition
Fetches a single Database by id with its schema (column names), viewType and sharing. Call this before writing records so the field names you send match the table's schema.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Get one database definition",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Database"
}
}
}
},
"404": {
"description": "Not found"
}
},
"description": "Fetches a single Database by id with its schema (column names), viewType and sharing. Call this before writing records so the field names you send match the table's schema."
}
PATCH /databases/{id}
Update a database's name, view or schema
Partially updates the table definition (name, viewType, schema columns, sharing). Editing the schema does not rewrite existing rows — change row values with pages__update__db_records.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Update a database's name, view or schema",
"description": "Partially updates the table definition (name, viewType, schema columns, sharing). Editing the schema does not rewrite existing rows — change row values with pages__update__db_records.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"description": "Fields to update (partial)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Database"
}
}
}
},
"responses": {
"200": {
"description": "Updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Database"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
DELETE /databases/{id}
Delete a structured database
DESTRUCTIVE and irreversible: removes the table definition. Its records are not deleted with it and become orphaned rows pointing at a missing databaseId, so confirm with the user first.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Delete a structured database",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Deleted",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"description": "Not found"
}
},
"description": "DESTRUCTIVE and irreversible: removes the table definition. Its records are not deleted with it and become orphaned rows pointing at a missing databaseId, so confirm with the user first."
}
GET /db-records
List rows inside Purple Pages databases
Returns the actual rows of the workspace's Notion-style tables — each DbRecord belongs to a databaseId and carries that table's user-defined fields (title, status, owner, priority, severity, dueDate, …). Almost always scope it with the databaseId query param, or you get rows from every table at once. Supports server-side OData querying — $filter, $search, $orderby, $select, $top, $skip, $count (MCP clients see these as _filter, _search, _orderby, _select, _top, _skip, _count because $ is not a legal tool-input key). Example: databaseId=<id> with $filter=status eq 'In Progress'.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "List rows inside Purple Pages databases",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "query",
"name": "limit",
"schema": {
"type": "integer",
"default": 50
},
"description": "Legacy page size (default 50). Prefer $top. Ignored when $top/$skip are used."
},
{
"in": "query",
"name": "cursor",
"schema": {
"type": "string"
},
"description": "Legacy keyset cursor — pass the previous response's nextCursor. Prefer $skip/$top."
},
{
"in": "query",
"name": "$filter",
"schema": {
"type": "string"
},
"description": "OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."
},
{
"in": "query",
"name": "$select",
"schema": {
"type": "string"
},
"description": "Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."
},
{
"in": "query",
"name": "$orderby",
"schema": {
"type": "string"
},
"description": "Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."
},
{
"in": "query",
"name": "$search",
"schema": {
"type": "string"
},
"description": "Free-text, case-insensitive substring match across the resource's searchable fields."
},
{
"in": "query",
"name": "$top",
"schema": {
"type": "integer"
},
"description": "Maximum number of items to return (page size)."
},
{
"in": "query",
"name": "$skip",
"schema": {
"type": "integer"
},
"description": "Number of matching items to skip before returning results (offset paging). Combine with $top."
},
{
"in": "query",
"name": "$count",
"schema": {
"type": "boolean"
},
"description": "When true, include @odata.count (total matches after $filter/$search, before paging) in the response."
},
{
"in": "query",
"name": "databaseId",
"required": false,
"schema": {
"type": "string"
},
"description": "Filter records by parent database ID. When omitted all records across all databases are returned.",
"example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
],
"responses": {
"200": {
"description": "Paginated list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/DbRecord"
}
},
"nextCursor": {
"type": "string",
"nullable": true,
"description": "Cursor for the next page, or null on the last page. With $top/$skip this is the next $skip value; otherwise a keyset cursor."
},
"total": {
"type": "integer",
"description": "Total documents in the collection."
},
"@odata.count": {
"type": "integer",
"description": "Present only when $count=true: total matches after $filter/$search."
},
"@odata.truncated": {
"type": "boolean",
"description": "Present and true only when the collection exceeded the in-memory query cap and results may be incomplete."
}
}
}
}
}
},
"400": {
"description": "Invalid OData query (e.g. malformed $filter)"
}
},
"description": "Returns the actual rows of the workspace's Notion-style tables — each DbRecord belongs to a databaseId and carries that table's user-defined fields (title, status, owner, priority, severity, dueDate, …). Almost always scope it with the databaseId query param, or you get rows from every table at once. Supports server-side OData querying — $filter, $search, $orderby, $select, $top, $skip, $count (MCP clients see these as _filter, _search, _orderby, _select, _top, _skip, _count because $ is not a legal tool-input key). Example: databaseId=<id> with $filter=status eq 'In Progress'."
}
POST /db-records
Add a row to a Purple Pages database
Creates a record inside an existing database — set databaseId to the parent table and supply fields matching that table's schema (check it with pages__get__databases first). Use this for tracker rows; use pages__create__pages for a prose document.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Add a row to a Purple Pages database",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DbRecord"
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DbRecord"
}
}
}
}
},
"description": "Creates a record inside an existing database — set databaseId to the parent table and supply fields matching that table's schema (check it with pages__get__databases first). Use this for tracker rows; use pages__create__pages for a prose document."
}
GET /db-records/{id}
Get one database row
Fetches a single DbRecord by id with all of its field values. Use it after locating the row with pages__list__db_records.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Get one database row",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DbRecord"
}
}
}
},
"404": {
"description": "Not found"
}
},
"description": "Fetches a single DbRecord by id with all of its field values. Use it after locating the row with pages__list__db_records."
}
PATCH /db-records/{id}
Update fields on a database row
Partially updates one row in a Purple Pages database — this is how you change a tracker item's status, owner, priority or due date. Send only the fields you want to change; unspecified fields are preserved.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Update fields on a database row",
"description": "Partially updates one row in a Purple Pages database — this is how you change a tracker item's status, owner, priority or due date. Send only the fields you want to change; unspecified fields are preserved.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"description": "Fields to update (partial)",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DbRecord"
}
}
}
},
"responses": {
"200": {
"description": "Updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DbRecord"
}
}
}
},
"404": {
"description": "Not found"
}
}
}
DELETE /db-records/{id}
Delete a database row
DESTRUCTIVE and irreversible: permanently removes the row from its database, with no trash or undo. If the intent is to mark work finished, patch its status field instead.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Delete a database row",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Deleted",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"description": "Not found"
}
},
"description": "DESTRUCTIVE and irreversible: permanently removes the row from its database, with no trash or undo. If the intent is to mark work finished, patch its status field instead."
}
POST /pages/{id}/move
Move a page under a new parent
Changes only WHERE a page sits in the Purple Pages hierarchy — reparents it by setting parent_id (pass null to move it to the top level). Use this for 'move X under Y' or 'move X to the top level'; use pages__update__pages to change the page's title, content or sharing. The page's content and comments are unaffected.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Move a page under a new parent",
"description": "Changes only WHERE a page sits in the Purple Pages hierarchy — reparents it by setting parent_id (pass null to move it to the top level). Use this for 'move X under Y' or 'move X to the top level'; use pages__update__pages to change the page's title, content or sharing. The page's content and comments are unaffected.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
},
"example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"parent_id": {
"type": "string",
"nullable": true,
"example": "notion_page_agent_studio_corner",
"description": "ID of the new parent page, or null to move to the top level"
}
},
"required": [
"parent_id"
]
}
}
}
},
"responses": {
"200": {
"description": "Updated page after move",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Page"
}
}
}
},
"400": {
"description": "parent_id is required"
},
"404": {
"description": "Page not found"
}
}
}
POST /pages/{id}/duplicate
Duplicate a page
Creates a brand-new page that copies the source page's content, emoji and other fields, titled "<title> (Copy)", and returns it with its new id. Handy for using an existing page as a template; the original is left untouched. Comments on the source page are not copied.
Authentication
[
{
"patAuth": []
}
]
Request, responses and operation details
{
"summary": "Duplicate a page",
"description": "Creates a brand-new page that copies the source page's content, emoji and other fields, titled \"<title> (Copy)\", and returns it with its new id. Handy for using an existing page as a template; the original is left untouched. Comments on the source page are not copied.",
"parameters": [
{
"$ref": "#/components/parameters/InstanceId"
},
{
"in": "path",
"name": "id",
"required": true,
"schema": {
"type": "string"
},
"example": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
],
"responses": {
"201": {
"description": "The newly created duplicate page",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Page"
}
}
}
},
"404": {
"description": "Page not found"
}
}
}