@notionhq/client 5.26.0 → 5.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +130 -129
  2. package/build/package.json +4 -3
  3. package/build/src/Client.d.ts +1869 -130
  4. package/build/src/Client.d.ts.map +1 -1
  5. package/build/src/Client.js +86 -571
  6. package/build/src/Client.js.map +1 -1
  7. package/build/src/api-endpoint-methods.d.ts +920 -0
  8. package/build/src/api-endpoint-methods.d.ts.map +1 -0
  9. package/build/src/api-endpoint-methods.js +171 -0
  10. package/build/src/api-endpoint-methods.js.map +1 -0
  11. package/build/src/api-endpoints/agent-skills.d.ts +68 -0
  12. package/build/src/api-endpoints/agent-skills.d.ts.map +1 -0
  13. package/build/src/api-endpoints/agent-skills.js +36 -0
  14. package/build/src/api-endpoints/agent-skills.js.map +1 -0
  15. package/build/src/api-endpoints/agents.d.ts +173 -146
  16. package/build/src/api-endpoints/agents.d.ts.map +1 -1
  17. package/build/src/api-endpoints/agents.js.map +1 -1
  18. package/build/src/api-endpoints/data-sources.d.ts +1 -0
  19. package/build/src/api-endpoints/data-sources.d.ts.map +1 -1
  20. package/build/src/api-endpoints/data-sources.js.map +1 -1
  21. package/build/src/api-endpoints/databases.d.ts +3 -1
  22. package/build/src/api-endpoints/databases.d.ts.map +1 -1
  23. package/build/src/api-endpoints/databases.js +1 -0
  24. package/build/src/api-endpoints/databases.js.map +1 -1
  25. package/build/src/api-endpoints/file-uploads.d.ts.map +1 -1
  26. package/build/src/api-endpoints/file-uploads.js.map +1 -1
  27. package/build/src/api-endpoints/pages.d.ts.map +1 -1
  28. package/build/src/api-endpoints/pages.js.map +1 -1
  29. package/build/src/api-endpoints/users.d.ts.map +1 -1
  30. package/build/src/api-endpoints/users.js.map +1 -1
  31. package/build/src/api-endpoints.d.ts +1 -0
  32. package/build/src/api-endpoints.d.ts.map +1 -1
  33. package/build/src/api-endpoints.js +1 -0
  34. package/build/src/api-endpoints.js.map +1 -1
  35. package/build/src/errors.d.ts +11 -0
  36. package/build/src/errors.d.ts.map +1 -1
  37. package/build/src/errors.js +20 -1
  38. package/build/src/errors.js.map +1 -1
  39. package/build/src/helpers.d.ts +4 -4
  40. package/build/src/helpers.d.ts.map +1 -1
  41. package/build/src/helpers.js.map +1 -1
  42. package/build/src/index.d.ts +2 -2
  43. package/build/src/index.d.ts.map +1 -1
  44. package/build/src/index.js +2 -1
  45. package/build/src/index.js.map +1 -1
  46. package/build/src/webhooks.d.ts +12 -19
  47. package/build/src/webhooks.d.ts.map +1 -1
  48. package/build/src/webhooks.js +27 -35
  49. package/build/src/webhooks.js.map +1 -1
  50. package/package.json +4 -3
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Notion SDK for JavaScript
2
2
 
3
- <img alt="Notion Logo" src="https://www.notion.so/images/notion-logo-block-main.svg" width="70" />
3
+ <img alt="Notion logo" src="https://mcp.notion.com/notion-logo-block-sticker.svg" width="70" />
4
4
 
5
- **A simple and easy to use client for the [Notion API](https://developers.notion.com).**
5
+ A JavaScript and TypeScript client for the [Notion API](https://developers.notion.com). This reference covers the SDK's methods, options, and helpers.
6
6
 
7
7
  ![Build status](https://github.com/makenotion/notion-sdk-js/actions/workflows/ci.yml/badge.svg)
8
8
  [![npm version](https://badge.fury.io/js/%40notionhq%2Fclient.svg)](https://www.npmjs.com/package/@notionhq/client)
@@ -18,14 +18,13 @@ npm install @notionhq/client
18
18
  ## Usage
19
19
 
20
20
  > [!NOTE]
21
- > Use Notion's [Getting Started Guide](https://developers.notion.com/docs/getting-started) to get set up to use Notion's API.
21
+ > For setup steps, see Notion's [getting started guide](https://developers.notion.com/docs/getting-started).
22
22
 
23
- Import and initialize a client using an **integration token** or an OAuth **access token**.
23
+ `Client` accepts an integration token or an OAuth access token.
24
24
 
25
25
  ```js
26
26
  const { Client } = require("@notionhq/client")
27
27
 
28
- // Initializing a client
29
28
  const notion = new Client({
30
29
  auth: process.env.NOTION_TOKEN,
31
30
  })
@@ -36,17 +35,14 @@ Make a request to any Notion API endpoint.
36
35
  ```js
37
36
  ;(async () => {
38
37
  const listUsersResponse = await notion.users.list({})
38
+ console.log(listUsersResponse)
39
39
  })()
40
40
  ```
41
41
 
42
42
  > [!NOTE]
43
43
  > See the complete list of endpoints in the [API reference](https://developers.notion.com/reference).
44
44
 
45
- Each method returns a `Promise` that resolves the response.
46
-
47
- ```js
48
- console.log(listUsersResponse)
49
- ```
45
+ Request methods return a `Promise` with the response. For example:
50
46
 
51
47
  ```ts
52
48
  {
@@ -83,12 +79,14 @@ const myPage = await notion.dataSources.query({
83
79
 
84
80
  ### Handling errors
85
81
 
86
- If the API returns an unsuccessful response, the returned `Promise` rejects with a `APIResponseError`.
87
-
88
- The error contains properties from the response, and the most helpful is `code`. You can compare `code` to the values in the `APIErrorCode` object to avoid misspelling error codes.
82
+ Notion API errors reject the request with an `APIResponseError`. The `code` property identifies the error. `APIErrorCode` contains the known server error codes.
89
83
 
90
84
  ```js
91
- const { Client, APIErrorCode } = require("@notionhq/client")
85
+ const {
86
+ Client,
87
+ APIErrorCode,
88
+ isNotionClientError,
89
+ } = require("@notionhq/client")
92
90
 
93
91
  try {
94
92
  const notion = new Client({ auth: process.env.NOTION_TOKEN })
@@ -102,10 +100,11 @@ try {
102
100
  },
103
101
  })
104
102
  } catch (error) {
105
- if (error.code === APIErrorCode.ObjectNotFound) {
106
- //
107
- // For example: handle by asking the user to select a different data source
108
- //
103
+ if (
104
+ isNotionClientError(error) &&
105
+ error.code === APIErrorCode.ObjectNotFound
106
+ ) {
107
+ // Ask the user to select a different data source.
109
108
  } else {
110
109
  // Other error handling code
111
110
  console.error(error)
@@ -115,9 +114,7 @@ try {
115
114
 
116
115
  ### Logging
117
116
 
118
- The client emits useful information to a logger. By default, it only emits warnings and errors.
119
-
120
- If you're debugging an application, and would like the client to log response bodies, set the `logLevel` option to `LogLevel.DEBUG`.
117
+ The default logger writes warnings and errors to the console. `LogLevel.DEBUG` also logs response bodies.
121
118
 
122
119
  ```js
123
120
  const { Client, LogLevel } = require("@notionhq/client")
@@ -128,42 +125,57 @@ const notion = new Client({
128
125
  })
129
126
  ```
130
127
 
131
- You may also set a custom `logger` to emit logs to a destination other than `stdout`. A custom logger is a function which is called with 3 parameters: `logLevel`, `message`, and `extraInfo`. The custom logger should not return a value.
128
+ A custom `logger` receives `logLevel`, `message`, and `extraInfo`. It should return no value.
132
129
 
133
130
  ### Client options
134
131
 
135
- The `Client` supports the following options on initialization. These options are all keys in the single constructor parameter.
132
+ The `Client` constructor accepts one options object.
133
+
134
+ | Option | Default value | Type | Description |
135
+ | ------------------------- | --------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
136
+ | `auth` | `undefined` | `string` | Bearer token for authentication. If left undefined, the `auth` parameter should be set on each request. |
137
+ | `logLevel` | `LogLevel.WARN` | `LogLevel` | Verbosity of logs the instance will produce. By default, logs are written to `stdout`. |
138
+ | `timeoutMs` | `DEFAULT_TIMEOUT_MS` | `number` | Number of milliseconds to wait before emitting a `RequestTimeoutError` |
139
+ | `baseUrl` | `DEFAULT_BASE_URL` | `string` | The root URL for sending API requests. This can be changed to test with a mock server. |
140
+ | `logger` | Log to console | `Logger` | A custom logging function. This function is only called when the client emits a log that is equal or greater severity than `logLevel`. |
141
+ | `agent` | Default node agent | `http.Agent` | Used to control creation of TCP sockets. A common use is to proxy requests with [`https-proxy-agent`](https://github.com/TooTallNate/node-https-proxy-agent) |
142
+ | `retry` | See [constants](#constants) | `RetryOptions` | Configuration for automatic retries on rate limits (429), service overloads (529), and server errors (500, 503). See [Automatic retries](#automatic-retries) below. |
143
+ | `dangerouslyAllowBrowser` | `false` | `boolean` | Allows a token inside a browser page or worker. Without it, the client throws `BrowserTokenNotAllowedError` there. See [Browser usage](#browser-usage) below. |
136
144
 
137
- | Option | Default value | Type | Description |
138
- | ----------- | --------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
139
- | `auth` | `undefined` | `string` | Bearer token for authentication. If left undefined, the `auth` parameter should be set on each request. |
140
- | `logLevel` | `LogLevel.WARN` | `LogLevel` | Verbosity of logs the instance will produce. By default, logs are written to `stdout`. |
141
- | `timeoutMs` | `DEFAULT_TIMEOUT_MS` | `number` | Number of milliseconds to wait before emitting a `RequestTimeoutError` |
142
- | `baseUrl` | `DEFAULT_BASE_URL` | `string` | The root URL for sending API requests. This can be changed to test with a mock server. |
143
- | `logger` | Log to console | `Logger` | A custom logging function. This function is only called when the client emits a log that is equal or greater severity than `logLevel`. |
144
- | `agent` | Default node agent | `http.Agent` | Used to control creation of TCP sockets. A common use is to proxy requests with [`https-proxy-agent`](https://github.com/TooTallNate/node-https-proxy-agent) |
145
- | `retry` | See [constants](#constants) | `RetryOptions` | Configuration for automatic retries on rate limits (429), service overloads (529), and server errors (500, 503). See [Automatic retries](#automatic-retries) below. |
145
+ ### Browser usage
146
+
147
+ The Notion API sends CORS headers, so browser code can call `api.notion.com` directly. The client works in modern browsers with the built-in `fetch`.
148
+
149
+ A token in a web page isn't a secret. Anyone who can load the page can read it and act as your connection.
150
+
151
+ - For an app other people use, keep the token on a server. Create a [public connection](https://developers.notion.com/guides/get-started/public-connections), which uses OAuth. Exchange the authorization code on your server, and send the page only the data it needs.
152
+ - For a personal tool or a local page that only you can open, using a token directly is your own risk to accept.
153
+
154
+ The client blocks tokens in a browser by default. Constructing it with `auth` inside a browser page throws `BrowserTokenNotAllowedError`. Pass `dangerouslyAllowBrowser: true` to confirm you understand the risk.
155
+
156
+ ```js
157
+ const notion = new Client({
158
+ auth: tokenTypedIntoYourOwnPage,
159
+ dangerouslyAllowBrowser: true,
160
+ })
161
+ ```
162
+
163
+ A token passed per request through `auth` is blocked the same way: the request rejects with `BrowserTokenNotAllowedError` before anything is sent. A client without a token still works in a browser, for example with a `baseUrl` that points at your own server, which adds the token. Web workers and service workers count as a browser too, because every visitor downloads their script. Test runners that emulate a browser, such as jsdom, count as well, so pass `dangerouslyAllowBrowser: true` in those tests. The `agent` option is ignored in browsers, and the client doesn't send its `user-agent` header there, because the API's CORS rules don't allow it. OAuth token endpoints under `/v1/oauth/` don't send CORS headers, so exchange authorization codes on a server.
146
164
 
147
165
  ### Automatic retries
148
166
 
149
- The client automatically retries requests that fail due to rate limiting or transient server errors. By default, it will retry up to 2 times using exponential back-off with jitter.
167
+ The client retries failed requests up to 2 times by default. Delays increase with each retry and include a random offset.
150
168
 
151
- **Retryable errors:**
169
+ Retried errors:
152
170
 
153
171
  - `rate_limited` (HTTP 429) - Too many requests; retried for all HTTP methods
154
172
  - `service_overload` (HTTP 529) - Service overloaded; retried for all HTTP methods
155
173
  - `internal_server_error` (HTTP 500) - Server error; retried only for GET and DELETE
156
174
  - `service_unavailable` (HTTP 503) - Service temporarily unavailable; retried only for GET and DELETE
157
175
 
158
- Server errors (500, 503) are only retried for idempotent HTTP methods (GET, DELETE) to avoid duplicate side effects. Rate limits (429) and service overloads (529) are retried for all methods since the server explicitly asks clients to retry.
159
-
160
- **Retry behavior:**
176
+ For server errors, only GET and DELETE are retried to avoid repeating writes. The client uses the `Retry-After` header when present. It accepts a delay in seconds or an HTTP date.
161
177
 
162
- - Uses exponential back-off: delays increase with each retry attempt
163
- - Respects the `Retry-After` header when present (both delta-seconds and HTTP-date formats)
164
- - Adds random jitter to prevent thundering herd problems
165
-
166
- **Configuration:**
178
+ Retry options:
167
179
 
168
180
  ```js
169
181
  const notion = new Client({
@@ -187,7 +199,7 @@ const notion = new Client({
187
199
 
188
200
  ### Constants
189
201
 
190
- The SDK exports named constants for all default values used by the client, as well as useful Notion-specific values. You can import them directly:
202
+ The SDK exports these defaults and Notion-specific values:
191
203
 
192
204
  ```js
193
205
  const {
@@ -200,7 +212,7 @@ const {
200
212
  } = require("@notionhq/client")
201
213
  ```
202
214
 
203
- `MIN_VIEW_COLUMN_WIDTH` is the minimum width (in pixels) that a table column can have in the Notion UI. Set a property's `width` to this value when creating or updating a view to make a column appear collapsed -- useful for checkbox or status-as-checkbox columns:
215
+ `MIN_VIEW_COLUMN_WIDTH` is the minimum table column width in pixels. A column at this width appears collapsed. For example:
204
216
 
205
217
  ```js
206
218
  await notion.views.create({
@@ -223,19 +235,22 @@ await notion.views.create({
223
235
 
224
236
  ### TypeScript
225
237
 
226
- This package contains type definitions for all request parameters and responses,
227
- as well as some useful sub-objects from those entities.
238
+ The package includes types for request parameters, responses, and their fields.
228
239
 
229
- Because errors in TypeScript start with type `any` or `unknown`, you should use
230
- the `isNotionClientError` type guard to handle them in a type-safe way. Each
231
- `NotionClientError` type is uniquely identified by its `error.code`. Codes in
232
- the `APIErrorCode` enum are returned from the server. Codes in the
233
- `ClientErrorCode` enum are produced on the client.
240
+ With strict TypeScript, caught errors have type `unknown`. `isNotionClientError`
241
+ narrows the error to a known SDK error type. `APIErrorCode` identifies server
242
+ errors; `ClientErrorCode` identifies errors raised by the client.
234
243
 
235
244
  ```ts
245
+ import {
246
+ APIErrorCode,
247
+ ClientErrorCode,
248
+ isNotionClientError,
249
+ } from "@notionhq/client"
250
+
236
251
  try {
237
252
  const response = await notion.dataSources.query({
238
- /* ... */
253
+ data_source_id: dataSourceId,
239
254
  })
240
255
  } catch (error: unknown) {
241
256
  if (isNotionClientError(error)) {
@@ -250,10 +265,8 @@ try {
250
265
  case APIErrorCode.Unauthorized:
251
266
  // ...
252
267
  break
253
- // ...
254
268
  default:
255
- // you could even take advantage of exhaustiveness checking
256
- assertNever(error.code)
269
+ console.error(error)
257
270
  }
258
271
  }
259
272
  }
@@ -261,8 +274,8 @@ try {
261
274
 
262
275
  #### Type guards
263
276
 
264
- There are several [type guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types)
265
- provided to distinguish between full and partial API responses.
277
+ These [type guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types)
278
+ distinguish full API responses from partial responses.
266
279
 
267
280
  | Type guard function | Purpose |
268
281
  | ------------------------ | ---------------------------------------------------------------------------------------- |
@@ -273,7 +286,7 @@ provided to distinguish between full and partial API responses.
273
286
  | `isFullUser` | Determine whether an object is a full `UserObjectResponse` |
274
287
  | `isFullComment` | Determine whether an object is a full `CommentObjectResponse` |
275
288
 
276
- Here is an example of using a type guard:
289
+ Example:
277
290
 
278
291
  ```typescript
279
292
  const fullOrPartialPages = await notion.dataSources.query({
@@ -293,26 +306,25 @@ for (const page of fullOrPartialPages.results) {
293
306
 
294
307
  ### Utility functions
295
308
 
296
- This package also exports a few utility functions that are helpful for dealing with
297
- any of our paginated APIs.
309
+ These helpers read results across multiple pages.
298
310
 
299
311
  #### `iteratePaginatedAPI(listFn, firstPageArgs)`
300
312
 
301
- This utility turns any paginated API into an async iterator.
313
+ Returns an async iterator that reads each page of results as needed.
302
314
 
303
- **Parameters:**
315
+ Parameters:
304
316
 
305
317
  - `listFn`: Any function on the Notion client that represents a paginated API (i.e. accepts
306
318
  `start_cursor`.) Example: `notion.blocks.children.list`.
307
319
  - `firstPageArgs`: Arguments that should be passed to the API on the first and subsequent calls
308
320
  to the API, for example a `block_id`.
309
321
 
310
- **Returns:**
322
+ Returns:
311
323
 
312
324
  An [async iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols)
313
325
  over results from the API.
314
326
 
315
- **Example:**
327
+ Example:
316
328
 
317
329
  ```javascript
318
330
  for await (const block of iteratePaginatedAPI(notion.blocks.children.list, {
@@ -324,24 +336,21 @@ for await (const block of iteratePaginatedAPI(notion.blocks.children.list, {
324
336
 
325
337
  #### `collectPaginatedAPI(listFn, firstPageArgs)`
326
338
 
327
- This utility accepts the same arguments as `iteratePaginatedAPI`, but collects
328
- the results into an in-memory array.
339
+ Accepts the same arguments as `iteratePaginatedAPI` and returns all results in
340
+ one array. The results must fit in memory.
329
341
 
330
- Before using this utility, check that the data you are dealing with is
331
- small enough to fit in memory.
332
-
333
- **Parameters:**
342
+ Parameters:
334
343
 
335
344
  - `listFn`: Any function on the Notion client that represents a paginated API (i.e. accepts
336
345
  `start_cursor`.) Example: `notion.blocks.children.list`.
337
346
  - `firstPageArgs`: Arguments that should be passed to the API on the first and subsequent calls
338
347
  to the API, for example a `block_id`.
339
348
 
340
- **Returns:**
349
+ Returns:
341
350
 
342
351
  An array with results from the API.
343
352
 
344
- **Example:**
353
+ Example:
345
354
 
346
355
  ```javascript
347
356
  const blocks = await collectPaginatedAPI(notion.blocks.children.list, {
@@ -352,23 +361,14 @@ const blocks = await collectPaginatedAPI(notion.blocks.children.list, {
352
361
 
353
362
  #### `iterateAllDataSourceRows(client, args)`
354
363
 
355
- A single data source query (one filter and sort) returns at most a fixed number
356
- of rows, 10,000 by default. Once that limit is reached, `has_more` becomes
357
- `false` and the response carries `request_status.type === "incomplete"`. Plain
358
- pagination such as `iteratePaginatedAPI` stops there and silently misses the rest
359
- of a larger data source.
360
-
361
- This utility reads every row anyway. It partitions the data source into
362
- `created_time` windows: it sorts by `created_time` ascending, and whenever a
363
- window hits the limit it starts a fresh query from the last row's timestamp.
364
- Each fresh query has a different filter, so it gets its own result budget. Rows
365
- that share a boundary timestamp are de-duplicated by id, so every row is yielded
366
- exactly once.
364
+ Reads rows beyond the limit for a single data source query, which is 10,000 by
365
+ default. At that limit, `has_more` is `false` and `request_status.type` is
366
+ `"incomplete"`; ordinary pagination stops.
367
367
 
368
- `created_time` is used because it never changes. `last_edited_time` would shift
369
- rows between windows as they are edited, causing gaps or duplicates.
368
+ This helper sorts by `created_time` and starts a new query from the last row's
369
+ timestamp each time it reaches the limit. It removes duplicate rows by ID.
370
370
 
371
- **Parameters:**
371
+ Parameters:
372
372
 
373
373
  - `client`: A Notion client instance.
374
374
  - `args`: The same arguments as `dataSources.query`, minus the fields the helper
@@ -376,17 +376,17 @@ rows between windows as they are edited, causing gaps or duplicates.
376
376
  `created_time` ascending to partition). `data_source_id` is required. Any
377
377
  `filter` you pass is combined with the window bound using `and`.
378
378
 
379
- **Returns:**
379
+ Returns:
380
380
 
381
381
  An async iterator over every row in the data source.
382
382
 
383
- **Throws:**
383
+ Throws:
384
384
 
385
385
  If a single `created_time` value holds more rows than the limit, the window
386
386
  cannot be narrowed by time alone. Pass a `filter` in that case so each window
387
387
  stays under the limit.
388
388
 
389
- **Example:**
389
+ Example:
390
390
 
391
391
  ```javascript
392
392
  for await (const row of iterateAllDataSourceRows(notion, {
@@ -398,23 +398,20 @@ for await (const row of iterateAllDataSourceRows(notion, {
398
398
 
399
399
  #### `collectAllDataSourceRows(client, args)`
400
400
 
401
- This utility accepts the same arguments as `iterateAllDataSourceRows`, but
402
- collects the results into an in-memory array.
401
+ Accepts the same arguments as `iterateAllDataSourceRows` and returns all results
402
+ in one array. The full data source must fit in memory. For larger data sources,
403
+ `iterateAllDataSourceRows` reads rows as a stream.
403
404
 
404
- Before using this utility, check that the full data source fits in memory. For
405
- very large data sources, prefer `iterateAllDataSourceRows` and process rows as
406
- they stream.
407
-
408
- **Parameters:**
405
+ Parameters:
409
406
 
410
407
  - `client`: A Notion client instance.
411
408
  - `args`: The same arguments as `iterateAllDataSourceRows`.
412
409
 
413
- **Returns:**
410
+ Returns:
414
411
 
415
412
  An array with every row in the data source.
416
413
 
417
- **Example:**
414
+ Example:
418
415
 
419
416
  ```javascript
420
417
  const rows = await collectAllDataSourceRows(notion, {
@@ -425,7 +422,7 @@ const rows = await collectAllDataSourceRows(notion, {
425
422
 
426
423
  ### Custom requests
427
424
 
428
- To make requests directly to a Notion API endpoint instead of using the pre-built families of methods, call the `request()` method. For example:
425
+ `request()` calls a Notion API endpoint directly. For example:
429
426
 
430
427
  ```ts
431
428
  // POST /v1/comments
@@ -442,51 +439,57 @@ const response = await notion.request({
442
439
  console.log(JSON.stringify(response, null, 2))
443
440
  ```
444
441
 
445
- The `notion.request<ResponseBody>({...})` method is generic; `ResponseBody` represents the expected type of response object Notion returns for the endpoint you're calling (we don't validate this at runtime; you can pass anything!)
442
+ `notion.request<ResponseBody>({...})` uses `ResponseBody` as the expected response type. It does not validate that type at runtime.
446
443
 
447
444
  > [!TIP]
448
- > Usually, making custom requests with `notion.request()` isn't necessary, but can be helpful in some cases, e.g. when upgrading your [Notion API version](https://developers.notion.com/reference/versioning) incrementally before upgrading your SDK version. For example, if there's a new or renamed endpoint in the new API version that isn't yet available to call via a dedicated method on `Client`.
449
- >
450
- > In the above example, the simpler approach is to use `await notion.comments.create()`.
445
+ > Prefer a named SDK method when one exists, such as `notion.comments.create()` for this example. `request()` can call endpoints that the SDK does not yet cover.
451
446
 
452
- Another customization you can make is to pass your own `fetch` function to the `Client` constructor. This might be helpful for some execution environments where the default, built-in `fetch` isn't suitable.
447
+ The `Client` constructor also accepts a custom `fetch` function.
453
448
 
454
449
  ### Verifying webhook signatures
455
450
 
456
- If your integration receives [Notion webhook deliveries](https://developers.notion.com/reference/webhooks), use `verifyWebhookSignature` to confirm each request was sent by Notion and was not tampered with in transit. Notion signs every delivery with HMAC-SHA256 over the raw request body using the subscription's verification token, and places the result in the `X-Notion-Signature` header as `sha256=<hex>`.
451
+ `verifyWebhookSignature` checks the signature on a [Notion webhook event](https://developers.notion.com/reference/webhooks). It uses the raw request body, the subscription's verification token, and the `X-Notion-Signature` header. The header contains an HMAC-SHA256 signature in the form `sha256=<hex>`.
457
452
 
458
453
  ```ts
459
454
  import { verifyWebhookSignature } from "@notionhq/client"
460
455
 
461
- // Express example. The body must be read as the raw text/bytes that
462
- // arrived over the wire — JSON-parsed and re-serialized bodies will not
463
- // verify, because re-serialization changes whitespace and key order.
456
+ const verificationToken = process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN
457
+ if (!verificationToken) {
458
+ throw new Error("Missing webhook verification token")
459
+ }
460
+
461
+ // Express example. Use the raw body: parsing and re-serializing JSON
462
+ // can change its bytes and invalidate the signature.
464
463
  app.post(
465
464
  "/notion-webhook",
466
465
  express.text({ type: "application/json" }),
467
466
  async (req, res) => {
467
+ const body: unknown = req.body
468
+ if (typeof body !== "string") {
469
+ return res.status(400).send("expected a raw text body")
470
+ }
468
471
  const ok = await verifyWebhookSignature({
469
- body: req.body,
472
+ body,
470
473
  signature: req.header("x-notion-signature"),
471
- verificationToken: process.env.NOTION_WEBHOOK_VERIFICATION_TOKEN!,
474
+ verificationToken,
472
475
  })
473
476
  if (!ok) {
474
477
  return res.status(401).send("invalid signature")
475
478
  }
476
479
 
477
- const event = JSON.parse(req.body)
478
- // …handle the event
480
+ const event: unknown = JSON.parse(body)
481
+ // Validate the event's shape before using its fields.
479
482
  res.status(200).send("ok")
480
483
  }
481
484
  )
482
485
  ```
483
486
 
484
- The same helper handles the initial verification handshake that Notion sends when you first register a webhook URL — the handshake body (`{ "verification_token": "..." }`) is signed with the same token, so a single code path covers both cases.
487
+ Subscription setup is separate from event validation. The [webhook setup guide](https://developers.notion.com/reference/webhooks#step-2-verifying-the-subscription) covers receiving and saving the initial verification token.
485
488
 
486
- `signWebhookPayload({ body, verificationToken })` produces the same `sha256=<hex>` header value, which is useful for unit-testing your webhook handler without standing up a real subscription.
489
+ `signWebhookPayload({ body, verificationToken })` creates a signature for tests without a live subscription.
487
490
 
488
491
  > [!NOTE]
489
- > Both helpers are async. They prefer the Web Crypto API (`globalThis.crypto.subtle`) when present and transparently fall back to `node:crypto` on older Node.js 18 builds, so they run unchanged on Node.js, Bun, Deno, Vercel Edge Functions, Cloudflare Workers, and modern browsers.
492
+ > Both helpers are async. They use `globalThis.crypto.subtle` when available and fall back to the Web Crypto API in `node:crypto` on Node.js.
490
493
 
491
494
  ## Examples
492
495
 
@@ -499,9 +502,7 @@ This package supports the following minimum versions:
499
502
  - Runtime: `node >= 18`
500
503
  - Type definitions (optional): `typescript >= 5.9`
501
504
 
502
- Earlier versions may still work, but we encourage people building new applications to upgrade to the current stable.
503
-
504
- In some cases, due to backwards-incompatible changes across [Notion API versions](https://developers.notion.com/reference/versioning), more recent versions of this SDK don't work well with older API versions:
505
+ Older versions are not supported. SDK releases also have minimum recommended [Notion API versions](https://developers.notion.com/reference/versioning):
505
506
 
506
507
  | Version of JS/TS SDK | Minimum recommended API version |
507
508
  | -------------------- | ------------------------------- |
@@ -519,24 +520,24 @@ const notion = new Client({
519
520
 
520
521
  Key changes in `2026-03-11`:
521
522
 
522
- - **Block positioning**: The `after` parameter on `appendBlockChildren` is replaced by `position`, which supports `after_block`, `start`, and `end`.
523
- - **Trash status**: The `archived` field is replaced by `in_trash` on pages, blocks, databases, and data sources.
524
- - **Block type rename**: The `transcription` block type is renamed to `meeting_notes`.
523
+ - The `position` parameter replaces `after` on `appendBlockChildren`. It supports `after_block`, `start`, and `end`.
524
+ - The `in_trash` field replaces `archived` on pages, blocks, databases, and data sources.
525
+ - The `meeting_notes` block type replaces `transcription`.
525
526
 
526
- Both the old and new field names are available in the SDK's TypeScript types for a smooth migration. Fields from the older version are marked `@deprecated`.
527
+ The SDK's TypeScript types include both sets of field names. Older names are marked `@deprecated`.
527
528
 
528
- In these cases, we recommend upgrading your Notion API version header using the `Client()` constructor across all of your requests before upgrading to a newer version of the SDK.
529
+ The `notionVersion` constructor option sets the API version header for requests.
529
530
 
530
531
  ## Contributing
531
532
 
532
- While we value open-source contributions to this SDK, most of the client code is generated programmatically from the Notion API specification. Additions made directly to `src/api-endpoints.ts` or other generated code would be overwritten upon the next release.
533
+ Follow [the contribution guide](CONTRIBUTING.md) to build, test, and check SDK compatibility before opening a pull request.
533
534
 
534
- If you'd like to contribute a feature or fix to the SDK's core functionality, we suggest opening an issue first to discuss it with us. This helps ensure your effort aligns with how the SDK is maintained.
535
+ Endpoint code is generated from a schema in Notion's internal repo. Outside contributors cannot run that generator. Ask a maintainer for generated-code changes; direct edits will be overwritten. Open an issue to discuss changes to the SDK's core behavior first.
535
536
 
536
- However, contributions to documentation (including this README), examples, and bug reports are always welcome and greatly appreciated!
537
+ Docs, examples, and bug reports are welcome.
537
538
 
538
539
  ## Getting help
539
540
 
540
- If you want to submit a feature request for Notion's API, or are experiencing any issues with the API platform, please email us at `developers@makenotion.com`.
541
+ For API support, SDK problems, or feature requests, email `developers@makenotion.com`.
541
542
 
542
- To report issues with the SDK, it is possible to [submit an issue](https://github.com/makenotion/notion-sdk-js/issues) to this repo. However, we don't monitor these issues very closely. We recommend you reach out to us at `developers@makenotion.com` instead.
543
+ You can also [open an SDK issue](https://github.com/makenotion/notion-sdk-js/issues), but we do not monitor issues as closely as email.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/client",
3
- "version": "5.26.0",
3
+ "version": "5.27.0",
4
4
  "description": "A simple and easy to use client for the Notion API",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -26,8 +26,9 @@
26
26
  "prepublishOnly": "npm run checkLoggedIn && npm run lint && npm run test",
27
27
  "build": "tsc",
28
28
  "prettier": "prettier --write .",
29
- "lint": "prettier --check . && eslint . --ext .ts && cspell '**/*' ",
29
+ "lint": "prettier --check . && eslint . --ext .ts,.js && cspell '**/*'",
30
30
  "test": "jest ./test",
31
+ "check:compatibility": "node build/test/checkCompatibility.js",
31
32
  "check-links": "git ls-files | grep md$ | xargs -n 1 markdown-link-check",
32
33
  "prebuild": "npm run clean",
33
34
  "clean": "rm -rf ./build",
@@ -35,7 +36,7 @@
35
36
  },
36
37
  "lint-staged": {
37
38
  "*.{ts,js,json,md}": "prettier --write",
38
- "*.ts": "eslint --fix"
39
+ "*.{ts,js}": "eslint --fix"
39
40
  },
40
41
  "author": "",
41
42
  "license": "MIT",