# Data sources, templates and moves

Databases with several data sources, templates, two-way relations, long property values and page moves between parents.

A Notion database holds one or more data sources, and each data source holds the rows, schema, templates and relations a test works with. This page covers the routes that move pages and rows between them. The block below loads the scenarios that hold each case:

```bash
curl -s -X POST "$RYSTIC_URL/_rystic/scenario" -d '{"name":"notion_template_board"}'
curl -s "$RYSTIC_URL/v1/data_sources/92000000-0000-4000-8000-000000000301/templates" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' | jq -c '.templates | map({name, is_default})'
# -> [{"name":"Bug Report","is_default":true},{"name":"Bug Triage Checklist","is_default":false},{"name":"Feature Request","is_default":false},{"name":"Urgent Outage","is_default":false},{"name":"Customer Call Notes","is_default":false}]
curl -s "$RYSTIC_URL/v1/pages" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' -H 'Content-Type: application/json' -d '{"parent":{"data_source_id":"92000000-0000-4000-8000-000000000301"},"template":{"type":"default"},"properties":{"Name":{"title":[{"text":{"content":"Checkout fails"}}]}}}' | jq -c '{id, parent: .parent.type}'
# -> {"id":"73d01b77-cb6c-4662-8000-000000000001","parent":"data_source_id"}
curl -s "$RYSTIC_URL/v1/pages" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' -H 'Content-Type: application/json' -d '{"parent":{"data_source_id":"92000000-0000-4000-8000-000000000302"},"template":{"type":"default"},"properties":{"Name":{"title":[{"text":{"content":"v1.2"}}]}}}' | jq -c '{status, code}'
# -> {"status":400,"code":"validation_error"}
curl -s -X POST "$RYSTIC_URL/_rystic/scenario" -d '{"name":"notion_page_tree"}'
curl -s "$RYSTIC_URL/v1/pages/91000000-0000-4000-8000-000000000106/move" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' -H 'Content-Type: application/json' -d '{"parent":{"type":"page_id","page_id":"91000000-0000-4000-8000-000000000103"}}' | jq -c '{id, parent}'
# -> {"id":"91000000-0000-4000-8000-000000000106","parent":{"page_id":"91000000-0000-4000-8000-000000000103","type":"page_id"}}
curl -s "$RYSTIC_URL/v1/blocks/91000000-0000-4000-8000-000000000103/children" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' | jq -c '.results | map(.child_page.title)'
# -> ["Deploy Checklist","Old Postmortems"]
curl -s "$RYSTIC_URL/v1/pages/91000000-0000-4000-8000-000000000103/move" -H 'Authorization: Bearer secret_placeholder' -H 'Notion-Version: 2026-03-11' -H 'Content-Type: application/json' -d '{"parent":{"type":"page_id","page_id":"91000000-0000-4000-8000-000000000103"}}' | jq -c '{status, code}'
# -> {"status":400,"code":"validation_error"}
```

### Data sources

`POST /v1/data_sources` adds a data source with its own schema to an existing database, named in `parent` as `{"database_id": …}`. The database's `data_sources` lists it, and it takes rows, queries and search like the first. A data source query sorts by a number, title or rich_text property. Sorting by any other property type is unconfirmed: it has not been checked against Notion yet.

### Templates

`GET /v1/data_sources/{data_source_id}/templates` lists a data source's templates, filtered by `name` and paged. Notion's API cannot create a template, so a data source lists only the templates its scenario or seed holds; `notion_template_board` holds five.

`POST /v1/pages` with a `template` copies the template page's children into the new page:

| `template` | The new page |
|---|---|
| `{"type": "default"}` | gets the children of the data source's default template. With no default template, the request returns 400 `validation_error`. |
| `{"type": "template_id", "template_id": …}` | gets the children of that template |
| `{"type": "none"}` | takes `children` as usual |

With either of the first two, sending `children`, `content` or `markdown` as well returns 400 `validation_error`.

### Moves

`POST /v1/pages/{page_id}/move` moves a page under another page or into a data source. The page leaves its old parent's children and joins the new parent's children or the data source's query results, and its subtree goes with it. Moving a page under itself returns 400 `validation_error`.

### Property items and two-way relations

`GET /v1/pages/{page_id}/properties/{property_id}` takes the property's `id` from the page. The title property's id is `title`. Every other property the simulator creates has its own 4-character id, made of letters only.

**Differs from Notion:** Notion's property ids may also hold URL-encoded punctuation, such as `%3B`.

What the read returns depends on the property's type:

| Property | The read returns |
|---|---|
| a single-value property | one `property_item` |
| a `title`, `rich_text`, `people` or `relation` property | a list with one value per item, paged as [Pagination](https://www.rystic.ai/docs/simulators/notion.md#pagination) describes |

The list is how a caller reads a relation longer than the 25 references a page read returns. While `has_more` is true, `property_item.next_url` is the next page's URL on the simulator's host, carrying `start_cursor` and `page_size`. On the last page it is `null`. The `notion_linked_sources` scenario holds a project that relates 30 tasks for this.

Creating a relation property with `dual_property: {}`, through `POST /v1/data_sources` or `PATCH /v1/data_sources/{data_source_id}`, adds the matching property to the target data source. The simulator names it `Related to <title> (<property>)`, as Notion does. The response carries its `synced_property_id` and `synced_property_name`.

Writing such a relation on a row, through `POST /v1/pages` or `PATCH /v1/pages/{page_id}`, updates the synced property on every page the value names or no longer names. Both sides of the relation, its property items and a query's `relation` filter then agree. A `single_property` relation stays one-way.

**Differs from Notion:** Notion fills the synced side a second or more after the write. The simulator fills it at once.

---

Source: https://www.rystic.ai/docs/simulators/notion/features/data-sources · Markdown: https://www.rystic.ai/docs/simulators/notion/features/data-sources.md
