> ## Documentation Index
> Fetch the complete documentation index at: https://developers.notion.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pages

> Create, read, update, and archive Notion pages from a custom block.

<Warning>
  Custom blocks are in private alpha:

  * Do not discuss publicly or share with anyone outside your workspace.
  * Access must be enabled per workspace. Installing the SDK does not grant access.
  * These APIs may experience breaking changes without notice.
</Warning>

The `pages` API lets a block write to Notion. It works with or without React.

```typescript theme={null}
import { pages } from "@notionhq/custom-blocks"
```

Every method resolves to a discriminated union rather than throwing:

```typescript theme={null}
const result = await pages.get(pageId)

if (result.status === "error") {
	// result.error.code, result.error.message, result.error.isRetryable
	return
}

// result.page
```

## Create a page

```typescript theme={null}
const result = await pages.create({
	parent: { type: "data_source_key", key: "issues" },
	properties: {
		title: { type: "title", title: [{ text: { content: "Fix the login bug" } }] },
		status: { type: "status", status: { name: "To do" } },
	},
})
```

| Field | Description |
| - | - |
| `parent` | Required. Where the page goes. |
| `properties` | Required. Property values keyed by either a declared property key or a raw property ID. |
| `position` | Optional. Where the page lands relative to siblings. |

### Choosing a parent

`pages.create()` accepts these parent inputs. These inputs are separate from the `NotionParent` values returned in app context and page responses.

| Parent input | Use when |
| - | - |
| `{ type: "data_source_key", key }` | Creating a page in a data source declared and bound by the block. |
| `{ type: "data_source_id", data_source_id }` | Creating a page in a specific data source. |
| `{ type: "page_id", page_id }` | Creating a child page under a regular page or a wiki root. |

Prefer `data_source_key` when the block uses one of its declared data sources. This keeps the block independent of the user's data source ID.

The returned `result.page.parent` uses the same parent shapes as app context. See [App context](/custom-blocks/sdk/app-context) for the complete list of parent values.

### Position

`position` controls where the new page is inserted:

* `start` and `end` insert the page directly under the page surface.
* `before` and `after` insert the page beside the anchor block.
* The anchor can be nested inside a toggle, column, or another block.
* If the anchor is inside a toggle or column, the new page is also inserted inside that block.
* The `page_id` must identify a regular page or a wiki root.

<Note>
  `icon` and `cover` cannot be set at creation time. Create the page, then set
  them with `pages.update()`.
</Note>

## Read a page

`pages.get()` returns page metadata and properties. It does not return block or Markdown content.

```typescript theme={null}
const result = await pages.get(pageId)
```

`pages.get()` and `pages.update()` accept a regular page ID or a wiki root ID.

On success, `result.page` is a `NotionPage`: its `id`, `parent`, `properties`, and — when available — `created_time`, `last_edited_time`, `icon`, `cover`, `url`, `public_url`, and `in_trash`.

### Read archive status

Archiving keeps content you no longer actively use without deleting it. [Learn about Archive](https://www.notion.com/help/archive-pages).

Read `is_archived` on a page or data source row. It is `true` when the page or an ancestor is archived.

### Read Trash status

Pages in Trash are scheduled for permanent deletion. By default, Notion permanently deletes them 30 days after they enter Trash, unless you restore them first. Enterprise workspaces can configure a different retention period. [Learn about Trash retention](https://www.notion.com/help/custom-data-retention-settings).

Read `in_trash` on a page or data source row. It is `true` when the page or an ancestor is in Trash.

## Update a page

Unlike `pages.create`, `properties` here is keyed by **raw property ID only** — declared keys are not resolved for you. Map them yourself with `propertyIdsByKey` from [`useDataSource`](/custom-blocks/sdk/data-sources):

```typescript theme={null}
const statusPropertyId = propertyIdsByKey.status
if (!statusPropertyId) return

const result = await pages.update({
	pageId,
	properties: {
		[statusPropertyId]: {
			id: statusPropertyId,
			type: "status",
			status: { name: "Done" },
		},
	},
	icon: { type: "emoji", emoji: "✅" },
})
```

| Field | Description |
| - | - |
| `pageId` | Required. |
| `properties` | This field is optional. Use raw property IDs as keys. Repeat each key in the value’s `id` field. The API changes only the supplied properties. |
| `icon` | Optional. |
| `cover` | Optional. |
| `is_archived` | Optional. Set `true` to archive, `false` to unarchive. |

For a row in one of your block's bound data sources, prefer that row's own `update` method from [`useDataSource`](/custom-blocks/sdk/data-sources). It takes your declared keys and resolves them against the binding, so you avoid the ID mapping entirely.

## Archive and unarchive a page

Use `pages.archive(pageId)` to archive a page. Use `pages.unarchive(pageId)` to remove its archive state.

```typescript theme={null}
import { pages } from "@notionhq/custom-blocks"

const result = await pages.archive(pageId)
if (result.status === "error") {
	console.error(result.error.message)
}

const restored = await pages.unarchive(pageId)
if (restored.status === "error") {
	console.error(restored.error.message)
}
```

`pages.archive` is shorthand for calling `pages.update` with `is_archived: true`.
`pages.unarchive` is shorthand for calling `pages.update` with `is_archived: false`.
Both methods return an `UpdatePageResult`.

## Trashing and untrashing pages

This is not currently supported by the custom blocks SDK.

## Property support

This table shows property support for custom blocks running in Notion.

* **Read value** shows `page.properties` in responses from `pages.get`, `pages.create`, and `pages.update`. The table identifies top-level fields separately. Unavailable values are absent from `page.properties`.
* **Create** covers property writes through `pages.create`.
* **Update** covers property writes through `pages.update`.

Pages outside a data source support only `title`. Other property writes require a data source row and a property in its schema.

* 🟢 **Supported:** The SDK supports this operation in Notion.
* 🟡 **Limited:** The SDK supports this operation with the restrictions shown in the cell.
* 🔴 **Unavailable:** The SDK does not expose this operation.
* ⚫ **Not supported in Notion:** Notion itself does not support this operation in the stated case.

| Property type | Read value (`pages.get`) | Create (`pages.create`) | Update (`pages.update`) |
| - | - | - | - |
| `title` | 🟡 Text array with plain text only | 🟡 Plain text only | 🟡 Plain text only |
| `rich_text` | 🟡 Text array with plain text only | 🟡 Plain text only | 🟡 Plain text only |
| `number` | 🟢 Number or `null` | 🟢 Supported | 🟢 Supported |
| `checkbox` | 🟢 Boolean | 🟢 Supported | 🟢 Supported |
| `url` | 🟢 String or `null` | 🟢 Supported | 🟢 Supported |
| `email` | 🟢 String or `null` | 🟢 Supported | 🟢 Supported |
| `phone_number` | 🟢 String or `null` | 🟢 Supported | 🟢 Supported |
| `select` | 🟡 Does not return available options | 🟡 Set value only | 🟡 Set value only |
| `multi_select` | 🟡 Does not return available options | 🟡 Set value only | 🟡 Set value only |
| `status` | 🟡 Does not return available options | 🟡 Set value only | 🟡 Set value only |
| `date` | 🟢 `start` and optional `end` strings, or `null` | 🟡 Minute precision | 🟡 Minute precision |
| `people` | 🟢 Array of user/group ID objects | 🟢 Supported | 🟢 Supported |
| `files` | 🟡 External file links only | 🟡 File URLs only | 🟡 File URLs only |
| `unique_id` | 🔴 Unavailable | 🔴 Unavailable | 🔴 Unavailable |
| `relation` | 🟢 Array of page ID objects | 🟡 Replaces value. Rejects `has_more: true`. | 🟡 Replaces value. Rejects `has_more: true`. |
| `place` | 🟢 Coordinates and optional place details, or `null` | 🟢 Supported | 🟢 Supported |
| `formula` | 🔴 Unavailable | 🔴 Unavailable | 🔴 Unavailable |
| `rollup` | 🔴 Unavailable | 🔴 Unavailable | 🔴 Unavailable |
| `button` | 🔴 Unavailable | 🔴 Unavailable | 🔴 Unavailable |
| `verification` | 🔴 Unavailable | 🔴 Unavailable | 🔴 Unavailable |
| `last_visited_time` | 🔴 Unavailable | 🔴 Unavailable | 🔴 Unavailable |
| `location` | 🔴 Unavailable | 🔴 Unavailable | 🔴 Unavailable |
| `created_time` | 🟡 Top-level ISO timestamp only | 🔴 Unavailable | 🔴 Unavailable |
| `last_edited_time` | 🟡 Top-level ISO timestamp only | 🔴 Unavailable | 🔴 Unavailable |
| `created_by` | 🔴 Unavailable | 🔴 Unavailable | 🔴 Unavailable |
| `last_edited_by` | 🔴 Unavailable | 🔴 Unavailable | 🔴 Unavailable |

### Notes

* Select, multi-select, and status reads return option names and colors. They also return IDs when the options match the schema. Writes accept an existing option ID or a name and set the selected value. They do not edit option definitions. If you supply both an ID and a name, the host uses the ID. The host rejects unknown IDs.
* Date-time writes preserve hours and minutes but discard seconds and milliseconds.
* Title and rich text use text arrays, but reads and writes preserve only plain text. Reads and writes do not preserve formatting, links, mentions, or equations as structured rich text.
* File reads include link items only. File writes accept `external` URLs and existing `file` URLs and store them as links. Upload references (`file_upload`) are unsupported.
* Relation writes replace the value and reject `has_more: true`.
* `created_time` and `last_edited_time` are read-only top-level fields on `page`, when available. The host omits them from `page.properties`. The host does not return `created_by` or `last_edited_by`, even as top-level fields. The host does not return structured page values for formula, rollup, unique ID, or the other omitted types.

For flattened query values, filters, and sorts, see [Data source property support](/custom-blocks/sdk/data-sources#property-support).

## Permissions

A block acts within what the viewing user can already see and edit. A write the user could not perform themselves fails with an access error rather than succeeding with elevated permissions, so treat every result as something that can legitimately fail per user.
