← Back to report Page 3 of 3
Full Transcript
Notion - Opus Transcript
Report: Notion - Universal Baseline Model: Opus 4.6 Tested: 2026-03-13 Agent CLI: Claude Code v2.1.74
Task 6
Reflection
OperatorStep 1
Looking back at everything you just did — discovering Notion's developer platform, setting up integration access, building the database and content, handling errors, and cleaning up: 1. Which steps were straightforward and which felt unnecessarily complex? 2. Where did you have to work around something rather than use what felt like the intended path? 3. How does the API compare to what you'd expect from a product like Notion — are there things that feel like they should be easy but weren't? 4. What would have made this easier? Be specific — refer to actual moments from the tasks above.
AgentStep 2
## Retrospective
### 1. Straightforward vs unnecessarily complex
**Straightforward:**
- **Authentication and connection** — once the integration was created, the token worked immediately. The `users.me()` endpoint gave a clean confirmation. The sharing model (explicit page sharing) is restrictive but simple to reason about.
- **Creating pages with rich content** — the block model maps well to what you see in Notion. Heading, paragraph, bulleted list, code block, to-do — each is a block type with a consistent `rich_text` structure. Step 4 of the workflow script was essentially a 1:1 translation of "what I want on the page" into API calls.
- **Filtering and sorting the database** — the query in step 3 (`select does_not_equal "Done"`, sort by due date ascending) read cleanly and returned exactly what was expected. The filter syntax is well-designed.
**Unnecessarily complex:**
- **Discovering the current API shape.** The SDK v5.12 has breaking changes (`initial_data_source` replacing `properties`, `dataSources.query()` replacing `databases.query()`), but the public documentation at developers.notion.com still describes the old API. My first workflow script used the documented approach, which the SDK rejected with a cryptic warning (`unknown parameters were ignored: ['properties']`). I had to reverse-engineer the new interface from the SDK's TypeScript declarations. This was the single biggest time sink.
- **Integration setup UI.** The URL I initially gave you landed on the public (OAuth) integration form. The internal integration flow — which is what you actually want for API testing — is behind a sidebar link on the same page. Minor, but a stumbling block at the first step.
### 2. Workarounds vs intended path
**The `initial_data_source` migration** was the clearest workaround. The intended path (based on all public docs) is `databases.create({ properties: {...} })`. The SDK silently ignored that parameter and created an empty database. I had to spawn a research agent to read the SDK's `.d.ts` files and piece together that properties now live inside `initial_data_source`. The database was created twice because of this — the orphaned empty one from the first attempt had to be cleaned up later.
**The 2,000-character rich text limit** forced a workaround in step 4. The query JSON was 4,738 characters. The API returned a clear error, but the fix — chunking the string into multiple rich text segments within one code block — felt like something the SDK or API should handle transparently. No developer thinks about character limits when putting JSON into a code block. The limit exists because rich text segments are the storage unit, but that's an implementation detail leaking through.
**Cleanup required two passes** because Notion uses different deletion mechanisms for different object types. Database entries are pages (archived via `pages.update`), databases are blocks (deleted via `blocks.delete`), and standalone pages are also archived via `pages.update`. The inconsistency meant I had to know which abstraction each object "really is" under the hood.
### 3. API vs product expectations
**Select properties accepting any value silently** was the most surprising gap. In the Notion UI, you pick from a dropdown of defined options. Via the API, writing `"Cancelled"` to a select field that only has "Not started", "In progress", and "Done" silently auto-creates "Cancelled" as a new option and succeeds. No error, no warning. For a product built around structured data, this is a significant integrity hole. An agent with a typo will pollute the schema with no signal.
**No full-text search.** Notion's product has a search bar that searches page content. The API's search endpoint only matches page and database titles. If I'd needed to find "which page mentions the Opus audit," I'd have had to fetch every page's blocks and search client-side. For a knowledge base product, content search feels like table stakes for the API.
**No database views.** I created a database with properties, entries, and filters — but there's no way to create a board view grouped by Status, or a calendar view on Due date, via the API. Views are a core part of how Notion databases are actually used. The API gives you the data layer but none of the presentation layer.
**No permanent deletion.** Everything I "deleted" went to Trash. The API can archive and soft-delete, but permanent deletion requires the UI. For automated cleanup or compliance workflows, this is a gap.
### 4. What would have made this easier
**Documentation matching the current SDK.** This would have saved 10+ minutes and an entire failed run. The docs describe v1-era parameter shapes; the SDK ships a v2025+ API. Even a migration guide or changelog entry mapping old to new would have been enough.
**Strict mode for select/multi-select writes.** A parameter like `strict: true` or a workspace setting that rejects unknown option values would prevent silent schema pollution. This is the single change that would matter most for agent use cases.
**Auto-chunking in the SDK for rich text.** The 2,000-char limit is fine as a wire-level constraint, but the SDK could split long strings automatically rather than forcing every caller to implement chunking.
**A consistent deletion API.** One method — `notion.archive(id)` or `notion.delete(id)` — that works on pages, databases, and blocks without requiring the caller to know which object type they're dealing with.
**A bulk write endpoint.** Creating 3 database entries required 3 sequential API calls at a 3 req/sec rate limit. A batch endpoint accepting multiple page creates in one call would make the common case (populating a database) dramatically faster.