This is page 2 of 2. Use http://codebase.md/makenotion/notion-mcp-server?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .dockerignore
├── .gitignore
├── docker-compose.yml
├── Dockerfile
├── docs
│ └── images
│ ├── connections.png
│ ├── integration-access.png
│ ├── integrations-capabilities.png
│ ├── integrations-creation.png
│ └── page-access-edit.png
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── scripts
│ ├── build-cli.js
│ ├── notion-openapi.json
│ └── start-server.ts
├── smithery.yaml
├── src
│ ├── init-server.ts
│ └── openapi-mcp-server
│ ├── auth
│ │ ├── index.ts
│ │ ├── template.ts
│ │ └── types.ts
│ ├── client
│ │ ├── __tests__
│ │ │ ├── http-client-upload.test.ts
│ │ │ ├── http-client.integration.test.ts
│ │ │ └── http-client.test.ts
│ │ ├── http-client.ts
│ │ └── polyfill-headers.ts
│ ├── index.ts
│ ├── mcp
│ │ ├── __tests__
│ │ │ └── proxy.test.ts
│ │ └── proxy.ts
│ ├── openapi
│ │ ├── __tests__
│ │ │ ├── file-upload.test.ts
│ │ │ ├── parser-multipart.test.ts
│ │ │ └── parser.test.ts
│ │ ├── file-upload.ts
│ │ └── parser.ts
│ └── README.md
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/src/openapi-mcp-server/openapi/__tests__/parser.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { OpenAPIToMCPConverter } from '../parser'
2 | import { OpenAPIV3 } from 'openapi-types'
3 | import { describe, expect, it } from 'vitest'
4 | import { JSONSchema7 as IJsonSchema } from 'json-schema'
5 |
6 | interface ToolMethod {
7 | name: string
8 | description: string
9 | inputSchema: any
10 | returnSchema?: any
11 | }
12 |
13 | interface Tool {
14 | methods: ToolMethod[]
15 | }
16 |
17 | interface Tools {
18 | [key: string]: Tool
19 | }
20 |
21 | // Helper function to verify tool method structure without checking the exact Zod schema
22 | function verifyToolMethod(actual: ToolMethod, expected: any, toolName: string) {
23 | expect(actual.name).toBe(expected.name)
24 | expect(actual.description).toBe(expected.description)
25 | expect(actual.inputSchema, `inputSchema ${actual.name} ${toolName}`).toEqual(expected.inputSchema)
26 | if (expected.returnSchema) {
27 | expect(actual.returnSchema, `returnSchema ${actual.name} ${toolName}`).toEqual(expected.returnSchema)
28 | }
29 | }
30 |
31 | // Helper function to verify tools structure
32 | function verifyTools(actual: Tools, expected: any) {
33 | expect(Object.keys(actual)).toEqual(Object.keys(expected))
34 | for (const [key, value] of Object.entries(actual)) {
35 | expect(value.methods.length).toBe(expected[key].methods.length)
36 | value.methods.forEach((method: ToolMethod, index: number) => {
37 | verifyToolMethod(method, expected[key].methods[index], key)
38 | })
39 | }
40 | }
41 |
42 | // A helper function to derive a type from a possibly complex schema.
43 | // If no explicit type is found, we assume 'object' for testing purposes.
44 | function getTypeFromSchema(schema: IJsonSchema): string {
45 | if (schema.type) {
46 | return Array.isArray(schema.type) ? schema.type[0] : schema.type
47 | } else if (schema.$ref) {
48 | // If there's a $ref, we treat it as an object reference.
49 | return 'object'
50 | } else if (schema.oneOf || schema.anyOf || schema.allOf) {
51 | // Complex schema combos - assume object for these tests.
52 | return 'object'
53 | }
54 | return 'object'
55 | }
56 |
57 | // Updated helper function to get parameters from inputSchema
58 | // Now handles $ref by treating it as an object reference without expecting properties.
59 | function getParamsFromSchema(method: { inputSchema: IJsonSchema }) {
60 | return Object.entries(method.inputSchema.properties || {}).map(([name, prop]) => {
61 | if (typeof prop === 'boolean') {
62 | throw new Error(`Boolean schema not supported for parameter ${name}`)
63 | }
64 |
65 | // If there's a $ref, treat it as an object reference.
66 | const schemaType = getTypeFromSchema(prop)
67 | return {
68 | name,
69 | type: schemaType,
70 | description: prop.description,
71 | optional: !(method.inputSchema.required || []).includes(name),
72 | }
73 | })
74 | }
75 |
76 | // Updated helper function to get return type from returnSchema
77 | // No longer requires that the schema be fully expanded. If we have a $ref, just note it as 'object'.
78 | function getReturnType(method: { returnSchema?: IJsonSchema }) {
79 | if (!method.returnSchema) return null
80 | const schema = method.returnSchema
81 | return {
82 | type: getTypeFromSchema(schema),
83 | description: schema.description,
84 | }
85 | }
86 |
87 | describe('OpenAPIToMCPConverter', () => {
88 | describe('Simple API Conversion', () => {
89 | const sampleSpec: OpenAPIV3.Document = {
90 | openapi: '3.0.0',
91 | info: {
92 | title: 'Test API',
93 | version: '1.0.0',
94 | },
95 | paths: {
96 | '/pets/{petId}': {
97 | get: {
98 | operationId: 'getPet',
99 | summary: 'Get a pet by ID',
100 | parameters: [
101 | {
102 | name: 'petId',
103 | in: 'path',
104 | required: true,
105 | description: 'The ID of the pet',
106 | schema: {
107 | type: 'integer',
108 | },
109 | },
110 | ],
111 | responses: {
112 | '200': {
113 | description: 'Pet found',
114 | content: {
115 | 'application/json': {
116 | schema: {
117 | type: 'object',
118 | properties: {
119 | id: { type: 'integer' },
120 | name: { type: 'string' },
121 | },
122 | },
123 | },
124 | },
125 | },
126 | },
127 | },
128 | },
129 | },
130 | }
131 |
132 | it('converts simple OpenAPI paths to MCP tools', () => {
133 | const converter = new OpenAPIToMCPConverter(sampleSpec)
134 | const { tools, openApiLookup } = converter.convertToMCPTools()
135 |
136 | expect(tools).toHaveProperty('API')
137 | expect(tools.API.methods).toHaveLength(1)
138 | expect(Object.keys(openApiLookup)).toHaveLength(1)
139 |
140 | const getPetMethod = tools.API.methods.find((m) => m.name === 'getPet')
141 | expect(getPetMethod).toBeDefined()
142 |
143 | const params = getParamsFromSchema(getPetMethod!)
144 | expect(params).toContainEqual({
145 | name: 'petId',
146 | type: 'integer',
147 | description: 'The ID of the pet',
148 | optional: false,
149 | })
150 | })
151 |
152 | it('truncates tool names exceeding 64 characters', () => {
153 | const longOperationId = 'a'.repeat(65)
154 | const specWithLongName: OpenAPIV3.Document = {
155 | openapi: '3.0.0',
156 | info: {
157 | title: 'Test API',
158 | version: '1.0.0'
159 | },
160 | paths: {
161 | '/pets/{petId}': {
162 | get: {
163 | operationId: longOperationId,
164 | summary: 'Get a pet by ID',
165 | parameters: [
166 | {
167 | name: 'petId',
168 | in: 'path',
169 | required: true,
170 | description: 'The ID of the pet',
171 | schema: {
172 | type: 'integer'
173 | }
174 | }
175 | ],
176 | responses: {
177 | '200': {
178 | description: 'Pet found',
179 | content: {
180 | 'application/json': {
181 | schema: {
182 | type: 'object',
183 | properties: {
184 | id: { type: 'integer' },
185 | name: { type: 'string' }
186 | }
187 | }
188 | }
189 | }
190 | }
191 | }
192 | }
193 | }
194 | }
195 | }
196 |
197 | const converter = new OpenAPIToMCPConverter(specWithLongName)
198 | const { tools } = converter.convertToMCPTools()
199 |
200 | const longNameMethod = tools.API.methods.find(m => m.name.startsWith('a'.repeat(59)))
201 | expect(longNameMethod).toBeDefined()
202 | expect(longNameMethod!.name.length).toBeLessThanOrEqual(64)
203 | })
204 | })
205 |
206 | describe('Complex API Conversion', () => {
207 | const complexSpec: OpenAPIV3.Document = {
208 | openapi: '3.0.0',
209 | info: { title: 'Complex API', version: '1.0.0' },
210 | components: {
211 | schemas: {
212 | Error: {
213 | type: 'object',
214 | required: ['code', 'message'],
215 | properties: {
216 | code: { type: 'integer' },
217 | message: { type: 'string' },
218 | },
219 | },
220 | Pet: {
221 | type: 'object',
222 | required: ['id', 'name'],
223 | properties: {
224 | id: { type: 'integer', description: 'The ID of the pet' },
225 | name: { type: 'string', description: 'The name of the pet' },
226 | category: { $ref: '#/components/schemas/Category', description: 'The category of the pet' },
227 | tags: {
228 | type: 'array',
229 | description: 'The tags of the pet',
230 | items: { $ref: '#/components/schemas/Tag' },
231 | },
232 | status: {
233 | type: 'string',
234 | description: 'The status of the pet',
235 | enum: ['available', 'pending', 'sold'],
236 | },
237 | },
238 | },
239 | Category: {
240 | type: 'object',
241 | required: ['id', 'name'],
242 | properties: {
243 | id: { type: 'integer' },
244 | name: { type: 'string' },
245 | subcategories: {
246 | type: 'array',
247 | items: { $ref: '#/components/schemas/Category' },
248 | },
249 | },
250 | },
251 | Tag: {
252 | type: 'object',
253 | required: ['id', 'name'],
254 | properties: {
255 | id: { type: 'integer' },
256 | name: { type: 'string' },
257 | },
258 | },
259 | },
260 | parameters: {
261 | PetId: {
262 | name: 'petId',
263 | in: 'path',
264 | required: true,
265 | description: 'ID of pet to fetch',
266 | schema: { type: 'integer' },
267 | },
268 | QueryLimit: {
269 | name: 'limit',
270 | in: 'query',
271 | description: 'Maximum number of results to return',
272 | schema: { type: 'integer', minimum: 1, maximum: 100, default: 20 },
273 | },
274 | },
275 | responses: {
276 | NotFound: {
277 | description: 'The specified resource was not found',
278 | content: {
279 | 'application/json': {
280 | schema: { $ref: '#/components/schemas/Error' },
281 | },
282 | },
283 | },
284 | },
285 | },
286 | paths: {
287 | '/pets': {
288 | get: {
289 | operationId: 'listPets',
290 | summary: 'List all pets',
291 | parameters: [{ $ref: '#/components/parameters/QueryLimit' }],
292 | responses: {
293 | '200': {
294 | description: 'A list of pets',
295 | content: {
296 | 'application/json': {
297 | schema: {
298 | type: 'array',
299 | items: { $ref: '#/components/schemas/Pet' },
300 | },
301 | },
302 | },
303 | },
304 | },
305 | },
306 | post: {
307 | operationId: 'createPet',
308 | summary: 'Create a pet',
309 | requestBody: {
310 | required: true,
311 | content: {
312 | 'application/json': {
313 | schema: { $ref: '#/components/schemas/Pet' },
314 | },
315 | },
316 | },
317 | responses: {
318 | '201': {
319 | description: 'Pet created',
320 | content: {
321 | 'application/json': {
322 | schema: { $ref: '#/components/schemas/Pet' },
323 | },
324 | },
325 | },
326 | },
327 | },
328 | },
329 | '/pets/{petId}': {
330 | get: {
331 | operationId: 'getPet',
332 | summary: 'Get a pet by ID',
333 | parameters: [{ $ref: '#/components/parameters/PetId' }],
334 | responses: {
335 | '200': {
336 | description: 'Pet found',
337 | content: {
338 | 'application/json': {
339 | schema: { $ref: '#/components/schemas/Pet' },
340 | },
341 | },
342 | },
343 | '404': {
344 | $ref: '#/components/responses/NotFound',
345 | },
346 | },
347 | },
348 | put: {
349 | operationId: 'updatePet',
350 | summary: 'Update a pet',
351 | parameters: [{ $ref: '#/components/parameters/PetId' }],
352 | requestBody: {
353 | required: true,
354 | content: {
355 | 'application/json': {
356 | schema: { $ref: '#/components/schemas/Pet' },
357 | },
358 | },
359 | },
360 | responses: {
361 | '200': {
362 | description: 'Pet updated',
363 | content: {
364 | 'application/json': {
365 | schema: { $ref: '#/components/schemas/Pet' },
366 | },
367 | },
368 | },
369 | '404': {
370 | $ref: '#/components/responses/NotFound',
371 | },
372 | },
373 | },
374 | },
375 | },
376 | }
377 |
378 | it('converts operations with referenced parameters', () => {
379 | const converter = new OpenAPIToMCPConverter(complexSpec)
380 | const { tools } = converter.convertToMCPTools()
381 |
382 | const getPetMethod = tools.API.methods.find((m) => m.name === 'getPet')
383 | expect(getPetMethod).toBeDefined()
384 | const params = getParamsFromSchema(getPetMethod!)
385 | expect(params).toContainEqual({
386 | name: 'petId',
387 | type: 'integer',
388 | description: 'ID of pet to fetch',
389 | optional: false,
390 | })
391 | })
392 |
393 | it('converts operations with query parameters', () => {
394 | const converter = new OpenAPIToMCPConverter(complexSpec)
395 | const { tools } = converter.convertToMCPTools()
396 |
397 | const listPetsMethod = tools.API.methods.find((m) => m.name === 'listPets')
398 | expect(listPetsMethod).toBeDefined()
399 |
400 | const params = getParamsFromSchema(listPetsMethod!)
401 | expect(params).toContainEqual({
402 | name: 'limit',
403 | type: 'integer',
404 | description: 'Maximum number of results to return',
405 | optional: true,
406 | })
407 | })
408 |
409 | it('converts operations with array responses', () => {
410 | const converter = new OpenAPIToMCPConverter(complexSpec)
411 | const { tools } = converter.convertToMCPTools()
412 |
413 | const listPetsMethod = tools.API.methods.find((m) => m.name === 'listPets')
414 | expect(listPetsMethod).toBeDefined()
415 |
416 | const returnType = getReturnType(listPetsMethod!)
417 | // Now we only check type since description might not be carried through
418 | // if we are not expanding schemas.
419 | expect(returnType).toMatchObject({
420 | type: 'array',
421 | })
422 | })
423 |
424 | it('converts operations with request bodies using $ref', () => {
425 | const converter = new OpenAPIToMCPConverter(complexSpec)
426 | const { tools } = converter.convertToMCPTools()
427 |
428 | const createPetMethod = tools.API.methods.find((m) => m.name === 'createPet')
429 | expect(createPetMethod).toBeDefined()
430 |
431 | const params = getParamsFromSchema(createPetMethod!)
432 | // Now that we are preserving $ref, the request body won't be expanded into multiple parameters.
433 | // Instead, we'll have a single "body" parameter referencing Pet.
434 | expect(params).toEqual(
435 | expect.arrayContaining([
436 | expect.objectContaining({
437 | name: 'body',
438 | type: 'object', // Because it's a $ref
439 | optional: false,
440 | }),
441 | ]),
442 | )
443 | })
444 |
445 | it('converts operations with referenced error responses', () => {
446 | const converter = new OpenAPIToMCPConverter(complexSpec)
447 | const { tools } = converter.convertToMCPTools()
448 |
449 | const getPetMethod = tools.API.methods.find((m) => m.name === 'getPet')
450 | expect(getPetMethod).toBeDefined()
451 |
452 | // We just check that the description includes the error references now.
453 | expect(getPetMethod?.description).toContain('404: The specified resource was not found')
454 | })
455 |
456 | it('handles recursive schema references without expanding them', () => {
457 | const converter = new OpenAPIToMCPConverter(complexSpec)
458 | const { tools } = converter.convertToMCPTools()
459 |
460 | const createPetMethod = tools.API.methods.find((m) => m.name === 'createPet')
461 | expect(createPetMethod).toBeDefined()
462 |
463 | const params = getParamsFromSchema(createPetMethod!)
464 | // Since "category" would be inside Pet, and we're not expanding,
465 | // we won't see 'category' directly. We only have 'body' as a reference.
466 | // Thus, the test no longer checks for a direct 'category' param.
467 | expect(params.find((p) => p.name === 'body')).toBeDefined()
468 | })
469 |
470 | it('converts all operations correctly respecting $ref usage', () => {
471 | const converter = new OpenAPIToMCPConverter(complexSpec)
472 | const { tools } = converter.convertToMCPTools()
473 |
474 | expect(tools.API.methods).toHaveLength(4)
475 |
476 | const methodNames = tools.API.methods.map((m) => m.name)
477 | expect(methodNames).toEqual(expect.arrayContaining(['listPets', 'createPet', 'getPet', 'updatePet']))
478 |
479 | tools.API.methods.forEach((method) => {
480 | expect(method).toHaveProperty('name')
481 | expect(method).toHaveProperty('description')
482 | expect(method).toHaveProperty('inputSchema')
483 | expect(method).toHaveProperty('returnSchema')
484 |
485 | // For 'get' operations, we just check the return type is recognized correctly.
486 | if (method.name.startsWith('get')) {
487 | const returnType = getReturnType(method)
488 | // With $ref usage, we can't guarantee description or direct expansion.
489 | expect(returnType?.type).toBe('object')
490 | }
491 | })
492 | })
493 | })
494 |
495 | describe('Complex Schema Conversion', () => {
496 | // A similar approach for the nested spec
497 | // Just as in the previous tests, we no longer test for direct property expansion.
498 | // We only confirm that parameters and return types are recognized and that references are preserved.
499 |
500 | const nestedSpec: OpenAPIV3.Document = {
501 | openapi: '3.0.0',
502 | info: { title: 'Nested API', version: '1.0.0' },
503 | components: {
504 | schemas: {
505 | Organization: {
506 | type: 'object',
507 | required: ['id', 'name'],
508 | properties: {
509 | id: { type: 'integer' },
510 | name: { type: 'string' },
511 | departments: {
512 | type: 'array',
513 | items: { $ref: '#/components/schemas/Department' },
514 | },
515 | metadata: { $ref: '#/components/schemas/Metadata' },
516 | },
517 | },
518 | Department: {
519 | type: 'object',
520 | required: ['id', 'name'],
521 | properties: {
522 | id: { type: 'integer' },
523 | name: { type: 'string' },
524 | employees: {
525 | type: 'array',
526 | items: { $ref: '#/components/schemas/Employee' },
527 | },
528 | subDepartments: {
529 | type: 'array',
530 | items: { $ref: '#/components/schemas/Department' },
531 | },
532 | metadata: { $ref: '#/components/schemas/Metadata' },
533 | },
534 | },
535 | Employee: {
536 | type: 'object',
537 | required: ['id', 'name'],
538 | properties: {
539 | id: { type: 'integer' },
540 | name: { type: 'string' },
541 | role: { $ref: '#/components/schemas/Role' },
542 | skills: {
543 | type: 'array',
544 | items: { $ref: '#/components/schemas/Skill' },
545 | },
546 | metadata: { $ref: '#/components/schemas/Metadata' },
547 | },
548 | },
549 | Role: {
550 | type: 'object',
551 | required: ['id', 'name'],
552 | properties: {
553 | id: { type: 'integer' },
554 | name: { type: 'string' },
555 | permissions: {
556 | type: 'array',
557 | items: { $ref: '#/components/schemas/Permission' },
558 | },
559 | },
560 | },
561 | Permission: {
562 | type: 'object',
563 | required: ['id', 'name'],
564 | properties: {
565 | id: { type: 'integer' },
566 | name: { type: 'string' },
567 | scope: { type: 'string' },
568 | },
569 | },
570 | Skill: {
571 | type: 'object',
572 | required: ['id', 'name'],
573 | properties: {
574 | id: { type: 'integer' },
575 | name: { type: 'string' },
576 | level: {
577 | type: 'string',
578 | enum: ['beginner', 'intermediate', 'expert'],
579 | },
580 | },
581 | },
582 | Metadata: {
583 | type: 'object',
584 | properties: {
585 | createdAt: { type: 'string', format: 'date-time' },
586 | updatedAt: { type: 'string', format: 'date-time' },
587 | tags: {
588 | type: 'array',
589 | items: { type: 'string' },
590 | },
591 | customFields: {
592 | type: 'object',
593 | additionalProperties: true,
594 | },
595 | },
596 | },
597 | },
598 | parameters: {
599 | OrgId: {
600 | name: 'orgId',
601 | in: 'path',
602 | required: true,
603 | description: 'Organization ID',
604 | schema: { type: 'integer' },
605 | },
606 | DeptId: {
607 | name: 'deptId',
608 | in: 'path',
609 | required: true,
610 | description: 'Department ID',
611 | schema: { type: 'integer' },
612 | },
613 | IncludeMetadata: {
614 | name: 'includeMetadata',
615 | in: 'query',
616 | description: 'Include metadata in response',
617 | schema: { type: 'boolean', default: false },
618 | },
619 | Depth: {
620 | name: 'depth',
621 | in: 'query',
622 | description: 'Depth of nested objects to return',
623 | schema: { type: 'integer', minimum: 1, maximum: 5, default: 1 },
624 | },
625 | },
626 | },
627 | paths: {
628 | '/organizations/{orgId}': {
629 | get: {
630 | operationId: 'getOrganization',
631 | summary: 'Get organization details',
632 | parameters: [
633 | { $ref: '#/components/parameters/OrgId' },
634 | { $ref: '#/components/parameters/IncludeMetadata' },
635 | { $ref: '#/components/parameters/Depth' },
636 | ],
637 | responses: {
638 | '200': {
639 | description: 'Organization details',
640 | content: {
641 | 'application/json': {
642 | schema: { $ref: '#/components/schemas/Organization' },
643 | },
644 | },
645 | },
646 | },
647 | },
648 | },
649 | '/organizations/{orgId}/departments/{deptId}': {
650 | get: {
651 | operationId: 'getDepartment',
652 | summary: 'Get department details',
653 | parameters: [
654 | { $ref: '#/components/parameters/OrgId' },
655 | { $ref: '#/components/parameters/DeptId' },
656 | { $ref: '#/components/parameters/IncludeMetadata' },
657 | { $ref: '#/components/parameters/Depth' },
658 | ],
659 | responses: {
660 | '200': {
661 | description: 'Department details',
662 | content: {
663 | 'application/json': {
664 | schema: { $ref: '#/components/schemas/Department' },
665 | },
666 | },
667 | },
668 | },
669 | },
670 | put: {
671 | operationId: 'updateDepartment',
672 | summary: 'Update department details',
673 | parameters: [{ $ref: '#/components/parameters/OrgId' }, { $ref: '#/components/parameters/DeptId' }],
674 | requestBody: {
675 | required: true,
676 | content: {
677 | 'application/json': {
678 | schema: { $ref: '#/components/schemas/Department' },
679 | },
680 | },
681 | },
682 | responses: {
683 | '200': {
684 | description: 'Department updated',
685 | content: {
686 | 'application/json': {
687 | schema: { $ref: '#/components/schemas/Department' },
688 | },
689 | },
690 | },
691 | },
692 | },
693 | },
694 | },
695 | }
696 |
697 | it('handles deeply nested object references', () => {
698 | const converter = new OpenAPIToMCPConverter(nestedSpec)
699 | const { tools } = converter.convertToMCPTools()
700 |
701 | const getOrgMethod = tools.API.methods.find((m) => m.name === 'getOrganization')
702 | expect(getOrgMethod).toBeDefined()
703 |
704 | const params = getParamsFromSchema(getOrgMethod!)
705 | expect(params).toEqual(
706 | expect.arrayContaining([
707 | expect.objectContaining({
708 | name: 'orgId',
709 | type: 'integer',
710 | description: 'Organization ID',
711 | optional: false,
712 | }),
713 | expect.objectContaining({
714 | name: 'includeMetadata',
715 | type: 'boolean',
716 | description: 'Include metadata in response',
717 | optional: true,
718 | }),
719 | expect.objectContaining({
720 | name: 'depth',
721 | type: 'integer',
722 | description: 'Depth of nested objects to return',
723 | optional: true,
724 | }),
725 | ]),
726 | )
727 | })
728 |
729 | it('handles recursive array references without requiring expansion', () => {
730 | const converter = new OpenAPIToMCPConverter(nestedSpec)
731 | const { tools } = converter.convertToMCPTools()
732 |
733 | const updateDeptMethod = tools.API.methods.find((m) => m.name === 'updateDepartment')
734 | expect(updateDeptMethod).toBeDefined()
735 |
736 | const params = getParamsFromSchema(updateDeptMethod!)
737 | // With $ref usage, we have a body parameter referencing Department.
738 | // The subDepartments array is inside Department, so we won't see it expanded here.
739 | // Instead, we just confirm 'body' is present.
740 | const bodyParam = params.find((p) => p.name === 'body')
741 | expect(bodyParam).toBeDefined()
742 | expect(bodyParam?.type).toBe('object')
743 | })
744 |
745 | it('handles complex nested object hierarchies without expansion', () => {
746 | const converter = new OpenAPIToMCPConverter(nestedSpec)
747 | const { tools } = converter.convertToMCPTools()
748 |
749 | const getDeptMethod = tools.API.methods.find((m) => m.name === 'getDepartment')
750 | expect(getDeptMethod).toBeDefined()
751 |
752 | const params = getParamsFromSchema(getDeptMethod!)
753 | // Just checking top-level params:
754 | expect(params).toEqual(
755 | expect.arrayContaining([
756 | expect.objectContaining({
757 | name: 'orgId',
758 | type: 'integer',
759 | optional: false,
760 | }),
761 | expect.objectContaining({
762 | name: 'deptId',
763 | type: 'integer',
764 | optional: false,
765 | }),
766 | expect.objectContaining({
767 | name: 'includeMetadata',
768 | type: 'boolean',
769 | optional: true,
770 | }),
771 | expect.objectContaining({
772 | name: 'depth',
773 | type: 'integer',
774 | optional: true,
775 | }),
776 | ]),
777 | )
778 | })
779 |
780 | it('handles schema with mixed primitive and reference types without expansion', () => {
781 | const converter = new OpenAPIToMCPConverter(nestedSpec)
782 | const { tools } = converter.convertToMCPTools()
783 |
784 | const updateDeptMethod = tools.API.methods.find((m) => m.name === 'updateDepartment')
785 | expect(updateDeptMethod).toBeDefined()
786 |
787 | const params = getParamsFromSchema(updateDeptMethod!)
788 | // Since we are not expanding, we won't see metadata fields directly.
789 | // We just confirm 'body' referencing Department is there.
790 | expect(params.find((p) => p.name === 'body')).toBeDefined()
791 | })
792 |
793 | it('converts all operations with complex schemas correctly respecting $ref', () => {
794 | const converter = new OpenAPIToMCPConverter(nestedSpec)
795 | const { tools } = converter.convertToMCPTools()
796 |
797 | expect(tools.API.methods).toHaveLength(3)
798 |
799 | const methodNames = tools.API.methods.map((m) => m.name)
800 | expect(methodNames).toEqual(expect.arrayContaining(['getOrganization', 'getDepartment', 'updateDepartment']))
801 |
802 | tools.API.methods.forEach((method) => {
803 | expect(method).toHaveProperty('name')
804 | expect(method).toHaveProperty('description')
805 | expect(method).toHaveProperty('inputSchema')
806 | expect(method).toHaveProperty('returnSchema')
807 |
808 | // If it's a GET operation, check that return type is recognized.
809 | if (method.name.startsWith('get')) {
810 | const returnType = getReturnType(method)
811 | // Without expansion, just check type is recognized as object.
812 | expect(returnType).toMatchObject({
813 | type: 'object',
814 | })
815 | }
816 | })
817 | })
818 | })
819 |
820 | it('preserves description on $ref nodes', () => {
821 | const spec: OpenAPIV3.Document = {
822 | openapi: '3.0.0',
823 | info: { title: 'Test API', version: '1.0.0' },
824 | paths: {},
825 | components: {
826 | schemas: {
827 | TestSchema: {
828 | type: 'object',
829 | properties: {
830 | name: { type: 'string' },
831 | },
832 | },
833 | },
834 | },
835 | }
836 |
837 | const converter = new OpenAPIToMCPConverter(spec)
838 | const result = converter.convertOpenApiSchemaToJsonSchema(
839 | {
840 | $ref: '#/components/schemas/TestSchema',
841 | description: 'A schema description',
842 | },
843 | new Set(),
844 | )
845 |
846 | expect(result).toEqual({
847 | $ref: '#/$defs/TestSchema',
848 | description: 'A schema description',
849 | })
850 | })
851 | })
852 |
853 | // Additional complex test scenarios as a table test
854 | describe('OpenAPIToMCPConverter - Additional Complex Tests', () => {
855 | interface TestCase {
856 | name: string
857 | input: OpenAPIV3.Document
858 | expected: {
859 | tools: Record<
860 | string,
861 | {
862 | methods: Array<{
863 | name: string
864 | description: string
865 | inputSchema: IJsonSchema & { type: 'object' }
866 | returnSchema?: IJsonSchema
867 | }>
868 | }
869 | >
870 | openApiLookup: Record<string, OpenAPIV3.OperationObject & { method: string; path: string }>
871 | }
872 | }
873 |
874 | const cases: TestCase[] = [
875 | {
876 | name: 'Cyclic References with Full Descriptions',
877 | input: {
878 | openapi: '3.0.0',
879 | info: {
880 | title: 'Cyclic Test API',
881 | version: '1.0.0',
882 | },
883 | paths: {
884 | '/ab': {
885 | get: {
886 | operationId: 'getAB',
887 | summary: 'Get an A-B object',
888 | responses: {
889 | '200': {
890 | description: 'Returns an A object',
891 | content: {
892 | 'application/json': {
893 | schema: { $ref: '#/components/schemas/A' },
894 | },
895 | },
896 | },
897 | },
898 | },
899 | post: {
900 | operationId: 'createAB',
901 | summary: 'Create an A-B object',
902 | requestBody: {
903 | required: true,
904 | content: {
905 | 'application/json': {
906 | schema: {
907 | $ref: '#/components/schemas/A',
908 | description: 'A schema description',
909 | },
910 | },
911 | },
912 | },
913 | responses: {
914 | '201': {
915 | description: 'Created A object',
916 | content: {
917 | 'application/json': {
918 | schema: { $ref: '#/components/schemas/A' },
919 | },
920 | },
921 | },
922 | },
923 | },
924 | },
925 | },
926 | components: {
927 | schemas: {
928 | A: {
929 | type: 'object',
930 | description: 'A schema description',
931 | required: ['name', 'b'],
932 | properties: {
933 | name: {
934 | type: 'string',
935 | description: 'Name of A',
936 | },
937 | b: {
938 | $ref: '#/components/schemas/B',
939 | description: 'B property in A',
940 | },
941 | },
942 | },
943 | B: {
944 | type: 'object',
945 | description: 'B schema description',
946 | required: ['title', 'a'],
947 | properties: {
948 | title: {
949 | type: 'string',
950 | description: 'Title of B',
951 | },
952 | a: {
953 | $ref: '#/components/schemas/A',
954 | description: 'A property in B',
955 | },
956 | },
957 | },
958 | },
959 | },
960 | } as OpenAPIV3.Document,
961 | expected: {
962 | tools: {
963 | API: {
964 | methods: [
965 | {
966 | name: 'getAB',
967 | description: 'Get an A-B object',
968 | // Error responses might not be listed here since none are defined.
969 | // Just end the description with no Error Responses section.
970 | inputSchema: {
971 | type: 'object',
972 | properties: {},
973 | required: [],
974 | $defs: {
975 | A: {
976 | type: 'object',
977 | description: 'A schema description',
978 | additionalProperties: true,
979 | properties: {
980 | name: {
981 | type: 'string',
982 | description: 'Name of A',
983 | },
984 | b: {
985 | description: 'B property in A',
986 | $ref: '#/$defs/B',
987 | },
988 | },
989 | required: ['name', 'b'],
990 | },
991 | B: {
992 | type: 'object',
993 | description: 'B schema description',
994 | additionalProperties: true,
995 | properties: {
996 | title: {
997 | type: 'string',
998 | description: 'Title of B',
999 | },
1000 | a: {
1001 | description: 'A property in B',
1002 | $ref: '#/$defs/A',
1003 | },
1004 | },
1005 | required: ['title', 'a'],
1006 | },
1007 | },
1008 | },
1009 | returnSchema: {
1010 | $ref: '#/$defs/A',
1011 | description: 'Returns an A object',
1012 | $defs: {
1013 | A: {
1014 | type: 'object',
1015 | description: 'A schema description',
1016 | additionalProperties: true,
1017 | properties: {
1018 | name: {
1019 | type: 'string',
1020 | description: 'Name of A',
1021 | },
1022 | b: {
1023 | description: 'B property in A',
1024 | $ref: '#/$defs/B',
1025 | },
1026 | },
1027 | required: ['name', 'b'],
1028 | },
1029 | B: {
1030 | type: 'object',
1031 | description: 'B schema description',
1032 | additionalProperties: true,
1033 | properties: {
1034 | title: {
1035 | type: 'string',
1036 | description: 'Title of B',
1037 | },
1038 | a: {
1039 | description: 'A property in B',
1040 | $ref: '#/$defs/A',
1041 | },
1042 | },
1043 | required: ['title', 'a'],
1044 | },
1045 | },
1046 | },
1047 | },
1048 | {
1049 | name: 'createAB',
1050 | description: 'Create an A-B object',
1051 | inputSchema: {
1052 | type: 'object',
1053 | properties: {
1054 | // The requestBody references A. We keep it as a single body field with a $ref.
1055 | body: {
1056 | $ref: '#/$defs/A',
1057 | description: 'A schema description',
1058 | },
1059 | },
1060 | required: ['body'],
1061 |
1062 | $defs: {
1063 | A: {
1064 | type: 'object',
1065 | description: 'A schema description',
1066 | additionalProperties: true,
1067 | properties: {
1068 | name: {
1069 | type: 'string',
1070 | description: 'Name of A',
1071 | },
1072 | b: {
1073 | description: 'B property in A',
1074 | $ref: '#/$defs/B',
1075 | },
1076 | },
1077 | required: ['name', 'b'],
1078 | },
1079 | B: {
1080 | type: 'object',
1081 | description: 'B schema description',
1082 | additionalProperties: true,
1083 | properties: {
1084 | title: {
1085 | type: 'string',
1086 | description: 'Title of B',
1087 | },
1088 | a: {
1089 | description: 'A property in B',
1090 | $ref: '#/$defs/A',
1091 | },
1092 | },
1093 | required: ['title', 'a'],
1094 | },
1095 | },
1096 | },
1097 | returnSchema: {
1098 | $ref: '#/$defs/A',
1099 | description: 'Created A object',
1100 |
1101 | $defs: {
1102 | A: {
1103 | type: 'object',
1104 | description: 'A schema description',
1105 | additionalProperties: true,
1106 | properties: {
1107 | name: {
1108 | type: 'string',
1109 | description: 'Name of A',
1110 | },
1111 | b: {
1112 | description: 'B property in A',
1113 | $ref: '#/$defs/B',
1114 | },
1115 | },
1116 | required: ['name', 'b'],
1117 | },
1118 | B: {
1119 | type: 'object',
1120 | description: 'B schema description',
1121 | additionalProperties: true,
1122 | properties: {
1123 | title: {
1124 | type: 'string',
1125 | description: 'Title of B',
1126 | },
1127 | a: {
1128 | description: 'A property in B',
1129 | $ref: '#/$defs/A',
1130 | },
1131 | },
1132 | required: ['title', 'a'],
1133 | },
1134 | },
1135 | },
1136 | },
1137 | ],
1138 | },
1139 | },
1140 | openApiLookup: {
1141 | 'API-getAB': {
1142 | operationId: 'getAB',
1143 | summary: 'Get an A-B object',
1144 | responses: {
1145 | '200': {
1146 | description: 'Returns an A object',
1147 | content: {
1148 | 'application/json': {
1149 | schema: { $ref: '#/components/schemas/A' },
1150 | },
1151 | },
1152 | },
1153 | },
1154 | method: 'get',
1155 | path: '/ab',
1156 | },
1157 | 'API-createAB': {
1158 | operationId: 'createAB',
1159 | summary: 'Create an A-B object',
1160 | requestBody: {
1161 | required: true,
1162 | content: {
1163 | 'application/json': {
1164 | schema: {
1165 | $ref: '#/components/schemas/A',
1166 | description: 'A schema description',
1167 | },
1168 | },
1169 | },
1170 | },
1171 | responses: {
1172 | '201': {
1173 | description: 'Created A object',
1174 | content: {
1175 | 'application/json': {
1176 | schema: { $ref: '#/components/schemas/A' },
1177 | },
1178 | },
1179 | },
1180 | },
1181 | method: 'post',
1182 | path: '/ab',
1183 | },
1184 | },
1185 | },
1186 | },
1187 | {
1188 | name: 'allOf/oneOf References with Full Descriptions',
1189 | input: {
1190 | openapi: '3.0.0',
1191 | info: { title: 'Composed Schema API', version: '1.0.0' },
1192 | paths: {
1193 | '/composed': {
1194 | get: {
1195 | operationId: 'getComposed',
1196 | summary: 'Get a composed resource',
1197 | responses: {
1198 | '200': {
1199 | description: 'A composed object',
1200 | content: {
1201 | 'application/json': {
1202 | schema: { $ref: '#/components/schemas/C' },
1203 | },
1204 | },
1205 | },
1206 | },
1207 | },
1208 | },
1209 | },
1210 | components: {
1211 | schemas: {
1212 | Base: {
1213 | type: 'object',
1214 | description: 'Base schema description',
1215 | properties: {
1216 | baseName: {
1217 | type: 'string',
1218 | description: 'Name in the base schema',
1219 | },
1220 | },
1221 | },
1222 | D: {
1223 | type: 'object',
1224 | description: 'D schema description',
1225 | properties: {
1226 | dProp: {
1227 | type: 'integer',
1228 | description: 'D property integer',
1229 | },
1230 | },
1231 | },
1232 | E: {
1233 | type: 'object',
1234 | description: 'E schema description',
1235 | properties: {
1236 | choice: {
1237 | description: 'One of these choices',
1238 | oneOf: [
1239 | {
1240 | $ref: '#/components/schemas/F',
1241 | },
1242 | {
1243 | $ref: '#/components/schemas/G',
1244 | },
1245 | ],
1246 | },
1247 | },
1248 | },
1249 | F: {
1250 | type: 'object',
1251 | description: 'F schema description',
1252 | properties: {
1253 | fVal: {
1254 | type: 'boolean',
1255 | description: 'Boolean in F',
1256 | },
1257 | },
1258 | },
1259 | G: {
1260 | type: 'object',
1261 | description: 'G schema description',
1262 | properties: {
1263 | gVal: {
1264 | type: 'string',
1265 | description: 'String in G',
1266 | },
1267 | },
1268 | },
1269 | C: {
1270 | description: 'C schema description',
1271 | allOf: [{ $ref: '#/components/schemas/Base' }, { $ref: '#/components/schemas/D' }, { $ref: '#/components/schemas/E' }],
1272 | },
1273 | },
1274 | },
1275 | } as OpenAPIV3.Document,
1276 | expected: {
1277 | tools: {
1278 | API: {
1279 | methods: [
1280 | {
1281 | name: 'getComposed',
1282 | description: 'Get a composed resource',
1283 | inputSchema: {
1284 | type: 'object',
1285 | properties: {},
1286 | required: [],
1287 | $defs: {
1288 | Base: {
1289 | type: 'object',
1290 | description: 'Base schema description',
1291 | additionalProperties: true,
1292 | properties: {
1293 | baseName: {
1294 | type: 'string',
1295 | description: 'Name in the base schema',
1296 | },
1297 | },
1298 | },
1299 | C: {
1300 | description: 'C schema description',
1301 | allOf: [{ $ref: '#/$defs/Base' }, { $ref: '#/$defs/D' }, { $ref: '#/$defs/E' }],
1302 | },
1303 | D: {
1304 | type: 'object',
1305 | additionalProperties: true,
1306 | description: 'D schema description',
1307 | properties: {
1308 | dProp: {
1309 | type: 'integer',
1310 | description: 'D property integer',
1311 | },
1312 | },
1313 | },
1314 | E: {
1315 | type: 'object',
1316 | additionalProperties: true,
1317 | description: 'E schema description',
1318 | properties: {
1319 | choice: {
1320 | description: 'One of these choices',
1321 | oneOf: [{ $ref: '#/$defs/F' }, { $ref: '#/$defs/G' }],
1322 | },
1323 | },
1324 | },
1325 | F: {
1326 | type: 'object',
1327 | additionalProperties: true,
1328 | description: 'F schema description',
1329 | properties: {
1330 | fVal: {
1331 | type: 'boolean',
1332 | description: 'Boolean in F',
1333 | },
1334 | },
1335 | },
1336 | G: {
1337 | type: 'object',
1338 | additionalProperties: true,
1339 | description: 'G schema description',
1340 | properties: {
1341 | gVal: {
1342 | type: 'string',
1343 | description: 'String in G',
1344 | },
1345 | },
1346 | },
1347 | },
1348 | },
1349 | returnSchema: {
1350 | $ref: '#/$defs/C',
1351 | description: 'A composed object',
1352 | $defs: {
1353 | Base: {
1354 | type: 'object',
1355 | description: 'Base schema description',
1356 | additionalProperties: true,
1357 | properties: {
1358 | baseName: {
1359 | type: 'string',
1360 | description: 'Name in the base schema',
1361 | },
1362 | },
1363 | },
1364 | C: {
1365 | description: 'C schema description',
1366 | allOf: [{ $ref: '#/$defs/Base' }, { $ref: '#/$defs/D' }, { $ref: '#/$defs/E' }],
1367 | },
1368 | D: {
1369 | type: 'object',
1370 | additionalProperties: true,
1371 | description: 'D schema description',
1372 | properties: {
1373 | dProp: {
1374 | type: 'integer',
1375 | description: 'D property integer',
1376 | },
1377 | },
1378 | },
1379 | E: {
1380 | type: 'object',
1381 | additionalProperties: true,
1382 | description: 'E schema description',
1383 | properties: {
1384 | choice: {
1385 | description: 'One of these choices',
1386 | oneOf: [{ $ref: '#/$defs/F' }, { $ref: '#/$defs/G' }],
1387 | },
1388 | },
1389 | },
1390 | F: {
1391 | type: 'object',
1392 | additionalProperties: true,
1393 | description: 'F schema description',
1394 | properties: {
1395 | fVal: {
1396 | type: 'boolean',
1397 | description: 'Boolean in F',
1398 | },
1399 | },
1400 | },
1401 | G: {
1402 | type: 'object',
1403 | additionalProperties: true,
1404 | description: 'G schema description',
1405 | properties: {
1406 | gVal: {
1407 | type: 'string',
1408 | description: 'String in G',
1409 | },
1410 | },
1411 | },
1412 | },
1413 | },
1414 | },
1415 | ],
1416 | },
1417 | },
1418 | openApiLookup: {
1419 | 'API-getComposed': {
1420 | operationId: 'getComposed',
1421 | summary: 'Get a composed resource',
1422 | responses: {
1423 | '200': {
1424 | description: 'A composed object',
1425 | content: {
1426 | 'application/json': {
1427 | schema: { $ref: '#/components/schemas/C' },
1428 | },
1429 | },
1430 | },
1431 | },
1432 | method: 'get',
1433 | path: '/composed',
1434 | },
1435 | },
1436 | },
1437 | },
1438 | ]
1439 |
1440 | it.each(cases)('$name', ({ input, expected }) => {
1441 | const converter = new OpenAPIToMCPConverter(input)
1442 | const { tools, openApiLookup } = converter.convertToMCPTools()
1443 |
1444 | // Use the custom verification instead of direct equality
1445 | verifyTools(tools, expected.tools)
1446 | expect(openApiLookup).toEqual(expected.openApiLookup)
1447 | })
1448 | })
1449 |
```
--------------------------------------------------------------------------------
/scripts/notion-openapi.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "openapi": "3.1.0",
3 | "info": {
4 | "title": "Notion API",
5 | "version": "1"
6 | },
7 | "servers": [
8 | {
9 | "url": "https://api.notion.com"
10 | }
11 | ],
12 | "components": {
13 | "securitySchemes": {
14 | "bearerAuth": {
15 | "type": "http",
16 | "scheme": "bearer"
17 | },
18 | "basicAuth": {
19 | "type": "http",
20 | "scheme": "basic"
21 | }
22 | },
23 | "parameters": {},
24 | "schemas": {}
25 | },
26 | "security": [
27 | {
28 | "bearerAuth": []
29 | }
30 | ],
31 | "paths": {
32 | "/v1/users/{user_id}": {
33 | "get": {
34 | "summary": "Retrieve a user",
35 | "description": "",
36 | "operationId": "get-user",
37 | "parameters": [
38 | {
39 | "name": "user_id",
40 | "in": "path",
41 | "required": true,
42 | "schema": {
43 | "type": "string",
44 | "format": "uuid"
45 | }
46 | }
47 | ],
48 | "responses": {
49 | "200": {
50 | "description": "200",
51 | "content": {
52 | "application/json": {
53 | "examples": {
54 | "Result": {
55 | "value": "{\n \"object\": \"user\",\n \"id\": \"d40e767c-d7af-4b18-a86d-55c61f1e39a4\",\n \"type\": \"person\",\n\t\"person\": {\n\t\t\"email\": \"[email protected]\",\n\t},\n \"name\": \"Avocado Lovelace\",\n \"avatar_url\": \"https://secure.notion-static.com/e6a352a8-8381-44d0-a1dc-9ed80e62b53d.jpg\",\n}"
56 | }
57 | }
58 | }
59 | }
60 | },
61 | "400": {
62 | "description": "400",
63 | "content": {
64 | "application/json": {
65 | "examples": {
66 | "Result": {
67 | "value": "{}"
68 | }
69 | },
70 | "schema": {
71 | "type": "object",
72 | "properties": {}
73 | }
74 | }
75 | }
76 | }
77 | },
78 | "deprecated": false,
79 | "security": []
80 | }
81 | },
82 | "/v1/users": {
83 | "get": {
84 | "summary": "List all users",
85 | "operationId": "get-users",
86 | "parameters": [
87 | {
88 | "name": "start_cursor",
89 | "in": "query",
90 | "description": "If supplied, this endpoint will return a page of results starting after the cursor provided. If not supplied, this endpoint will return the first page of results.",
91 | "schema": {
92 | "type": "string"
93 | }
94 | },
95 | {
96 | "name": "page_size",
97 | "in": "query",
98 | "description": "The number of items from the full list desired in the response. Maximum: 100",
99 | "schema": {
100 | "type": "integer",
101 | "default": 100
102 | }
103 | }
104 | ],
105 | "responses": {
106 | "400": {
107 | "description": "400",
108 | "content": {
109 | "application/json": {
110 | "examples": {
111 | "Result": {
112 | "value": "{}"
113 | }
114 | },
115 | "schema": {
116 | "type": "object",
117 | "properties": {}
118 | }
119 | }
120 | }
121 | }
122 | },
123 | "deprecated": false
124 | }
125 | },
126 | "/v1/users/me": {
127 | "get": {
128 | "summary": "Retrieve your token's bot user",
129 | "description": "",
130 | "operationId": "get-self",
131 | "parameters": [],
132 | "responses": {
133 | "200": {
134 | "description": "200",
135 | "content": {
136 | "application/json": {
137 | "examples": {
138 | "Result": {
139 | "value": "{\n \"object\": \"user\",\n \"id\": \"16d84278-ab0e-484c-9bdd-b35da3bd8905\",\n \"name\": \"pied piper\",\n \"avatar_url\": null,\n \"type\": \"bot\",\n \"bot\": {\n \"owner\": {\n \"type\": \"user\",\n \"user\": {\n \"object\": \"user\",\n \"id\": \"5389a034-eb5c-47b5-8a9e-f79c99ef166c\",\n \"name\": \"christine makenotion\",\n \"avatar_url\": null,\n \"type\": \"person\",\n \"person\": {\n \"email\": \"[email protected]\"\n }\n }\n }\n }\n}"
140 | }
141 | },
142 | "schema": {
143 | "type": "object",
144 | "properties": {
145 | "object": {
146 | "type": "string",
147 | "example": "user"
148 | },
149 | "id": {
150 | "type": "string",
151 | "example": "16d84278-ab0e-484c-9bdd-b35da3bd8905"
152 | },
153 | "name": {
154 | "type": "string",
155 | "example": "pied piper"
156 | },
157 | "avatar_url": {},
158 | "type": {
159 | "type": "string",
160 | "example": "bot"
161 | },
162 | "bot": {
163 | "type": "object",
164 | "properties": {
165 | "owner": {
166 | "type": "object",
167 | "properties": {
168 | "type": {
169 | "type": "string",
170 | "example": "user"
171 | },
172 | "user": {
173 | "type": "object",
174 | "properties": {
175 | "object": {
176 | "type": "string",
177 | "example": "user"
178 | },
179 | "id": {
180 | "type": "string",
181 | "example": "5389a034-eb5c-47b5-8a9e-f79c99ef166c"
182 | },
183 | "name": {
184 | "type": "string",
185 | "example": "christine makenotion"
186 | },
187 | "avatar_url": {},
188 | "type": {
189 | "type": "string",
190 | "example": "person"
191 | },
192 | "person": {
193 | "type": "object",
194 | "properties": {
195 | "email": {
196 | "type": "string",
197 | "example": "[email protected]"
198 | }
199 | }
200 | }
201 | }
202 | }
203 | }
204 | }
205 | }
206 | }
207 | }
208 | }
209 | }
210 | }
211 | }
212 | },
213 | "deprecated": false,
214 | "security": []
215 | }
216 | },
217 | "/v1/databases/{database_id}/query": {
218 | "post": {
219 | "summary": "Query a database",
220 | "description": "",
221 | "operationId": "post-database-query",
222 | "parameters": [
223 | {
224 | "name": "database_id",
225 | "in": "path",
226 | "description": "Identifier for a Notion database.",
227 | "schema": {
228 | "type": "string"
229 | },
230 | "required": true
231 | },
232 | {
233 | "name": "filter_properties",
234 | "in": "query",
235 | "description": "A list of page property value IDs associated with the database. Use this param to limit the response to a specific page property value or values for pages that meet the `filter` criteria.",
236 | "schema": {
237 | "type": "array",
238 | "items": {
239 | "type": "string"
240 | }
241 | }
242 | }
243 | ],
244 | "requestBody": {
245 | "content": {
246 | "application/json": {
247 | "schema": {
248 | "type": "object",
249 | "properties": {
250 | "filter": {
251 | "type": "object",
252 | "description": "When supplied, limits which pages are returned based on the [filter conditions](ref:post-database-query-filter).",
253 | "or": {
254 | "type": "array",
255 | "items": {
256 | "type": "object",
257 | "properties": {
258 | "type": "object",
259 | "properties": {
260 | "property": {
261 | "type": "string"
262 | },
263 | "title": {
264 | "type": "object",
265 | "properties": {
266 | "equals": {
267 | "type": "string"
268 | },
269 | "does_not_equal": {
270 | "type": "string"
271 | },
272 | "contains": {
273 | "type": "string"
274 | },
275 | "does_not_contain": {
276 | "type": "string"
277 | },
278 | "starts_with": {
279 | "type": "string"
280 | },
281 | "ends_with": {
282 | "type": "string"
283 | }
284 | }
285 | },
286 | "rich_text": {
287 | "type": "object",
288 | "properties": {
289 | "equals": {
290 | "type": "string"
291 | },
292 | "does_not_equal": {
293 | "type": "string"
294 | },
295 | "contains": {
296 | "type": "string"
297 | },
298 | "does_not_contain": {
299 | "type": "string"
300 | },
301 | "starts_with": {
302 | "type": "string"
303 | },
304 | "ends_with": {
305 | "type": "string"
306 | }
307 | }
308 | },
309 | "url": {
310 | "type": "object",
311 | "properties": {
312 | "equals": {
313 | "type": "string"
314 | },
315 | "does_not_equal": {
316 | "type": "string"
317 | },
318 | "contains": {
319 | "type": "string"
320 | },
321 | "does_not_contain": {
322 | "type": "string"
323 | },
324 | "starts_with": {
325 | "type": "string"
326 | },
327 | "ends_with": {
328 | "type": "string"
329 | }
330 | }
331 | },
332 | "email": {
333 | "type": "object",
334 | "properties": {
335 | "equals": {
336 | "type": "string"
337 | },
338 | "does_not_equal": {
339 | "type": "string"
340 | },
341 | "contains": {
342 | "type": "string"
343 | },
344 | "does_not_contain": {
345 | "type": "string"
346 | },
347 | "starts_with": {
348 | "type": "string"
349 | },
350 | "ends_with": {
351 | "type": "string"
352 | }
353 | }
354 | },
355 | "phone_number": {
356 | "type": "object",
357 | "properties": {
358 | "equals": {
359 | "type": "string"
360 | },
361 | "does_not_equal": {
362 | "type": "string"
363 | },
364 | "contains": {
365 | "type": "string"
366 | },
367 | "does_not_contain": {
368 | "type": "string"
369 | },
370 | "starts_with": {
371 | "type": "string"
372 | },
373 | "ends_with": {
374 | "type": "string"
375 | }
376 | }
377 | },
378 | "number": {
379 | "type": "object",
380 | "properties": {
381 | "equals": {
382 | "type": "number"
383 | },
384 | "does_not_equal": {
385 | "type": "number"
386 | },
387 | "contains": {
388 | "type": "number"
389 | },
390 | "does_not_contain": {
391 | "type": "number"
392 | },
393 | "starts_with": {
394 | "type": "number"
395 | },
396 | "ends_with": {
397 | "type": "number"
398 | }
399 | }
400 | },
401 | "checkbox": {
402 | "type": "object",
403 | "properties": {
404 | "equals": {
405 | "type": "boolean"
406 | },
407 | "does_not_equal": {
408 | "type": "boolean"
409 | }
410 | }
411 | },
412 | "select": {
413 | "type": "object",
414 | "properties": {
415 | "equals": {
416 | "type": "string"
417 | },
418 | "does_not_equal": {
419 | "type": "string"
420 | }
421 | }
422 | },
423 | "multi_select": {
424 | "type": "object",
425 | "properties": {
426 | "contains": {
427 | "type": "string"
428 | },
429 | "does_not_contain": {
430 | "type": "string"
431 | }
432 | }
433 | },
434 | "status": {
435 | "type": "object",
436 | "properties": {
437 | "equals": {
438 | "type": "string"
439 | },
440 | "does_not_equal": {
441 | "type": "string"
442 | }
443 | }
444 | },
445 | "date": {
446 | "type": "object",
447 | "properties": {
448 | "equals": {
449 | "type": "string",
450 | "format": "date"
451 | },
452 | "before": {
453 | "type": "string",
454 | "format": "date"
455 | },
456 | "after": {
457 | "type": "string",
458 | "format": "date"
459 | },
460 | "on_or_before": {
461 | "type": "string",
462 | "format": "date"
463 | },
464 | "on_or_after": {
465 | "type": "string",
466 | "format": "date"
467 | }
468 | }
469 | },
470 | "created_time": {
471 | "type": "object",
472 | "properties": {
473 | "equals": {
474 | "type": "string",
475 | "format": "date"
476 | },
477 | "before": {
478 | "type": "string",
479 | "format": "date"
480 | },
481 | "after": {
482 | "type": "string",
483 | "format": "date"
484 | },
485 | "on_or_before": {
486 | "type": "string",
487 | "format": "date"
488 | },
489 | "on_or_after": {
490 | "type": "string",
491 | "format": "date"
492 | }
493 | }
494 | },
495 | "last_edited_time": {
496 | "type": "object",
497 | "properties": {
498 | "equals": {
499 | "type": "string",
500 | "format": "date"
501 | },
502 | "before": {
503 | "type": "string",
504 | "format": "date"
505 | },
506 | "after": {
507 | "type": "string",
508 | "format": "date"
509 | },
510 | "on_or_before": {
511 | "type": "string",
512 | "format": "date"
513 | },
514 | "on_or_after": {
515 | "type": "string",
516 | "format": "date"
517 | }
518 | }
519 | }
520 | }
521 | }
522 | },
523 | "maxItems": 100
524 | },
525 | "and": {
526 | "type": "array",
527 | "items": {
528 | "type": "object",
529 | "properties": {
530 | "type": "object",
531 | "properties": {
532 | "property": {
533 | "type": "string"
534 | },
535 | "title": {
536 | "type": "object",
537 | "properties": {
538 | "equals": {
539 | "type": "string"
540 | },
541 | "does_not_equal": {
542 | "type": "string"
543 | },
544 | "contains": {
545 | "type": "string"
546 | },
547 | "does_not_contain": {
548 | "type": "string"
549 | },
550 | "starts_with": {
551 | "type": "string"
552 | },
553 | "ends_with": {
554 | "type": "string"
555 | }
556 | }
557 | },
558 | "rich_text": {
559 | "type": "object",
560 | "properties": {
561 | "equals": {
562 | "type": "string"
563 | },
564 | "does_not_equal": {
565 | "type": "string"
566 | },
567 | "contains": {
568 | "type": "string"
569 | },
570 | "does_not_contain": {
571 | "type": "string"
572 | },
573 | "starts_with": {
574 | "type": "string"
575 | },
576 | "ends_with": {
577 | "type": "string"
578 | }
579 | }
580 | },
581 | "url": {
582 | "type": "object",
583 | "properties": {
584 | "equals": {
585 | "type": "string"
586 | },
587 | "does_not_equal": {
588 | "type": "string"
589 | },
590 | "contains": {
591 | "type": "string"
592 | },
593 | "does_not_contain": {
594 | "type": "string"
595 | },
596 | "starts_with": {
597 | "type": "string"
598 | },
599 | "ends_with": {
600 | "type": "string"
601 | }
602 | }
603 | },
604 | "email": {
605 | "type": "object",
606 | "properties": {
607 | "equals": {
608 | "type": "string"
609 | },
610 | "does_not_equal": {
611 | "type": "string"
612 | },
613 | "contains": {
614 | "type": "string"
615 | },
616 | "does_not_contain": {
617 | "type": "string"
618 | },
619 | "starts_with": {
620 | "type": "string"
621 | },
622 | "ends_with": {
623 | "type": "string"
624 | }
625 | }
626 | },
627 | "phone_number": {
628 | "type": "object",
629 | "properties": {
630 | "equals": {
631 | "type": "string"
632 | },
633 | "does_not_equal": {
634 | "type": "string"
635 | },
636 | "contains": {
637 | "type": "string"
638 | },
639 | "does_not_contain": {
640 | "type": "string"
641 | },
642 | "starts_with": {
643 | "type": "string"
644 | },
645 | "ends_with": {
646 | "type": "string"
647 | }
648 | }
649 | },
650 | "number": {
651 | "type": "object",
652 | "properties": {
653 | "equals": {
654 | "type": "number"
655 | },
656 | "does_not_equal": {
657 | "type": "number"
658 | },
659 | "contains": {
660 | "type": "number"
661 | },
662 | "does_not_contain": {
663 | "type": "number"
664 | },
665 | "starts_with": {
666 | "type": "number"
667 | },
668 | "ends_with": {
669 | "type": "number"
670 | }
671 | }
672 | },
673 | "checkbox": {
674 | "type": "object",
675 | "properties": {
676 | "equals": {
677 | "type": "boolean"
678 | },
679 | "does_not_equal": {
680 | "type": "boolean"
681 | }
682 | }
683 | },
684 | "select": {
685 | "type": "object",
686 | "properties": {
687 | "equals": {
688 | "type": "string"
689 | },
690 | "does_not_equal": {
691 | "type": "string"
692 | }
693 | }
694 | },
695 | "multi_select": {
696 | "type": "object",
697 | "properties": {
698 | "contains": {
699 | "type": "string"
700 | },
701 | "does_not_contain": {
702 | "type": "string"
703 | }
704 | }
705 | },
706 | "status": {
707 | "type": "object",
708 | "properties": {
709 | "equals": {
710 | "type": "string"
711 | },
712 | "does_not_equal": {
713 | "type": "string"
714 | }
715 | }
716 | },
717 | "date": {
718 | "type": "object",
719 | "properties": {
720 | "equals": {
721 | "type": "string",
722 | "format": "date"
723 | },
724 | "before": {
725 | "type": "string",
726 | "format": "date"
727 | },
728 | "after": {
729 | "type": "string",
730 | "format": "date"
731 | },
732 | "on_or_before": {
733 | "type": "string",
734 | "format": "date"
735 | },
736 | "on_or_after": {
737 | "type": "string",
738 | "format": "date"
739 | }
740 | }
741 | },
742 | "created_time": {
743 | "type": "object",
744 | "properties": {
745 | "equals": {
746 | "type": "string",
747 | "format": "date"
748 | },
749 | "before": {
750 | "type": "string",
751 | "format": "date"
752 | },
753 | "after": {
754 | "type": "string",
755 | "format": "date"
756 | },
757 | "on_or_before": {
758 | "type": "string",
759 | "format": "date"
760 | },
761 | "on_or_after": {
762 | "type": "string",
763 | "format": "date"
764 | }
765 | }
766 | },
767 | "last_edited_time": {
768 | "type": "object",
769 | "properties": {
770 | "equals": {
771 | "type": "string",
772 | "format": "date"
773 | },
774 | "before": {
775 | "type": "string",
776 | "format": "date"
777 | },
778 | "after": {
779 | "type": "string",
780 | "format": "date"
781 | },
782 | "on_or_before": {
783 | "type": "string",
784 | "format": "date"
785 | },
786 | "on_or_after": {
787 | "type": "string",
788 | "format": "date"
789 | }
790 | }
791 | }
792 | }
793 | }
794 | },
795 | "maxItems": 100
796 | }
797 | },
798 | "sorts": {
799 | "type": "array",
800 | "description": "When supplied, orders the results based on the provided [sort criteria](ref:post-database-query-sort).",
801 | "items": {
802 | "type": "object",
803 | "required": [
804 | "property",
805 | "direction"
806 | ],
807 | "properties": {
808 | "property": {
809 | "type": "string"
810 | },
811 | "direction": {
812 | "enum": [
813 | "ascending",
814 | "descending"
815 | ],
816 | "type": "string"
817 | }
818 | }
819 | }
820 | },
821 | "start_cursor": {
822 | "type": "string",
823 | "description": "When supplied, returns a page of results starting after the cursor provided. If not supplied, this endpoint will return the first page of results."
824 | },
825 | "page_size": {
826 | "type": "integer",
827 | "description": "The number of items from the full list desired in the response. Maximum: 100",
828 | "default": 100
829 | },
830 | "archived": {
831 | "type": "boolean"
832 | },
833 | "in_trash": {
834 | "type": "boolean"
835 | }
836 | }
837 | }
838 | }
839 | }
840 | },
841 | "responses": {},
842 | "deprecated": false,
843 | "security": []
844 | }
845 | },
846 | "/v1/search": {
847 | "post": {
848 | "summary": "Search by title",
849 | "description": "",
850 | "operationId": "post-search",
851 | "parameters": [],
852 | "requestBody": {
853 | "content": {
854 | "application/json": {
855 | "schema": {
856 | "type": "object",
857 | "properties": {
858 | "query": {
859 | "type": "string",
860 | "description": "The text that the API compares page and database titles against."
861 | },
862 | "sort": {
863 | "type": "object",
864 | "description": "A set of criteria, `direction` and `timestamp` keys, that orders the results. The **only** supported timestamp value is `\"last_edited_time\"`. Supported `direction` values are `\"ascending\"` and `\"descending\"`. If `sort` is not provided, then the most recently edited results are returned first.",
865 | "properties": {
866 | "direction": {
867 | "type": "string",
868 | "description": "The direction to sort. Possible values include `ascending` and `descending`."
869 | },
870 | "timestamp": {
871 | "type": "string",
872 | "description": "The name of the timestamp to sort against. Possible values include `last_edited_time`."
873 | }
874 | }
875 | },
876 | "filter": {
877 | "type": "object",
878 | "description": "A set of criteria, `value` and `property` keys, that limits the results to either only pages or only databases. Possible `value` values are `\"page\"` or `\"database\"`. The only supported `property` value is `\"object\"`.",
879 | "properties": {
880 | "value": {
881 | "type": "string",
882 | "description": "The value of the property to filter the results by. Possible values for object type include `page` or `database`. **Limitation**: Currently the only filter allowed is `object` which will filter by type of object (either `page` or `database`)"
883 | },
884 | "property": {
885 | "type": "string",
886 | "description": "The name of the property to filter by. Currently the only property you can filter by is the object type. Possible values include `object`. Limitation: Currently the only filter allowed is `object` which will filter by type of object (either `page` or `database`)"
887 | }
888 | }
889 | },
890 | "start_cursor": {
891 | "type": "string",
892 | "description": "A `cursor` value returned in a previous response that If supplied, limits the response to results starting after the `cursor`. If not supplied, then the first page of results is returned. Refer to [pagination](https://developers.notion.com/reference/intro#pagination) for more details."
893 | },
894 | "page_size": {
895 | "type": "integer",
896 | "description": "The number of items from the full list to include in the response. Maximum: `100`.",
897 | "default": 100,
898 | "format": "int32"
899 | }
900 | }
901 | }
902 | }
903 | }
904 | },
905 | "responses": {},
906 | "deprecated": false,
907 | "security": []
908 | }
909 | },
910 | "/v1/blocks/{block_id}/children": {
911 | "get": {
912 | "summary": "Retrieve block children",
913 | "description": "",
914 | "operationId": "get-block-children",
915 | "parameters": [
916 | {
917 | "name": "block_id",
918 | "in": "path",
919 | "description": "Identifier for a [block](ref:block)",
920 | "schema": {
921 | "type": "string"
922 | },
923 | "required": true
924 | },
925 | {
926 | "name": "start_cursor",
927 | "in": "query",
928 | "description": "If supplied, this endpoint will return a page of results starting after the cursor provided. If not supplied, this endpoint will return the first page of results.",
929 | "schema": {
930 | "type": "string"
931 | }
932 | },
933 | {
934 | "name": "page_size",
935 | "in": "query",
936 | "description": "The number of items from the full list desired in the response. Maximum: 100",
937 | "schema": {
938 | "type": "integer",
939 | "format": "int32",
940 | "default": 100
941 | }
942 | }
943 | ],
944 | "responses": {},
945 | "deprecated": false,
946 | "security": []
947 | },
948 | "patch": {
949 | "summary": "Append block children",
950 | "description": "",
951 | "operationId": "patch-block-children",
952 | "parameters": [
953 | {
954 | "name": "block_id",
955 | "in": "path",
956 | "description": "Identifier for a [block](ref:block). Also accepts a [page](ref:page) ID.",
957 | "schema": {
958 | "type": "string"
959 | },
960 | "required": true
961 | }
962 | ],
963 | "requestBody": {
964 | "content": {
965 | "application/json": {
966 | "schema": {
967 | "type": "object",
968 | "required": [
969 | "children"
970 | ],
971 | "properties": {
972 | "children": {
973 | "type": "array",
974 | "items": {
975 | "type": "object",
976 | "properties": {
977 | "paragraph": {
978 | "type": "object",
979 | "properties": {
980 | "rich_text": {
981 | "type": "array",
982 | "items": {
983 | "type": "object",
984 | "properties": {
985 | "text": {
986 | "type": "object",
987 | "properties": {
988 | "content": {
989 | "type": "string"
990 | },
991 | "link": {
992 | "type": [
993 | "object",
994 | "null"
995 | ],
996 | "properties": {
997 | "url": {
998 | "type": "string"
999 | }
1000 | },
1001 | "required": [
1002 | "url"
1003 | ]
1004 | }
1005 | },
1006 | "additionalProperties": false,
1007 | "required": [
1008 | "content"
1009 | ]
1010 | },
1011 | "type": {
1012 | "enum": [
1013 | "text"
1014 | ],
1015 | "type": "string"
1016 | }
1017 | },
1018 | "additionalProperties": false,
1019 | "required": [
1020 | "text"
1021 | ]
1022 | },
1023 | "maxItems": 100
1024 | }
1025 | },
1026 | "additionalProperties": false,
1027 | "required": [
1028 | "rich_text"
1029 | ]
1030 | },
1031 | "bulleted_list_item": {
1032 | "type": "object",
1033 | "properties": {
1034 | "rich_text": {
1035 | "type": "array",
1036 | "items": {
1037 | "type": "object",
1038 | "properties": {
1039 | "text": {
1040 | "type": "object",
1041 | "properties": {
1042 | "content": {
1043 | "type": "string"
1044 | },
1045 | "link": {
1046 | "type": [
1047 | "object",
1048 | "null"
1049 | ],
1050 | "properties": {
1051 | "url": {
1052 | "type": "string"
1053 | }
1054 | },
1055 | "required": [
1056 | "url"
1057 | ]
1058 | }
1059 | },
1060 | "additionalProperties": false,
1061 | "required": [
1062 | "content"
1063 | ]
1064 | },
1065 | "type": {
1066 | "enum": [
1067 | "text"
1068 | ],
1069 | "type": "string"
1070 | }
1071 | },
1072 | "additionalProperties": false,
1073 | "required": [
1074 | "text"
1075 | ]
1076 | },
1077 | "maxItems": 100
1078 | }
1079 | },
1080 | "additionalProperties": false,
1081 | "required": [
1082 | "rich_text"
1083 | ]
1084 | },
1085 | "type": {
1086 | "enum": [
1087 | "paragraph",
1088 | "bulleted_list_item"
1089 | ],
1090 | "type": "string"
1091 | }
1092 | },
1093 | "additionalProperties": false
1094 | },
1095 | "description": "Child content to append to a container block as an array of [block objects](ref:block)"
1096 | },
1097 | "after": {
1098 | "type": "string",
1099 | "description": "The ID of the existing block that the new block should be appended after."
1100 | }
1101 | }
1102 | }
1103 | }
1104 | }
1105 | },
1106 | "responses": {},
1107 | "deprecated": false,
1108 | "security": []
1109 | }
1110 | },
1111 | "/v1/blocks/{block_id}": {
1112 | "get": {
1113 | "summary": "Retrieve a block",
1114 | "description": "",
1115 | "operationId": "retrieve-a-block",
1116 | "parameters": [
1117 | {
1118 | "name": "block_id",
1119 | "in": "path",
1120 | "description": "Identifier for a Notion block",
1121 | "schema": {
1122 | "type": "string"
1123 | },
1124 | "required": true
1125 | }
1126 | ],
1127 | "responses": {},
1128 | "deprecated": false,
1129 | "security": []
1130 | },
1131 | "patch": {
1132 | "summary": "Update a block",
1133 | "description": "",
1134 | "operationId": "update-a-block",
1135 | "parameters": [
1136 | {
1137 | "name": "block_id",
1138 | "in": "path",
1139 | "description": "Identifier for a Notion block",
1140 | "schema": {
1141 | "type": "string"
1142 | },
1143 | "required": true
1144 | }
1145 | ],
1146 | "requestBody": {
1147 | "content": {
1148 | "application/json": {
1149 | "schema": {
1150 | "type": "object",
1151 | "properties": {
1152 | "type": {
1153 | "type": "object",
1154 | "description": "The [block object `type`](ref:block#block-object-keys) value with the properties to be updated. Currently only `text` (for supported block types) and `checked` (for `to_do` blocks) fields can be updated.",
1155 | "properties": {}
1156 | },
1157 | "archived": {
1158 | "type": "boolean",
1159 | "description": "Set to true to archive (delete) a block. Set to false to un-archive (restore) a block.",
1160 | "default": true
1161 | }
1162 | }
1163 | }
1164 | }
1165 | }
1166 | },
1167 | "responses": {},
1168 | "deprecated": false,
1169 | "security": []
1170 | },
1171 | "delete": {
1172 | "summary": "Delete a block",
1173 | "description": "",
1174 | "operationId": "delete-a-block",
1175 | "parameters": [
1176 | {
1177 | "name": "block_id",
1178 | "in": "path",
1179 | "description": "Identifier for a Notion block",
1180 | "schema": {
1181 | "type": "string"
1182 | },
1183 | "required": true
1184 | }
1185 | ],
1186 | "responses": {},
1187 | "deprecated": false,
1188 | "security": []
1189 | }
1190 | },
1191 | "/v1/pages/{page_id}": {
1192 | "get": {
1193 | "summary": "Retrieve a page",
1194 | "description": "",
1195 | "operationId": "retrieve-a-page",
1196 | "parameters": [
1197 | {
1198 | "name": "page_id",
1199 | "in": "path",
1200 | "description": "Identifier for a Notion page",
1201 | "schema": {
1202 | "type": "string"
1203 | },
1204 | "required": true
1205 | },
1206 | {
1207 | "name": "filter_properties",
1208 | "in": "query",
1209 | "description": "A list of page property value IDs associated with the page. Use this param to limit the response to a specific page property value or values. To retrieve multiple properties, specify each page property ID. For example: `?filter_properties=iAk8&filter_properties=b7dh`.",
1210 | "schema": {
1211 | "type": "string"
1212 | }
1213 | }
1214 | ],
1215 | "responses": {},
1216 | "deprecated": false,
1217 | "security": []
1218 | },
1219 | "patch": {
1220 | "summary": "Update page properties",
1221 | "description": "",
1222 | "operationId": "patch-page",
1223 | "parameters": [
1224 | {
1225 | "name": "page_id",
1226 | "in": "path",
1227 | "description": "The identifier for the Notion page to be updated.",
1228 | "schema": {
1229 | "type": "string"
1230 | },
1231 | "required": true
1232 | }
1233 | ],
1234 | "requestBody": {
1235 | "content": {
1236 | "application/json": {
1237 | "schema": {
1238 | "type": "object",
1239 | "properties": {
1240 | "properties": {
1241 | "description": "The property values to update for the page. The keys are the names or IDs of the property and the values are property values. If a page property ID is not included, then it is not changed.",
1242 | "type": "object",
1243 | "properties": {
1244 | "title": {
1245 | "type": "array",
1246 | "items": {
1247 | "type": "object",
1248 | "properties": {
1249 | "text": {
1250 | "type": "object",
1251 | "properties": {
1252 | "content": {
1253 | "type": "string"
1254 | },
1255 | "link": {
1256 | "type": [
1257 | "object",
1258 | "null"
1259 | ],
1260 | "properties": {
1261 | "url": {
1262 | "type": "string"
1263 | }
1264 | },
1265 | "required": [
1266 | "url"
1267 | ]
1268 | }
1269 | },
1270 | "additionalProperties": false,
1271 | "required": [
1272 | "content"
1273 | ]
1274 | },
1275 | "type": {
1276 | "enum": [
1277 | "text"
1278 | ],
1279 | "type": "string"
1280 | }
1281 | },
1282 | "additionalProperties": false,
1283 | "required": [
1284 | "text"
1285 | ]
1286 | },
1287 | "maxItems": 100
1288 | },
1289 | "type": {
1290 | "enum": [
1291 | "title"
1292 | ],
1293 | "type": "string"
1294 | }
1295 | },
1296 | "additionalProperties": false,
1297 | "required": [
1298 | "title"
1299 | ]
1300 | },
1301 | "in_trash": {
1302 | "type": "boolean",
1303 | "description": "Set to true to delete a block. Set to false to restore a block.",
1304 | "default": false
1305 | },
1306 | "archived": {
1307 | "type": "boolean"
1308 | },
1309 | "icon": {
1310 | "description": "A page icon for the page. Supported types are [external file object](https://developers.notion.com/reference/file-object) or [emoji object](https://developers.notion.com/reference/emoji-object).",
1311 | "type": "object",
1312 | "properties": {
1313 | "emoji": {
1314 | "type": "string"
1315 | }
1316 | },
1317 | "additionalProperties": false,
1318 | "required": [
1319 | "emoji"
1320 | ]
1321 | },
1322 | "cover": {
1323 | "type": "object",
1324 | "description": "A cover image for the page. Only [external file objects](https://developers.notion.com/reference/file-object) are supported.",
1325 | "properties": {
1326 | "external": {
1327 | "type": "object",
1328 | "properties": {
1329 | "url": {
1330 | "type": "string"
1331 | }
1332 | },
1333 | "additionalProperties": false,
1334 | "required": [
1335 | "url"
1336 | ]
1337 | },
1338 | "type": {
1339 | "enum": [
1340 | "external"
1341 | ],
1342 | "type": "string"
1343 | }
1344 | },
1345 | "required": [
1346 | "external"
1347 | ],
1348 | "additionalProperties": false
1349 | }
1350 | }
1351 | }
1352 | }
1353 | }
1354 | },
1355 | "responses": {},
1356 | "deprecated": false,
1357 | "security": []
1358 | }
1359 | },
1360 | "/v1/pages": {
1361 | "post": {
1362 | "summary": "Create a page",
1363 | "description": "",
1364 | "operationId": "post-page",
1365 | "parameters": [],
1366 | "requestBody": {
1367 | "content": {
1368 | "application/json": {
1369 | "schema": {
1370 | "type": "object",
1371 | "required": [
1372 | "parent",
1373 | "properties"
1374 | ],
1375 | "properties": {
1376 | "parent": {
1377 | "type": "object",
1378 | "properties": {
1379 | "page_id": {
1380 | "type": "string",
1381 | "format": "uuid"
1382 | }
1383 | },
1384 | "required": [
1385 | "page_id"
1386 | ]
1387 | },
1388 | "properties": {
1389 | "type": "object",
1390 | "properties": {
1391 | "title": {
1392 | "type": "array",
1393 | "items": {
1394 | "type": "object",
1395 | "required": [
1396 | "text"
1397 | ],
1398 | "properties": {
1399 | "text": {
1400 | "type": "object",
1401 | "required": [
1402 | "content"
1403 | ],
1404 | "properties": {
1405 | "content": {
1406 | "type": "string"
1407 | }
1408 | }
1409 | }
1410 | }
1411 | },
1412 | "maxItems": 100
1413 | },
1414 | "type": {
1415 | "enum": [
1416 | "title"
1417 | ],
1418 | "type": "string"
1419 | }
1420 | },
1421 | "additionalProperties": false,
1422 | "required": [
1423 | "title"
1424 | ]
1425 | },
1426 | "children": {
1427 | "type": "array",
1428 | "description": "The content to be rendered on the new page, represented as an array of [block objects](https://developers.notion.com/reference/block).",
1429 | "items": {
1430 | "type": "string"
1431 | }
1432 | },
1433 | "icon": {
1434 | "type": "string",
1435 | "description": "The icon of the new page. Either an [emoji object](https://developers.notion.com/reference/emoji-object) or an [external file object](https://developers.notion.com/reference/file-object)..",
1436 | "format": "json"
1437 | },
1438 | "cover": {
1439 | "type": "string",
1440 | "description": "The cover image of the new page, represented as a [file object](https://developers.notion.com/reference/file-object).",
1441 | "format": "json"
1442 | }
1443 | }
1444 | }
1445 | }
1446 | }
1447 | },
1448 | "responses": {},
1449 | "deprecated": false,
1450 | "security": []
1451 | }
1452 | },
1453 | "/v1/databases": {
1454 | "post": {
1455 | "summary": "Create a database",
1456 | "description": "",
1457 | "operationId": "create-a-database",
1458 | "parameters": [],
1459 | "requestBody": {
1460 | "content": {
1461 | "application/json": {
1462 | "schema": {
1463 | "type": "object",
1464 | "required": [
1465 | "parent",
1466 | "properties"
1467 | ],
1468 | "properties": {
1469 | "parent": {
1470 | "type": "object",
1471 | "properties": {
1472 | "type": {
1473 | "enum": [
1474 | "page_id"
1475 | ],
1476 | "type": "string"
1477 | },
1478 | "page_id": {
1479 | "type": "string",
1480 | "format": "uuid"
1481 | }
1482 | },
1483 | "required": [
1484 | "type",
1485 | "page_id"
1486 | ]
1487 | },
1488 | "properties": {
1489 | "type": "object",
1490 | "description": "Property schema of database. The keys are the names of properties as they appear in Notion and the values are [property schema objects](https://developers.notion.com/reference/property-schema-object).",
1491 | "additionalProperties": {
1492 | "oneOf": [
1493 | {
1494 | "type": "object",
1495 | "properties": {
1496 | "title": {
1497 | "type": "object",
1498 | "properties": {},
1499 | "additionalProperties": false
1500 | },
1501 | "description": {
1502 | "type": "string",
1503 | "maxLength": 280,
1504 | "minLength": 1
1505 | }
1506 | },
1507 | "additionalProperties": false,
1508 | "required": [
1509 | "title"
1510 | ]
1511 | }
1512 | ]
1513 | }
1514 | },
1515 | "title": {
1516 | "type": "array",
1517 | "items": {
1518 | "type": "object",
1519 | "required": [
1520 | "text"
1521 | ],
1522 | "properties": {
1523 | "text": {
1524 | "type": "object",
1525 | "properties": {
1526 | "content": {
1527 | "type": "string"
1528 | },
1529 | "link": {
1530 | "type": [
1531 | "object",
1532 | "null"
1533 | ],
1534 | "properties": {
1535 | "url": {
1536 | "type": "string"
1537 | }
1538 | },
1539 | "required": [
1540 | "url"
1541 | ]
1542 | }
1543 | },
1544 | "additionalProperties": false,
1545 | "required": [
1546 | "content"
1547 | ]
1548 | },
1549 | "type": {
1550 | "enum": [
1551 | "text"
1552 | ],
1553 | "type": "string"
1554 | }
1555 | },
1556 | "additionalProperties": false
1557 | },
1558 | "maxItems": 100
1559 | }
1560 | }
1561 | }
1562 | }
1563 | }
1564 | },
1565 | "responses": {
1566 | "200": {
1567 | "description": "200",
1568 | "content": {
1569 | "application/json": {
1570 | "examples": {
1571 | "Result": {
1572 | "value": "{\n \"object\": \"database\",\n \"id\": \"bc1211ca-e3f1-4939-ae34-5260b16f627c\",\n \"created_time\": \"2021-07-08T23:50:00.000Z\",\n \"last_edited_time\": \"2021-07-08T23:50:00.000Z\",\n \"icon\": {\n \"type\": \"emoji\",\n \"emoji\": \"🎉\"\n },\n \"cover\": {\n \"type\": \"external\",\n \"external\": {\n \"url\": \"https://website.domain/images/image.png\"\n }\n },\n \"url\": \"https://www.notion.so/bc1211cae3f14939ae34260b16f627c\",\n \"title\": [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"Grocery List\",\n \"link\": null\n },\n \"annotations\": {\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"code\": false,\n \"color\": \"default\"\n },\n \"plain_text\": \"Grocery List\",\n \"href\": null\n }\n ],\n \"properties\": {\n \"+1\": {\n \"id\": \"Wp%3DC\",\n \"name\": \"+1\",\n \"type\": \"people\",\n \"people\": {}\n },\n \"In stock\": {\n \"id\": \"fk%5EY\",\n \"name\": \"In stock\",\n \"type\": \"checkbox\",\n \"checkbox\": {}\n },\n \"Price\": {\n \"id\": \"evWq\",\n \"name\": \"Price\",\n \"type\": \"number\",\n \"number\": {\n \"format\": \"dollar\"\n }\n },\n \"Description\": {\n \"id\": \"V}lX\",\n \"name\": \"Description\",\n \"type\": \"rich_text\",\n \"rich_text\": {}\n },\n \"Last ordered\": {\n \"id\": \"eVnV\",\n \"name\": \"Last ordered\",\n \"type\": \"date\",\n \"date\": {}\n },\n \"Meals\": {\n \"id\": \"%7DWA~\",\n \"name\": \"Meals\",\n \"type\": \"relation\",\n \"relation\": {\n \"database_id\": \"668d797c-76fa-4934-9b05-ad288df2d136\",\n \"single_property\": {}\n }\n },\n \"Number of meals\": {\n \"id\": \"Z\\\\Eh\",\n \"name\": \"Number of meals\",\n \"type\": \"rollup\",\n \"rollup\": {\n \"rollup_property_name\": \"Name\",\n \"relation_property_name\": \"Meals\",\n \"rollup_property_id\": \"title\",\n \"relation_property_id\": \"mxp^\",\n \"function\": \"count\"\n }\n },\n \"Store availability\": {\n \"id\": \"s}Kq\",\n \"name\": \"Store availability\",\n \"type\": \"multi_select\",\n \"multi_select\": {\n \"options\": [\n {\n \"id\": \"cb79b393-d1c1-4528-b517-c450859de766\",\n \"name\": \"Duc Loi Market\",\n \"color\": \"blue\"\n },\n {\n \"id\": \"58aae162-75d4-403b-a793-3bc7308e4cd2\",\n \"name\": \"Rainbow Grocery\",\n \"color\": \"gray\"\n },\n {\n \"id\": \"22d0f199-babc-44ff-bd80-a9eae3e3fcbf\",\n \"name\": \"Nijiya Market\",\n \"color\": \"purple\"\n },\n {\n \"id\": \"0d069987-ffb0-4347-bde2-8e4068003dbc\",\n \"name\": \"Gus's Community Market\",\n \"color\": \"yellow\"\n }\n ]\n }\n },\n \"Photo\": {\n \"id\": \"yfiK\",\n \"name\": \"Photo\",\n \"type\": \"files\",\n \"files\": {}\n },\n \"Food group\": {\n \"id\": \"CM%3EH\",\n \"name\": \"Food group\",\n \"type\": \"select\",\n \"select\": {\n \"options\": [\n {\n \"id\": \"6d4523fa-88cb-4ffd-9364-1e39d0f4e566\",\n \"name\": \"🥦Vegetable\",\n \"color\": \"green\"\n },\n {\n \"id\": \"268d7e75-de8f-4c4b-8b9d-de0f97021833\",\n \"name\": \"🍎Fruit\",\n \"color\": \"red\"\n },\n {\n \"id\": \"1b234a00-dc97-489c-b987-829264cfdfef\",\n \"name\": \"💪Protein\",\n \"color\": \"yellow\"\n }\n ]\n }\n },\n \"Name\": {\n \"id\": \"title\",\n \"name\": \"Name\",\n \"type\": \"title\",\n \"title\": {}\n }\n },\n \"parent\": {\n \"type\": \"page_id\",\n \"page_id\": \"98ad959b-2b6a-4774-80ee-00246fb0ea9b\"\n },\n \"archived\": false\n}{\n \"object\": \"database\",\n \"id\": \"bc1211ca-e3f1-4939-ae34-5260b16f627c\",\n \"created_time\": \"2021-07-08T23:50:00.000Z\",\n \"last_edited_time\": \"2021-07-08T23:50:00.000Z\",\n \"icon\": {\n \"type\": \"emoji\",\n \"emoji\": \"🎉\"\n },\n \"cover\": {\n \"type\": \"external\",\n \"external\": {\n \"url\": \"https://website.domain/images/image.png\"\n }\n },\n \"url\": \"https://www.notion.so/bc1211cae3f14939ae34260b16f627c\",\n \"title\": [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"Grocery List\",\n \"link\": null\n },\n \"annotations\": {\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"code\": false,\n \"color\": \"default\"\n },\n \"plain_text\": \"Grocery List\",\n \"href\": null\n }\n ],\n \"properties\": {\n \"+1\": {\n \"id\": \"Wp%3DC\",\n \"name\": \"+1\",\n \"type\": \"people\",\n \"people\": {}\n },\n \"In stock\": {\n \"id\": \"fk%5EY\",\n \"name\": \"In stock\",\n \"type\": \"checkbox\",\n \"checkbox\": {}\n },\n \"Price\": {\n \"id\": \"evWq\",\n \"name\": \"Price\",\n \"type\": \"number\",\n \"number\": {\n \"format\": \"dollar\"\n }\n },\n \"Description\": {\n \"id\": \"V}lX\",\n \"name\": \"Description\",\n \"type\": \"rich_text\",\n \"rich_text\": {}\n },\n \"Last ordered\": {\n \"id\": \"eVnV\",\n \"name\": \"Last ordered\",\n \"type\": \"date\",\n \"date\": {}\n },\n \"Meals\": {\n \"id\": \"%7DWA~\",\n \"name\": \"Meals\",\n \"type\": \"relation\",\n \"relation\": {\n \"database_id\": \"668d797c-76fa-4934-9b05-ad288df2d136\",\n \"synced_property_name\": \"Related to Grocery List (Meals)\"\n }\n },\n \"Number of meals\": {\n \"id\": \"Z\\\\Eh\",\n \"name\": \"Number of meals\",\n \"type\": \"rollup\",\n \"rollup\": {\n \"rollup_property_name\": \"Name\",\n \"relation_property_name\": \"Meals\",\n \"rollup_property_id\": \"title\",\n \"relation_property_id\": \"mxp^\",\n \"function\": \"count\"\n }\n },\n \"Store availability\": {\n \"id\": \"s}Kq\",\n \"name\": \"Store availability\",\n \"type\": \"multi_select\",\n \"multi_select\": {\n \"options\": [\n {\n \"id\": \"cb79b393-d1c1-4528-b517-c450859de766\",\n \"name\": \"Duc Loi Market\",\n \"color\": \"blue\"\n },\n {\n \"id\": \"58aae162-75d4-403b-a793-3bc7308e4cd2\",\n \"name\": \"Rainbow Grocery\",\n \"color\": \"gray\"\n },\n {\n \"id\": \"22d0f199-babc-44ff-bd80-a9eae3e3fcbf\",\n \"name\": \"Nijiya Market\",\n \"color\": \"purple\"\n },\n {\n \"id\": \"0d069987-ffb0-4347-bde2-8e4068003dbc\",\n \"name\": \"Gus's Community Market\",\n \"color\": \"yellow\"\n }\n ]\n }\n },\n \"Photo\": {\n \"id\": \"yfiK\",\n \"name\": \"Photo\",\n \"type\": \"files\",\n \"files\": {}\n },\n \"Food group\": {\n \"id\": \"CM%3EH\",\n \"name\": \"Food group\",\n \"type\": \"select\",\n \"select\": {\n \"options\": [\n {\n \"id\": \"6d4523fa-88cb-4ffd-9364-1e39d0f4e566\",\n \"name\": \"🥦Vegetable\",\n \"color\": \"green\"\n },\n {\n \"id\": \"268d7e75-de8f-4c4b-8b9d-de0f97021833\",\n \"name\": \"🍎Fruit\",\n \"color\": \"red\"\n },\n {\n \"id\": \"1b234a00-dc97-489c-b987-829264cfdfef\",\n \"name\": \"💪Protein\",\n \"color\": \"yellow\"\n }\n ]\n }\n },\n \"Name\": {\n \"id\": \"title\",\n \"name\": \"Name\",\n \"type\": \"title\",\n \"title\": {}\n }\n },\n \"parent\": {\n \"type\": \"page_id\",\n \"page_id\": \"98ad959b-2b6a-4774-80ee-00246fb0ea9b\"\n },\n \"archived\": false,\n \"is_inline\": false\n}"
1573 | }
1574 | }
1575 | }
1576 | }
1577 | }
1578 | },
1579 | "deprecated": false,
1580 | "security": []
1581 | }
1582 | },
1583 | "/v1/databases/{database_id}": {
1584 | "patch": {
1585 | "summary": "Update a database",
1586 | "description": "",
1587 | "operationId": "update-a-database",
1588 | "parameters": [
1589 | {
1590 | "name": "database_id",
1591 | "in": "path",
1592 | "description": "identifier for a Notion database",
1593 | "schema": {
1594 | "type": "string"
1595 | },
1596 | "required": true
1597 | }
1598 | ],
1599 | "requestBody": {
1600 | "content": {
1601 | "application/json": {
1602 | "schema": {
1603 | "type": "object",
1604 | "properties": {
1605 | "title": {
1606 | "description": "An array of [rich text objects](https://developers.notion.com/reference/rich-text) that represents the title of the database that is displayed in the Notion UI. If omitted, then the database title remains unchanged.",
1607 | "type": "array",
1608 | "items": {
1609 | "type": "object",
1610 | "required": [
1611 | "text"
1612 | ],
1613 | "properties": {
1614 | "text": {
1615 | "type": "object",
1616 | "properties": {
1617 | "content": {
1618 | "type": "string"
1619 | },
1620 | "link": {
1621 | "type": [
1622 | "object",
1623 | "null"
1624 | ],
1625 | "properties": {
1626 | "url": {
1627 | "type": "string"
1628 | }
1629 | },
1630 | "required": [
1631 | "url"
1632 | ]
1633 | }
1634 | },
1635 | "additionalProperties": false,
1636 | "required": [
1637 | "content"
1638 | ]
1639 | },
1640 | "type": {
1641 | "enum": [
1642 | "text"
1643 | ],
1644 | "type": "string"
1645 | }
1646 | },
1647 | "additionalProperties": false
1648 | }
1649 | },
1650 | "description": {
1651 | "type": "array",
1652 | "items": {
1653 | "type": "object",
1654 | "required": [
1655 | "text"
1656 | ],
1657 | "properties": {
1658 | "text": {
1659 | "type": "object",
1660 | "properties": {
1661 | "content": {
1662 | "type": "string"
1663 | },
1664 | "link": {
1665 | "type": [
1666 | "object",
1667 | "null"
1668 | ],
1669 | "properties": {
1670 | "url": {
1671 | "type": "string"
1672 | }
1673 | },
1674 | "required": [
1675 | "url"
1676 | ]
1677 | }
1678 | },
1679 | "additionalProperties": false,
1680 | "required": [
1681 | "content"
1682 | ]
1683 | },
1684 | "type": {
1685 | "enum": [
1686 | "text"
1687 | ],
1688 | "type": "string"
1689 | }
1690 | },
1691 | "additionalProperties": false
1692 | },
1693 | "maxItems": 100,
1694 | "description": "An array of [rich text objects](https://developers.notion.com/reference/rich-text) that represents the description of the database that is displayed in the Notion UI. If omitted, then the database description remains unchanged."
1695 | },
1696 | "properties": {
1697 | "type": "object",
1698 | "description": "Property schema of database. The keys are the names of properties as they appear in Notion and the values are [property schema objects](https://developers.notion.com/reference/property-schema-object).",
1699 | "properties": {
1700 | "name": {
1701 | "type": "string"
1702 | }
1703 | }
1704 | }
1705 | },
1706 | "additionalProperties": false
1707 | }
1708 | }
1709 | }
1710 | },
1711 | "responses": {},
1712 | "deprecated": false,
1713 | "security": []
1714 | },
1715 | "get": {
1716 | "summary": "Retrieve a database",
1717 | "description": "",
1718 | "operationId": "retrieve-a-database",
1719 | "parameters": [
1720 | {
1721 | "name": "database_id",
1722 | "in": "path",
1723 | "description": "An identifier for the Notion database.",
1724 | "schema": {
1725 | "type": "string"
1726 | },
1727 | "required": true
1728 | }
1729 | ],
1730 | "responses": {
1731 | "200": {
1732 | "description": "200",
1733 | "content": {
1734 | "application/json": {
1735 | "examples": {
1736 | "Result": {
1737 | "value": "{\n \"object\": \"database\",\n \"id\": \"bc1211ca-e3f1-4939-ae34-5260b16f627c\",\n \"created_time\": \"2021-07-08T23:50:00.000Z\",\n \"last_edited_time\": \"2021-07-08T23:50:00.000Z\",\n \"icon\": {\n \"type\": \"emoji\",\n \"emoji\": \"🎉\"\n },\n \"cover\": {\n \"type\": \"external\",\n \"external\": {\n \"url\": \"https://website.domain/images/image.png\"\n }\n },\n \"url\": \"https://www.notion.so/bc1211cae3f14939ae34260b16f627c\",\n \"title\": [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"Grocery List\",\n \"link\": null\n },\n \"annotations\": {\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"code\": false,\n \"color\": \"default\"\n },\n \"plain_text\": \"Grocery List\",\n \"href\": null\n }\n ],\n \"description\": [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"Grocery list for just kale 🥬\",\n \"link\": null\n },\n \"annotations\": {\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"code\": false,\n \"color\": \"default\"\n },\n \"plain_text\": \"Grocery list for just kale 🥬\",\n \"href\": null\n }\n ],\n \"properties\": {\n \"+1\": {\n \"id\": \"Wp%3DC\",\n \"name\": \"+1\",\n \"type\": \"people\",\n \"people\": {}\n },\n \"In stock\": {\n \"id\": \"fk%5EY\",\n \"name\": \"In stock\",\n \"type\": \"checkbox\",\n \"checkbox\": {}\n },\n \"Price\": {\n \"id\": \"evWq\",\n \"name\": \"Price\",\n \"type\": \"number\",\n \"number\": {\n \"format\": \"dollar\"\n }\n },\n \"Description\": {\n \"id\": \"V}lX\",\n \"name\": \"Description\",\n \"type\": \"rich_text\",\n \"rich_text\": {}\n },\n \"Last ordered\": {\n \"id\": \"eVnV\",\n \"name\": \"Last ordered\",\n \"type\": \"date\",\n \"date\": {}\n },\n \"Meals\": {\n \"id\": \"%7DWA~\",\n \"name\": \"Meals\",\n \"type\": \"relation\",\n \"relation\": {\n \"database_id\": \"668d797c-76fa-4934-9b05-ad288df2d136\",\n \"synced_property_name\": \"Related to Grocery List (Meals)\"\n }\n },\n \"Number of meals\": {\n \"id\": \"Z\\\\Eh\",\n \"name\": \"Number of meals\",\n \"type\": \"rollup\",\n \"rollup\": {\n \"rollup_property_name\": \"Name\",\n \"relation_property_name\": \"Meals\",\n \"rollup_property_id\": \"title\",\n \"relation_property_id\": \"mxp^\",\n \"function\": \"count\"\n }\n },\n \"Store availability\": {\n \"id\": \"s}Kq\",\n \"name\": \"Store availability\",\n \"type\": \"multi_select\",\n \"multi_select\": {\n \"options\": [\n {\n \"id\": \"cb79b393-d1c1-4528-b517-c450859de766\",\n \"name\": \"Duc Loi Market\",\n \"color\": \"blue\"\n },\n {\n \"id\": \"58aae162-75d4-403b-a793-3bc7308e4cd2\",\n \"name\": \"Rainbow Grocery\",\n \"color\": \"gray\"\n },\n {\n \"id\": \"22d0f199-babc-44ff-bd80-a9eae3e3fcbf\",\n \"name\": \"Nijiya Market\",\n \"color\": \"purple\"\n },\n {\n \"id\": \"0d069987-ffb0-4347-bde2-8e4068003dbc\",\n \"name\": \"Gus's Community Market\",\n \"color\": \"yellow\"\n }\n ]\n }\n },\n \"Photo\": {\n \"id\": \"yfiK\",\n \"name\": \"Photo\",\n \"type\": \"files\",\n \"files\": {}\n },\n \"Food group\": {\n \"id\": \"CM%3EH\",\n \"name\": \"Food group\",\n \"type\": \"select\",\n \"select\": {\n \"options\": [\n {\n \"id\": \"6d4523fa-88cb-4ffd-9364-1e39d0f4e566\",\n \"name\": \"🥦Vegetable\",\n \"color\": \"green\"\n },\n {\n \"id\": \"268d7e75-de8f-4c4b-8b9d-de0f97021833\",\n \"name\": \"🍎Fruit\",\n \"color\": \"red\"\n },\n {\n \"id\": \"1b234a00-dc97-489c-b987-829264cfdfef\",\n \"name\": \"💪Protein\",\n \"color\": \"yellow\"\n }\n ]\n }\n },\n \"Name\": {\n \"id\": \"title\",\n \"name\": \"Name\",\n \"type\": \"title\",\n \"title\": {}\n }\n },\n \"parent\": {\n \"type\": \"page_id\",\n \"page_id\": \"98ad959b-2b6a-4774-80ee-00246fb0ea9b\"\n },\n \"archived\": false,\n \"is_inline\": false,\n \"public_url\": null\n}"
1738 | }
1739 | }
1740 | }
1741 | }
1742 | }
1743 | },
1744 | "deprecated": false,
1745 | "security": []
1746 | }
1747 | },
1748 | "/v1/pages/{page_id}/properties/{property_id}": {
1749 | "get": {
1750 | "summary": "Retrieve a page property item",
1751 | "description": "",
1752 | "operationId": "retrieve-a-page-property",
1753 | "parameters": [
1754 | {
1755 | "name": "page_id",
1756 | "in": "path",
1757 | "description": "Identifier for a Notion page",
1758 | "schema": {
1759 | "type": "string"
1760 | },
1761 | "required": true
1762 | },
1763 | {
1764 | "name": "property_id",
1765 | "in": "path",
1766 | "description": "Identifier for a page [property](https://developers.notion.com/reference/page#all-property-values)",
1767 | "schema": {
1768 | "type": "string"
1769 | },
1770 | "required": true
1771 | },
1772 | {
1773 | "name": "page_size",
1774 | "in": "query",
1775 | "description": "For paginated properties. The max number of property item objects on a page. The default size is 100",
1776 | "schema": {
1777 | "type": "integer",
1778 | "format": "int32"
1779 | }
1780 | },
1781 | {
1782 | "name": "start_cursor",
1783 | "in": "query",
1784 | "description": "For paginated properties.",
1785 | "schema": {
1786 | "type": "string"
1787 | }
1788 | }
1789 | ],
1790 | "responses": {
1791 | "200": {
1792 | "description": "200",
1793 | "content": {
1794 | "application/json": {
1795 | "examples": {
1796 | "Number Property Item": {
1797 | "value": "{\n \"object\": \"property_item\",\n \"id\" \"kjPO\",\n \"type\": \"number\",\n \"number\": 2\n}"
1798 | },
1799 | "Result": {
1800 | "value": "{\n \"object\": \"list\",\n \"results\": [\n {\n \"object\": \"property_item\",\n \"id\" \"kjPO\",\n \"type\": \"rich_text\",\n \"rich_text\": {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"Avocado \",\n \"link\": null\n },\n \"annotations\": {\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"code\": false,\n \"color\": \"default\"\n },\n \"plain_text\": \"Avocado \",\n \"href\": null\n }\n },\n {\n \"object\": \"property_item\",\n \"id\" \"ijPO\",\n \"type\": \"rich_text\",\n \"rich_text\": {\n \"type\": \"mention\",\n \"mention\": {\n \"type\": \"page\",\n \"page\": {\n \"id\": \"41117fd7-69a5-4694-bc07-c1e3a682c857\"\n }\n },\n \"annotations\": {\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"code\": false,\n \"color\": \"default\"\n },\n \"plain_text\": \"Lemons\",\n \"href\": \"http://notion.so/41117fd769a54694bc07c1e3a682c857\"\n }\n },\n {\n \"object\": \"property_item\",\n \"id\" \"kjPO\",\n \"type\": \"rich_text\",\n \"rich_text\": {\n \"type\": \"text\",\n \"text\": {\n \"content\": \" Tomato \",\n \"link\": null\n },\n \"annotations\": {\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"code\": false,\n \"color\": \"default\"\n },\n \"plain_text\": \" Tomato \",\n \"href\": null\n }\n },\n...\n ],\n \"next_cursor\": \"some-next-cursor-value\",\n \"has_more\": true,\n\t\t\"next_url\": \"http://api.notion.com/v1/pages/0e5235bf86aa4efb93aa772cce7eab71/properties/NVv^?start_cursor=some-next-cursor-value&page_size=25\",\n \"property_item\": {\n \"id\": \"NVv^\",\n \"next_url\": null,\n \"type\": \"rich_text\",\n \"rich_text\": {}\n }\n}"
1801 | },
1802 | "Rollup List Property Item": {
1803 | "value": "{\n \"object\": \"list\",\n \"results\": [\n {\n \"object\": \"property_item\",\n \t\"id\": \"dj2l\",\n \"type\": \"relation\",\n \"relation\": {\n \"id\": \"83f92c9d-523d-466e-8c1f-9bc2c25a99fe\"\n }\n },\n {\n \"object\": \"property_item\",\n \t\"id\": \"dj2l\",\n \"type\": \"relation\",\n \"relation\": {\n \"id\": \"45cfb825-3463-4891-8932-7e6d8c170630\"\n }\n },\n {\n \"object\": \"property_item\",\n \t\"id\": \"dj2l\",\n \"type\": \"relation\",\n \"relation\": {\n \"id\": \"1688be1a-a197-4f2a-9688-e528c4b56d94\"\n }\n }\n ],\n \"next_cursor\": \"some-next-cursor-value\",\n \"has_more\": true,\n\t\t\"property_item\": {\n \"id\": \"y}~p\",\n \"next_url\": \"http://api.notion.com/v1/pages/0e5235bf86aa4efb93aa772cce7eab71/properties/y%7D~p?start_cursor=1QaTunT5&page_size=25\",\n \"type\": \"rollup\",\n \"rollup\": {\n \"function\": \"sum\",\n \"type\": \"incomplete\",\n \"incomplete\": {}\n }\n }\n \"type\": \"property_item\"\n}"
1804 | }
1805 | }
1806 | }
1807 | }
1808 | }
1809 | },
1810 | "deprecated": false,
1811 | "security": []
1812 | }
1813 | },
1814 | "/v1/comments": {
1815 | "get": {
1816 | "summary": "Retrieve comments",
1817 | "description": "Retrieves a list of un-resolved [Comment objects](ref:comment-object) from a page or block.",
1818 | "operationId": "retrieve-a-comment",
1819 | "parameters": [
1820 | {
1821 | "name": "block_id",
1822 | "in": "query",
1823 | "description": "Identifier for a Notion block or page",
1824 | "required": true,
1825 | "schema": {
1826 | "type": "string"
1827 | }
1828 | },
1829 | {
1830 | "name": "start_cursor",
1831 | "in": "query",
1832 | "description": "If supplied, this endpoint will return a page of results starting after the cursor provided. If not supplied, this endpoint will return the first page of results.",
1833 | "schema": {
1834 | "type": "string"
1835 | }
1836 | },
1837 | {
1838 | "name": "page_size",
1839 | "in": "query",
1840 | "description": "The number of items from the full list desired in the response. Maximum: 100",
1841 | "schema": {
1842 | "type": "integer",
1843 | "format": "int32"
1844 | }
1845 | }
1846 | ],
1847 | "responses": {
1848 | "200": {
1849 | "description": "200",
1850 | "content": {
1851 | "application/json": {
1852 | "examples": {
1853 | "OK": {
1854 | "value": "{\n \"object\": \"list\",\n \"results\": [\n {\n \"object\": \"comment\",\n \"id\": \"94cc56ab-9f02-409d-9f99-1037e9fe502f\",\n \"parent\": {\n \"type\": \"page_id\",\n \"page_id\": \"5c6a2821-6bb1-4a7e-b6e1-c50111515c3d\"\n },\n \"discussion_id\": \"f1407351-36f5-4c49-a13c-49f8ba11776d\",\n \"created_time\": \"2022-07-15T16:52:00.000Z\",\n \"last_edited_time\": \"2022-07-15T19:16:00.000Z\",\n \"created_by\": {\n \"object\": \"user\",\n \"id\": \"9b15170a-9941-4297-8ee6-83fa7649a87a\"\n },\n \"rich_text\": [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"Single comment\",\n \"link\": null\n },\n \"annotations\": {\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"code\": false,\n \"color\": \"default\"\n },\n \"plain_text\": \"Single comment\",\n \"href\": null\n }\n ]\n }\n ],\n \"next_cursor\": null,\n \"has_more\": false,\n \"type\": \"comment\",\n \"comment\": {}\n}"
1855 | }
1856 | }
1857 | }
1858 | }
1859 | }
1860 | },
1861 | "deprecated": false,
1862 | "security": []
1863 | },
1864 | "post": {
1865 | "summary": "Create comment",
1866 | "description": "Creates a comment in a page or existing discussion thread.",
1867 | "operationId": "create-a-comment",
1868 | "requestBody": {
1869 | "content": {
1870 | "application/json": {
1871 | "schema": {
1872 | "type": "object",
1873 | "required": [
1874 | "parent",
1875 | "rich_text"
1876 | ],
1877 | "properties": {
1878 | "parent": {
1879 | "type": "object",
1880 | "description": "The page that contains the comment",
1881 | "required": [
1882 | "page_id"
1883 | ],
1884 | "properties": {
1885 | "page_id": {
1886 | "type": "string",
1887 | "description": "the page ID"
1888 | }
1889 | }
1890 | },
1891 | "rich_text": {
1892 | "type": "array",
1893 | "items": {
1894 | "type": "object",
1895 | "required": [
1896 | "text"
1897 | ],
1898 | "properties": {
1899 | "text": {
1900 | "type": "object",
1901 | "required": [
1902 | "content"
1903 | ],
1904 | "properties": {
1905 | "content": {
1906 | "type": "string",
1907 | "description": "The content of the comment"
1908 | }
1909 | }
1910 | }
1911 | }
1912 | }
1913 | }
1914 | }
1915 | }
1916 | }
1917 | }
1918 | },
1919 | "responses": {
1920 | "200": {
1921 | "description": "200",
1922 | "content": {
1923 | "application/json": {
1924 | "examples": {
1925 | "Result": {
1926 | "value": "{\n \"object\": \"comment\",\n \"id\": \"b52b8ed6-e029-4707-a671-832549c09de3\",\n \"parent\": {\n \"type\": \"page_id\",\n \"page_id\": \"5c6a2821-6bb1-4a7e-b6e1-c50111515c3d\"\n },\n \"discussion_id\": \"f1407351-36f5-4c49-a13c-49f8ba11776d\",\n \"created_time\": \"2022-07-15T20:53:00.000Z\",\n \"last_edited_time\": \"2022-07-15T20:53:00.000Z\",\n \"created_by\": {\n \"object\": \"user\",\n \"id\": \"067dee40-6ebd-496f-b446-093c715fb5ec\"\n },\n \"rich_text\": [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": \"Hello world\",\n \"link\": null\n },\n \"annotations\": {\n \"bold\": false,\n \"italic\": false,\n \"strikethrough\": false,\n \"underline\": false,\n \"code\": false,\n \"color\": \"default\"\n },\n \"plain_text\": \"Hello world\",\n \"href\": null\n }\n ]\n}"
1927 | }
1928 | }
1929 | }
1930 | }
1931 | }
1932 | },
1933 | "deprecated": false,
1934 | "security": []
1935 | }
1936 | }
1937 | }
1938 | }
1939 |
```