Notion
A Notion workspace that answers the REST API notion-client calls, Notion's public-integration OAuth, webhook events and Notion's MCP tools.
Vendor reference: https://developers.notion.com/reference/intro ↗. Machine-readable: compat/notion.json.
Surfaces
| Kind | Path | Notes |
|---|---|---|
| REST | /v1 | pages, databases, data sources, blocks, comments, users, search, file uploads; Notion-Version header required |
| OAuth | /v1/oauth | authorize, token, introspect, revoke with Notion's token body |
| Webhooks | webhook events | delivered to a subscribed endpoint, retried with backoff |
| MCP | /mcp | bearer-authenticated, with RFC 9728 discovery for clients that register themselves |
API versions: Notion-Version 2022-06-28, 2025-09-03, 2026-03-11
Supported
- Notion's list envelopes with
next_cursor/has_more, and its error bodies - The shape changes of
Notion-Version2025-09-03(data sources,page_or_data_source), and older clients seeing databases - The official SDK
notion-client, with onlybase_urlchanged - Notion's own MCP server tool set
- Connection capabilities from Notion's own set, enforced per token
- The database query grammar
Not supported
- Connected sources (Slack, Drive, Jira) in
notion-search - A consent screen for OAuth;
authorizeauto-approves - Storage behind data-source templates
- Fetching an upload's external URL
Known differences and test guidance
| Scenario | Difference from Notion | In your tests |
|---|---|---|
| Search ranking | Search ranks by substring match, not Notion's relevance model. | Assert that a page is found, not on its position. |
| Async tasks | allow_async and notion-duplicate-page return real task handles, but the work is done before the handle is issued. | Do not assert on an in-progress state. |
| Rate limits | There is no 180 requests/minute limit; limits are only what you inject. | Inject throttle to test backoff. |
| File URLs | Upload bytes are held in the process and served from the replica's own signed /v1/files/... URL, not Notion's CDN. | Do not assert on the file host. |
| Failing webhooks | A refused delivery is retried with backoff, but a subscription is never disabled after repeated failures. | Do not test webhook auto-disable. |
| Restarts | Writes last until the process stops unless persistence is configured; upload bytes, webhook subscriptions and faults are never kept. | Treat each run as starting from the seeded workspace. |
Fault injection
| Key | What the client sees |
|---|---|
throttle | 429 {"object": "error", "code": "rate_limited"} with Retry-After |
fail_next | The next N calls fail as throttle does, then clear |
quota_limit | 429 rate_limited "Request quota exhausted" |
error_rate | 500 internal_server_error for the given fraction of calls |
latency_ms | Fixed added latency on every data-plane request |
Applies to every system
- The hosted data plane is read-only: a write is refused with
403 writes_disabled. A SOQL, GraphQL or searchPOSTis a read and is answered. - Faults are injected through
POST /_admin/faultson a simulator you run yourself;POST /_admin/faults/resetclears them. - Rate limits do not happen on their own unless a page says so. Use fault injection to exercise a client's backoff.
- Distributions come from aggregated metadata sketches of data Eon backs up; no customer records; all Era data is simulated.