2026-06-18
Automate Notion with OpenClaw: Treat a Page as Two Write Surfaces
A 49-case write-contract drill separates Notion properties, blocks, markdown, and page creation across exact targets, capabilities, review, and replay.
Notion write-contract teardown · current API documentation · deterministic local fixture · no workspace mutation
Automate Notion with OpenClaw by splitting the page before the agent writes it. The structured properties that make a database useful live behind one contract. The blocks that make a page readable live behind another. Enhanced markdown adds a third editing surface with convenient search-and-replace and full replacement operations. Treating all three as “update this Notion doc” hides the destructive boundary exactly where an autonomous workflow needs it exposed.
The old version of this article promised four workflows, quick setup, and broad ROI. None of those claims had retained evidence. The narrower question is more useful: what exact object is allowed to change, which Notion operation performs that change, and what must still match when the request reaches the API?
2026-03-11 API version shown in current examples. A deterministic JavaScript fixture then varied 49 write packets across target identity, data-source discovery, capabilities, schema and page drift, property pagination, block anchors, markdown safety, payload limits, delivery state, review, and replay. All 49 expected decisions matched. No Notion credential or customer workspace was used.
Failure modes begin with the page-shaped trap
Notion’s API makes the split explicit. Updating a page object changes attributes such as properties, icon, cover, trash state, lock state, or template application. Adding page content uses the append-block-children endpoint instead. A page ID can be passed as the parent block ID, but the capability is different: property updates require update content, while appending children requires insert content.
Those distinctions are operational.
Those are not cosmetic distinctions. A property patch must match the parent data source’s schema. Rollups cannot be updated, and Notion-generated fields such as created time or last edited time are not writable. Block insertion has its own positional contract, a 100-child request limit, and a maximum of two nested levels in one request. Existing blocks cannot be moved with the append endpoint.
Enhanced markdown is useful because agents already reason in text. It is also the widest surface. The current endpoint recommends targeted update_content search-and-replace operations for precise edits and supports replace_content for a full rewrite. By default it refuses an edit that would delete child pages or databases. Turning on allow_deleting_content crosses a different risk boundary and should not be smuggled into an ordinary copy edit.
Version drift now changes object identity
The 2025-09-03 API upgrade introduced first-class multi-source databases. Operations that once relied on a database ID moved to a data-source ID, and a database can expose more than one source. The migration guide warns that older-version integrations can fail when a user adds another data source. Search results, webhooks, relations, and create/query flows all need to preserve the distinction.
That makes the Notion-Version header part of the approved packet, not boilerplate copied from an old sample. Current documentation examples use 2026-03-11. A runner should discover the data sources available under the selected database, resolve exactly one allowed source, retain its ID and schema digest, and stop if the set changes. A database ID and a data-source ID may look similar, but they are not interchangeable capabilities.
Discovery comes before selection.
The same rule applies to properties. Use stable property IDs where possible, not only display names that an editor can rename. Retrieve the current schema before planning a write. If a property contains more than 25 references, the normal page response may be incomplete; Notion provides the retrieve-page-property endpoint so title, rich text, relation, and people values can be paginated. A review built from a truncated relation list is not a complete review.
Testing forty-nine packets instead of one optimistic demo
The retained fixture started with one approved property patch. It fixed the API version, page ID, data-source ID, parent binding, last-edited timestamp, connection capabilities, property schema digest, current block-tree digest, request limits, review digest, and idempotency key. The fixture never called Notion. Its purpose was to test the admission layer that should run before a network mutation.
Each case changed one boundary. Five packets were write-ready—property patch, block append, markdown update, and page creation, plus a separate read outcome. One waited for review and one returned a cached receipt. The other 42 were blocked or held. The fixture SHA-256 is e942602db691dafeafac93b1fa661972899b66a8861e0bc6f844a0f2f49501ba.
| Changed condition | Decision | Why the write stops |
|---|---|---|
| API header falls back to an older version | BLOCK_API_VERSION | The reviewed object model no longer matches the request. |
| A second data source appears | BLOCK_DATA_SOURCE_DISCOVERY | The database no longer resolves to one approved write target. |
| Page last-edited time moves | BLOCK_PAGE_DRIFT | A person or another automation changed the page after planning. |
| Property schema digest changes | BLOCK_PROPERTY_SCHEMA_DRIFT | Names, types, or allowed values may no longer accept the approved patch. |
| Append anchor disappears | BLOCK_INSERT_ANCHOR | Blindly appending elsewhere would change document meaning. |
| Markdown read is truncated or contains unknown blocks | BLOCK_TRUNCATED_MARKDOWN / BLOCK_UNKNOWN_BLOCK | A full replacement cannot prove what it preserves. |
| Rate limit or overload response arrives | HOLD_RETRY_AFTER | The worker honors the server delay instead of improvising retries. |
| Completed key repeats | RETURN_CACHED_RECEIPT | The caller receives the first result rather than a second write. |
The numbers describe this synthetic contract, not Notion reliability. They show that the write adapter can distinguish a property mutation from a content mutation and can refuse ambiguous state before an external side effect.
The refusal is the result.
Give the agent four small tools
A generic HTTP tool forces policy into a prompt. A generic “Notion update” tool merely gives the ambiguity a nicer name. The safer design is a family of narrow operations whose schemas make incompatible actions impossible to combine.
{
"operation": "update_properties",
"apiVersion": "2026-03-11",
"pageId": "…",
"dataSourceId": "…",
"expectedLastEditedTime": "…",
"schemaDigest": "sha256:…",
"patch": [{ "propertyId": "…", "type": "status", "value": "Ready" }],
"reviewDigest": "sha256:…",
"idempotencyKey": "workspace:page:revision:operation"
}
notion.properties.patch accepts only writable property types and stable property IDs. notion.blocks.append accepts a page or block target, a bounded child array, and a typed position: start, end, or after one verified block. notion.markdown.update accepts targeted old/new pairs and fails on a missing or ambiguous match unless an explicit replace-all policy was reviewed. notion.pages.create accepts one parent form and a bounded initial body or template choice.
One verb per tool.
Keep trashing, restoring, locking, applying templates, erasing content, moving organizational structure, and deleting child pages out of those normal tools. They can exist as separate operator actions. A property update should never acquire destructive page administration because both happen to share PATCH /v1/pages/{page_id}.
Validation starts with the representation you plan to edit
A block workflow should recursively retrieve block children because nested blocks are not returned inline. A property workflow should retrieve the relevant property items to completion. A markdown workflow should inspect truncated and unknown_block_ids before it proposes replacement. File URLs returned in markdown are short-lived signed URLs, so they belong in a transient read record rather than a durable source manifest.
Store a digest of the canonical read snapshot. Immediately before writing, re-read the page metadata and the relevant schema or content surface. Compare the last-edited time, target IDs, property schema, block anchors, and content digest. If any of them moved, rebuild the proposal and ask for fresh review.
This is optimistic concurrency implemented outside the API. It cannot prevent every race between the final read and the write, but it closes the large window between agent planning and execution. The remaining race is why the receipt should include the returned page revision and a post-write readback.
No fresh read, no write.
Webhooks are doorbells, not page snapshots
Notion’s webhook documentation is refreshingly direct: an event signals that something changed; it does not contain the full changed content. Events may arrive in a different order than they occurred, and the documentation recommends fetching the latest state. Delivery includes an event timestamp and attempt number, with retries when an endpoint does not acknowledge receipt.
That means an agent should not turn a webhook payload into a write proposal by itself. Record the event ID, order by timestamp only when needed, deduplicate the delivery, then fetch the current page and data source. If a newer event or revision appears while the job is queued, coalesce the work and plan from the newest snapshot. A webhook is permission to inspect, not permission to mutate.
A doorbell is not consent.
Rate limiting belongs in the same state machine. Notion documents an average of three requests per second per connection, plus a workspace-wide shared rate, and asks clients to honor Retry-After for 429 and 529 responses. A durable worker should record the hold-until time. Rapid blind retries are both impolite and a source of duplicate uncertainty.
The review should name the surface
A useful approval summary states whether the operation changes properties, appends blocks, performs targeted markdown replacements, replaces all content, or creates a page. It includes the workspace-neutral target label, exact IDs, API version, before/after values, insertion anchor, unknown or unsupported content, child-page deletion risk, capabilities used, and the idempotency key.
Hash the canonical packet, not the prose summary alone. Bind the operator decision to that digest and an expiry. If the page, schema, operation, anchor, requested values, or deletion policy changes, the digest changes and the approval no longer applies.
The replay ledger acquires the idempotency key before the network call. A completed entry returns its retained receipt. An in-progress entry waits. An uncertain entry fetches the latest page and reconciles the intended patch before deciding whether any retry is safe. Notion does not supply a universal idempotency key for these write surfaces, so the application has to own this discipline.
No receipt, no blind retry.
Best practices make request limits design inputs
The current request limits are concrete: 500 KB overall, 1,000 block elements, 2,000 characters for rich-text content and link URLs, and 100 items for arrays such as relations, people, and multi-select values. Append-block-children narrows one request further to 100 children and two levels of nesting.
Validate those limits before review, then chunk only where the semantics remain clear. A multi-request append should reserve one job key with ordered child batches and retain a receipt for each batch. If batch three fails, the worker resumes from retained state; it does not replay batches one and two. A full markdown replacement should not be silently chunked because splitting it changes the operation.
Chunking is not semantics.
Large markdown creates and updates can run asynchronously and return a task object. Treat that task ID as producer state. Poll or reconcile it; do not assume a client timeout means the write did not happen.
A rollout built around refusals
Start with a disposable workspace and read-only access. Add one write surface at a time, beginning with a low-impact property whose schema is fixed. Keep page creation, markdown replacement, trash, template application, and child deletion absent until their failure paths are tested.
- Rename a property after planning and require a schema-drift refusal.
- Add a second data source to the database and require fresh discovery.
- Edit the page between review and execution and invalidate the approval.
- Remove insert capability and verify that block append cannot fall back to another tool.
- Delete the approved insertion anchor and refuse a blind end-of-page append.
- Place a child page and an unsupported block in a markdown target; block full replacement.
- Return a synthetic 429, retain
Retry-After, and resume from the same job record. - Lose the response after a successful write, then reconcile the latest page before retrying.
- Replay a completed idempotency key and return the original receipt.
Only after those refusals are routine should the connection reach a valuable team database. The interesting automation is not the meeting-note prompt. It is the small, explainable write contract that prevents the prompt from changing the wrong surface.
Claim boundary
This replacement does not claim that a Notion connection was installed, that a workspace was queried, or that a customer page was changed. It makes no setup-time, speed, success-rate, cost, or ROI promise. The retained exercise proves that one local decision function produced the expected result for 49 synthetic packets. Official documentation supports the API facts; the admission policy is an operator design derived from those facts.
The operational takeaway is deliberately narrow
That modest boundary is deliberate. OpenClaw can draft excellent page content. A production Notion integration earns trust somewhere else: in the exact page and data-source IDs, the capability that matches the operation, the bytes reviewed, and the receipt that explains why a retry did—or did not—write again.
Sources
- Notion authorization: bearer authentication and connection types.
- Notion connection capabilities: read, update, and insert capability boundaries plus least-privilege guidance.
- Working with page content: page properties, block children, recursion, unsupported blocks, and page creation.
- Update page: property updates, templates, erase-content risk, limitations, and update capability.
- Append block children: insertion positions, 100-child and two-level limits, immovable existing blocks, and insert capability.
- Working with markdown content: supported and unknown blocks, truncation, create/read/update surfaces, and asynchronous writes.
- Update page content as markdown: targeted edits, full replacement, ambiguity errors, and protected child pages/databases.
- Upgrade guide for 2025-09-03: multi-source databases and migration from database IDs to data-source IDs.
- Notion request limits: rate, payload, block, rich-text, URL, and array limits.
- Retrieve a page property item: paginated property values and the 25-reference page-response boundary.
- Webhook event types and delivery: signal-only events, ordering, retries, and latest-state retrieval.
- OpenClaw Tools: typed tools and active tool-policy boundaries.