為Microsoft自訂連接器建立kintone的OpenAPI Spec 2.0

作者姓名:Aiko Hanatsuka( サイボウズ株式会社 (External link) )

目錄

前言

Microsoft Power Platform提供了 官方的kintone連接器 (日語)。
但由於支援的API與動作有限,有時無法透過Microsoft產品實現與kintone的整合。
這種情況下,只要匯入OpenAPI Spec,就能輕鬆建立自訂連接器,使用官方kintone連接器未提供的API。

不過, kintone OpenAPI Spec 為OpenAPI 3.0,而建立自訂連接器只能使用OpenAPI 2.0。
因此,本文將介紹可輕鬆建立kintone OpenAPI 2.0的Claude Code Skills。

information

建立OpenAPI Spec 2.0不一定要使用本文的方法。
請將其作為參考範例。

使用的內容

本文使用以下兩個檔案。

檔案名稱 說明
SKILL.md 用於參照 kintone OpenAPI Spec 生成OpenAPI 2.0的Skills
sample.json 假設在自訂連接器中使用的OpenAPI 2.0範例

範例中預先包含了與kintone整合時最常使用的以下API。

  • 取得記錄
  • 批次新增記錄
  • 批次更新記錄
  • 取得應用程式的欄位資訊

使用方法

請下載以下zip檔案,並執行/kintone-openapi2-generate Skills。
只要告知AI所需的API、認證方式與子網域,就能依照kintone的規格生成OpenAPI Spec 2.0。

kintone-openapi2-generator.zip

zip檔案內的目錄結構如下。

1
2
3
4
5
6
7
kintone-openapi2-generator/
└── .claude/
    └── skills/
        └── kintone-openapi2-generate/
            ├── SKILL.md
            └── references/
                └── sample.json

SKILL.md與sample.json的內容分別如下。
除Claude Code以外的AI服務也能運用Skills,請作為參考。

SKILL.md
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# kintone OpenAPI 2.0 Generator

建立或更新OpenAPI 2.0 JSON,以便從Microsoft Power Platform自訂連接器或Copilot Studio使用kintone REST API。

## 1. 參照官方規格

kintone REST API的規格,必須參照kintone OpenAPI Spec。
[https://github.com/kintone/openapi-spec](https://github.com/kintone/openapi-spec)

請使用`main`分支的最新規格。
特別是各API的description、parameter、request、response、限制事項,應依據官方規格建立。

不得以推測補充API規格。若無法從官方規格判斷,請告知使用者。

## 2. 向使用者確認

即使工作目錄中沒有`sample.json`,也不得判定為「不存在」。`references/sample.json`已隨此Skill附帶,因此無需詢問使用者,應自行讀取。

建立OpenAPI之前,請確認以下3點。

1. 希望允許Copilot執行的操作
2. 認證方式
3. kintone的子網域

### 希望允許Copilot執行的操作

請確認「您要使用哪個kintone REST API?」。
若使用者不知道API名稱,請確認「您希望Copilot在kintone中執行什麼操作?」。

例如,請確認以下操作。

- 取得多筆記錄
- 新增多筆記錄
- 更新多筆記錄
- 刪除多筆記錄
- 取得應用程式的欄位資訊
- 取得/新增記錄的回覆
- 取得檔案
- 其他kintone操作

請從使用者的目的判斷所需的kintone REST API,並以官方規格確認。
在新增或更新記錄時,必須一併加入取得應用程式欄位資訊的API。

一個操作需要多個API時,請確定所需的API。但不得僅因「有用」而新增API,允許Copilot執行的操作應限制在最低必要範圍。
只有在無法從目的唯一判斷API時,才向使用者追加確認。

### 認證方式

請確認使用以下哪一種。

- API權杖認證
- OAuth用戶端認證

### 子網域

請確認用於OpenAPI的`host`的kintone子網域。
API名稱、endpoint、OAuth scope、schema等可從官方資訊判斷的技術資訊,不向使用者提問,而由AI端確認。

## 3. 生成OpenAPI 2.0

必須讀取此Skill下的`references/sample.json`(與此SKILL.md位於同一目錄的`references/`內),並以其設計為基礎。
若使用者提供了既有的OpenAPI 2.0檔案,也須一併作為基礎。

除非必要,不得變更或刪除既有的operation與definitions,並應重複利用可用的definitions。

不得將官方提供的kintone OpenAPI Spec進行單純的機械式轉換,應僅將所需的API與schema重新建構為OpenAPI 2.0格式。

基本依照以下規則進行轉換。

- `servers` → `host` / `basePath` / `schemes`
- `components.schemas` → `definitions`
- `components.securitySchemes` → `securityDefinitions`
- `requestBody` → `in: body`的parameter
- response的`content` → response `schema`
- `#/components/schemas/...` → `#/definitions/...`

不得在最終結果中保留OpenAPI 3.x特有的定義。

### OpenAPI 2.0無法直接表示的情況

- 不得單純刪除`oneOf` / `anyOf`等。
- 如`sample.json`所示,若能透過共通schema與description維持語意,則予以簡化。無法安全轉換時,不得推測,請告知使用者。
- `nullable: true`視需要以`x-nullable: true`表示。
- `example` / `examples`應配置於Swagger 2.0中有效的位置。
- 若有`style`、`explode`等parameter serialization的指定,請確認實際的API規格,並以OpenAPI 2.0適當表示。無法安全轉換時,不得推測。

### kintone的動態欄位

kintone的欄位代碼因應用程式而異,因此不得固定定義不確定是否存在的欄位代碼。
應盡可能重複利用`sample.json`中通用的Record與FieldValue schema。

### description

description應視為AI代理正確使用API的重要資訊。
以官方規格的description為基礎,特別需維持以下資訊。

- parameter的意義
- query的寫法
- 依欄位類型而異的差異
- 上限值
- 權限
- API特有的限制事項

## 4. 確認認證方式

不得假設使用者所選的認證方式適用於所有API。
對於每個新增的API,都必須以官方規格確認所選認證方式是否可用。
若有無法使用的API,請在生成OpenAPI之前告知使用者。

### API權杖認證

確認對象API支援API權杖認證,並使用`X-Cybozu-API-Token`。
不得在OpenAPI中寫入實際的API權杖。

### OAuth用戶端認證

從官方資訊確認對象API所需的OAuth scope。
使用多個API時,請確認各自所需的scope,並將所需的scope彙整定義。
不得以推測設定scope。

## 5. 最終檢查

輸出前請確認以下事項。

- 是否為正確的JSON
- 是否為`"swagger": "2.0"`
- 是否殘留OpenAPI 3.x特有的定義
- `$ref`的參照目標是否存在
- `operationId`是否沒有重複
- 所選認證方式是否可用於各API
- 若為OAuth,是否包含所需的scope
- 是否僅包含使用者所需的API
- 是否遺漏了對API使用重要的description或限制事項

更新既有檔案時,不得刪除未被要求的operation。
請返回完整的OpenAPI 2.0 JSON,並簡潔說明新增的API與認證方式。
sample.json
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
{
  "swagger": "2.0",
  "info": {
    "title": "kintone REST API (Copilot Studio validation profile)",
    "version": "1.0.0",
    "description": "kintone REST API的一部分已轉換為Swagger 2.0格式(因為Power Platform / Copilot Studio尚未支援OpenAPI 3.x)。\n\n提供以下4項操作。\n\n1. `GET /app/form/fields.json` — 取得欄位代碼與類型\n2. `GET /records.json` — 搜尋記錄\n3. `POST /records.json` — 批次新增記錄(最多100筆)\n4. `PUT /records.json` — 批次更新記錄(最多100筆)\n\n這是將整個kintone REST API(OpenAPI 3.0,204項操作)簡化後的子集。使用前請將host改為您自己的kintone子網域。",
    "license": {
      "name": "MIT No Attribution",
      "url": "https://opensource.org/license/MIT-0"
    }
  },
  "host": "sample.cybozu.com",
  "basePath": "/k/v1",
  "schemes": [
    "https"
  ],
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "securityDefinitions": {
    "api_key": {
      "type": "apiKey",
      "in": "header",
      "name": "X-Cybozu-API-Token"
    }
  },
  "security": [
    {
      "api_key": []
    }
  ],
  "paths": {
    "/records.json": {
      "get": {
        "operationId": "getRecords",
        "summary": "Get records",
        "description": "Retrieves records from an App. Use the `query` parameter to filter, sort and page. Returns at most 500 records per call.",
        "parameters": [
          {
            "name": "app",
            "in": "query",
            "required": true,
            "description": "The App ID. This is the number in the App's URL, for example `31` in `https://example.cybozu.com/k/31/`.",
            "type": "integer"
          },
          {
            "name": "query",
            "in": "query",
            "required": false,
            "description": "Search conditions written in kintone's own query syntax. This is **not** SQL and **not** OData.\nOmit this parameter to retrieve all records the credential can access.\n\n## Syntax rules\n\n- The field code always goes on the **left** of the operator, never on the right.\n- Wrap string values in double quotes: `Status in (\"Closed\")`. Numbers and times are unquoted: `price > 10`, `Time > 10:00`.\n- Combine conditions with `and` / `or`, and group them with parentheses.\n- Field codes are defined per App and are case-sensitive. Call `GET /app/form/fields.json` first to obtain the exact field codes and their types. Do not guess field codes.\n\n## Operators allowed per field type\n\nUsing an operator that the field type does not support returns an error, so check the field type first.\n\n| Field type | Allowed operators |\n|---|---|\n| Single-line text, Link | `=` `!=` `in` `not in` `like` `not like` |\n| Multi-line text | `like` `not like` `is empty` `is not empty` — **`=` is not allowed** |\n| Rich text | `like` `not like` |\n| Number, Calculated | `=` `!=` `>` `<` `>=` `<=` `in` `not in` |\n| Drop-down, Radio button, Check box, Multi-choice | **`in` / `not in` only — `=` is not allowed** |\n| Date, Date and time, Time | `=` `!=` `>` `<` `>=` `<=` |\n| Created datetime, Updated datetime | `=` `!=` `>` `<` `>=` `<=` |\n| User selection, Department selection, Group selection | `in` `not in` |\n| Created by, Updated by | `in` `not in` |\n| Status | `=` `!=` `in` `not in` |\n| Record number, `$id` | `=` `!=` `>` `<` `>=` `<=` `in` `not in` |\n| Attachment | `like` `not like` `is empty` `is not empty` |\n| Lookup | Same as the field type it looks up |\n| Related Records, fields inside a Table | Same as the source field type, but `=` and `!=` are **not** allowed — use `in` / `not in` |\n| Field group, Category | Cannot be used in a query |\n\nTo reference a field inside a Related Records field, use `<relatedRecordsFieldCode>.<fieldCode>`.\n\n## Functions\n\n| Function | Applies to |\n|---|---|\n| `LOGINUSER()` | User selection, Created by, Updated by |\n| `PRIMARY_ORGANIZATION()` | Department selection |\n| `NOW()` `TODAY()` `YESTERDAY()` `TOMORROW()` | Date, Date and time, Created datetime, Updated datetime |\n| `FROM_TODAY(n, \"DAYS\"\\|\"WEEKS\"\\|\"MONTHS\"\\|\"YEARS\")` | same as above |\n| `THIS_WEEK(SUNDAY..SATURDAY)` `LAST_WEEK()` `NEXT_WEEK()` | same as above |\n| `THIS_MONTH(1..31\\|LAST)` `LAST_MONTH()` `NEXT_MONTH()` | same as above |\n| `THIS_YEAR()` `LAST_YEAR()` `NEXT_YEAR()` | same as above |\n\nArguments may be omitted to mean the whole week / month / year, e.g. `THIS_MONTH()`.\n\n## Options\n\nPlace these **after all conditions, in this exact order**: `order by`, `limit`, `offset`.\n\n| Option | Meaning |\n|---|---|\n| `order by <fieldCode> asc\\|desc` | Sort order. Separate multiple fields with commas. Defaults to record ID descending. |\n| `limit <n>` | Number of records to return. Default 100, **maximum 500**. |\n| `offset <n>` | Number of records to skip. **Maximum 10000.** To read past 10000 records, use the cursor API instead. |\n\n## Escaping\n\nInside double quotes, escape `\"` and `\\` with a backslash. Required for text, rich text, check box, radio button, drop-down, multi-choice and status fields.\nExample: `Checkbox in (\"sample\\\"1\\\"\")`\n\n## Examples\n\n```\nText like \"sample\" and Text_area like \"sample\" order by Created_datetime desc\nDrop_down in (\"value1\", \"value2\") and (Multi_choice in (\"value3\") or Radio_button in (\"value4\"))\nTime > 10:00 and Time < 19:00 and Created_datetime = TODAY() order by $id asc limit 10\nUser_selection in (LOGINUSER()) and Department_selection not in (PRIMARY_ORGANIZATION())\nCompany_DB.company_name in (\"kintone\") and Company_DB.location like \"San Francisco\"\nText_area is empty\n```",
            "type": "string"
          },
          {
            "name": "totalCount",
            "in": "query",
            "required": false,
            "description": "If set to `true`, the total count of records that match the query conditions will be included in the response.\nIgnoring this parameter will return `null`.",
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "The matching records.",
            "schema": {
              "$ref": "#/definitions/GetRecordsResponse"
            },
            "examples": {
              "application/json": {
                "records": [
                  {
                    "$id": {
                      "type": "__ID__",
                      "value": "12"
                    },
                    "公司名稱": {
                      "type": "SINGLE_LINE_TEXT",
                      "value": "Cybozu股份有限公司"
                    },
                    "銷售額": {
                      "type": "NUMBER",
                      "value": "1200000"
                    },
                    "洽談階段": {
                      "type": "DROP_DOWN",
                      "value": "提案"
                    },
                    "應對類型": {
                      "type": "CHECK_BOX",
                      "value": [
                        "拜訪",
                        "電話"
                      ]
                    },
                    "預計接單日": {
                      "type": "DATE",
                      "value": "2026-08-31"
                    },
                    "更新人": {
                      "type": "MODIFIER",
                      "value": {
                        "code": "sean",
                        "name": "Sean"
                      }
                    }
                  }
                ],
                "totalCount": "1"
              }
            }
          },
          "400": {
            "description": "The request was rejected.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "401": {
            "description": "Unauthorized.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "403": {
            "description": "Forbidden.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "404": {
            "description": "NotFound.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "429": {
            "description": "Too many requests.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "495": {
            "description": "Cert Error.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "496": {
            "description": "No Cert.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "503": {
            "description": "Service Unavailable.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "default": {
            "description": "An unexpected error occurred.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          }
        }
      },
      "post": {
        "operationId": "addRecords",
        "summary": "Register (add) multiple records in an App",
        "description": "Registers up to 100 new records in a single call. Call `GET /app/form/fields.json` first so that field codes, required fields and choice strings (for selection fields) are known before building the request.\n\nRecords are registered in the order given in `records`. If any record in the request fails validation, **the entire batch is cancelled** and no records are registered.\n\nThe following fields cannot be set through this API: fields copied from a Lookup's source, Status, Category, Calculated, Assignee, and an auto-calculated Single-line text field. Setting Created by / Updated by / Created datetime / Updated datetime requires App Management permission on the credential; otherwise these are set automatically.\n\nRequired permissions: record-add permission on the App, and edit permission on every field being set.",
        "consumes": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "description": "The records to register.",
            "schema": {
              "$ref": "#/definitions/AddRecordsRequestBody"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The records were registered. `ids` and `revisions` are in the same order as the `records` given in the request.",
            "schema": {
              "$ref": "#/definitions/AddRecordsResponse"
            },
            "examples": {
              "application/json": {
                "ids": [
                  "100",
                  "101"
                ],
                "revisions": [
                  "1",
                  "1"
                ]
              }
            }
          },
          "400": {
            "description": "The request was rejected.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "401": {
            "description": "Unauthorized.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "403": {
            "description": "Forbidden.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "404": {
            "description": "NotFound.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "429": {
            "description": "Too many requests.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "495": {
            "description": "Cert Error.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "496": {
            "description": "No Cert.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "503": {
            "description": "Service Unavailable.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "default": {
            "description": "An unexpected error occurred.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          }
        }
      },
      "put": {
        "operationId": "updateRecords",
        "summary": "Update multiple records in an App",
        "description": "Updates up to 100 existing records in a single call. Each entry in `records` identifies one record either by `id` (the record ID) or by `updateKey` (a field/value pair on a field with duplicates disallowed) — never both. Call `GET /app/form/fields.json` first so that field codes, and which field is safe to use as an `updateKey`, are known.\n\nIf `upsert` is `true`, an entry whose target record does not already exist is registered as a new record instead of failing (UPSERT mode); this also requires record-add permission on the App. `updateKey` is required to use UPSERT mode, since a not-yet-existing record has no `id` to match against.\n\nIf `records[].record` is omitted for an entry, that record's field values are left unchanged. If `records[].revision` is given, the update is rejected (and the whole batch cancelled) when it does not match the record's current revision; omit it, or pass `-1`, to skip this check.\n\nIf any entry in the request fails, **the entire batch is cancelled** and no records are updated.\n\nThe following fields cannot be updated through this API: fields copied from a Lookup's source, Status, Category, Calculated, Assignee, Created by, Created datetime, Updated by, Updated datetime, and an auto-calculated Single-line text field.\n\nRequired permissions: record-edit permission on the App, and edit permission on the records and fields being updated (plus record-add permission on the App when `upsert` is `true`).",
        "consumes": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "description": "The records to update.",
            "schema": {
              "$ref": "#/definitions/UpdateRecordsRequestBody"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The records were updated (or, in UPSERT mode, registered). `records` is in the same order as the `records` given in the request.",
            "schema": {
              "$ref": "#/definitions/UpdateRecordsResponse"
            },
            "examples": {
              "application/json": {
                "records": [
                  {
                    "id": "1",
                    "revision": "5",
                    "operation": "UPDATE"
                  },
                  {
                    "id": "2",
                    "revision": "1",
                    "operation": "INSERT"
                  }
                ]
              }
            }
          },
          "400": {
            "description": "The request was rejected.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "401": {
            "description": "Unauthorized.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "403": {
            "description": "Forbidden.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "404": {
            "description": "NotFound.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "429": {
            "description": "Too many requests.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "495": {
            "description": "Cert Error.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "496": {
            "description": "No Cert.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "503": {
            "description": "Service Unavailable.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "default": {
            "description": "An unexpected error occurred.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          }
        }
      }
    },
    "/app/form/fields.json": {
      "get": {
        "operationId": "getFormFields",
        "summary": "Get the field codes and types of an App",
        "description": "Returns the App's field definitions. Call this before building a `query` so that the correct operators are used for each field type, and so that exact field codes and choice strings are known.",
        "parameters": [
          {
            "name": "app",
            "in": "query",
            "required": true,
            "description": "The App ID. This is the number in the App's URL, for example `31` in `https://example.cybozu.com/k/31/`.",
            "type": "integer"
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "description": "The localized language to retrieve the data in:\n- `default`: retrieves the default names\n- `en`: retrieves the localized English names\n- `zh`: retrieves the localized Chinese names\n- `ja`: retrieves the localized Japanese names\n- `user`: retrieves the localized names, in the same language as the language setting* set on the user used for the authentication.If ignored, the default names will be retrieved.",
            "type": "string",
            "enum": [
              "default",
              "user",
              "ja",
              "en",
              "zh"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "`properties` is keyed by field code. Each entry has at least `type`, `code` and `label`.\nUse `type` to decide which query operators are valid for that field (see the `query` parameter of `GET /records.json`).\nSelection fields additionally carry `options`, which lists the exact choice strings that a query must match.",
            "schema": {
              "$ref": "#/definitions/GetFormFieldsResponse"
            }
          },
          "400": {
            "description": "The request was rejected.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "401": {
            "description": "Unauthorized.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "403": {
            "description": "Forbidden.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "404": {
            "description": "NotFound.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "429": {
            "description": "Too many requests.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "495": {
            "description": "Cert Error.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "496": {
            "description": "No Cert.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "503": {
            "description": "Service Unavailable.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          },
          "default": {
            "description": "An unexpected error occurred.",
            "schema": {
              "$ref": "#/definitions/Error"
            }
          }
        }
      }
    }
  },
  "definitions": {
    "GetRecordsResponse": {
      "type": "object",
      "properties": {
        "records": {
          "type": "array",
          "description": "The records that matched the query.",
          "items": {
            "$ref": "#/definitions/Record"
          }
        },
        "totalCount": {
          "type": "string",
          "x-nullable": true,
          "description": "The total number of records matching the query, as a string. `null` unless `totalCount=true` was requested."
        }
      }
    },
    "Record": {
      "type": "object",
      "description": "One record. **The keys are the App's field codes**, which differ per App, so they are not listed in this schema.\nEach value is an object with `type` (the field type) and `value` (the content).\nRead a field as `records[i].<fieldCode>.value`.\n\nThe shape of `value` depends on `type`:\n\n| type | shape of `value` |\n|---|---|\n| `SINGLE_LINE_TEXT` `MULTI_LINE_TEXT` `RICH_TEXT` `LINK` `NUMBER` `CALC` `DATE` `TIME` `DATETIME` `RECORD_NUMBER` `STATUS` `__ID__` `__REVISION__` | string |\n| `CHECK_BOX` `MULTI_SELECT` `CATEGORY` | array of strings |\n| `DROP_DOWN` `RADIO_BUTTON` | string, or `null` when nothing is selected |\n| `USER_SELECT` `ORGANIZATION_SELECT` `GROUP_SELECT` `STATUS_ASSIGNEE` | array of `{ code, name }` |\n| `CREATOR` `MODIFIER` | `{ code, name }` |\n| `FILE` | array of `{ fileKey, name, contentType, size }` |\n| `SUBTABLE` | array of `{ id, value: { <fieldCode>: { type, value } } }` |\n\nField types that hold no data of their own — `GROUP`, `REFERENCE_TABLE`, `LABEL`, `SPACER`, `HR` — are **not returned** in the response even though they exist on the form.",
      "additionalProperties": {
        "$ref": "#/definitions/FieldValue"
      }
    },
    "FieldValue": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "description": "The field type, for example `SINGLE_LINE_TEXT`, `NUMBER`, `DROP_DOWN`, `DATE`, `USER_SELECT`, `SUBTABLE`."
        },
        "value": {
          "description": "The field's content. A string, an array of strings, an object, or an array of objects depending on `type`. See the description of the parent schema."
        }
      }
    },
    "GetFormFieldsResponse": {
      "type": "object",
      "properties": {
        "properties": {
          "type": "object",
          "description": "Field definitions, keyed by field code.",
          "additionalProperties": {
            "$ref": "#/definitions/FieldProperty"
          }
        },
        "revision": {
          "type": "string",
          "description": "The App's settings revision number."
        }
      }
    },
    "FieldProperty": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "description": "The field type. Determines which query operators may be used against this field."
        },
        "code": {
          "type": "string",
          "description": "The field code, used in queries."
        },
        "label": {
          "type": "string",
          "description": "The display name shown on the form."
        },
        "required": {
          "type": "boolean",
          "description": "Whether the field is mandatory."
        },
        "options": {
          "type": "object",
          "description": "For selection fields only. Keyed by the exact choice string that a query must match.",
          "additionalProperties": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string"
              },
              "index": {
                "type": "string"
              }
            }
          }
        }
      }
    },
    "AddRecordsRequestBody": {
      "type": "object",
      "required": [
        "app",
        "records"
      ],
      "properties": {
        "app": {
          "type": "integer",
          "description": "The App ID. This is the number in the App's URL, for example `31` in `https://example.cybozu.com/k/31/`."
        },
        "records": {
          "type": "array",
          "maxItems": 100,
          "description": "The records to register, up to 100 per call. Records are registered in the order given here.\nAn empty object (`{}`) registers a record with every field left at its default value.\nA field code that does not exist on the App is silently ignored rather than rejected.",
          "items": {
            "$ref": "#/definitions/RecordInput"
          }
        }
      }
    },
    "RecordInput": {
      "type": "object",
      "description": "One record to write, keyed by the App's field codes (call `GET /app/form/fields.json` first to obtain these). Each value is an object of the form `{ \"value\": <content> }`.\n\nThe shape of `value` depends on the field's type:\n\n| Field type | shape of `value` |\n|---|---|\n| Single-line text, Multi-line text, Rich text, Link, Number, Date, Time, Date and time, Radio button, Drop-down | string (`\"\"` clears a Radio button/Drop-down) |\n| Check box, Multi-choice | array of strings — must exactly match the App's configured choice strings |\n| User selection, Department selection, Group selection | array of `{ \"code\": <login name or org/group code> }` |\n| Attachment | array of `{ \"fileKey\": <key from the file-upload API> }` — include existing `fileKey`s to keep already-attached files |\n| Table (Subtable) | array of `{ \"id\": <existing row id, optional>, \"value\": { <fieldCode>: { \"value\": ... } } }` |\n\nFields that cannot be set through this API — values copied from a Lookup's source, Status, Category, Calculated, Assignee, and an auto-calculated Single-line text field — are ignored if included.\nSetting Created by, Updated by, Created datetime or Updated datetime requires App Management permission on the credential.",
      "additionalProperties": {
        "$ref": "#/definitions/FieldValueInput"
      }
    },
    "FieldValueInput": {
      "type": "object",
      "properties": {
        "value": {
          "description": "The value to write. A string, an array of strings, or an array of objects depending on the field's type — see the description of the parent schema."
        }
      }
    },
    "AddRecordsResponse": {
      "type": "object",
      "properties": {
        "ids": {
          "type": "array",
          "description": "The record IDs assigned to the newly registered records, in the same order as the `records` given in the request.",
          "items": {
            "type": "string"
          }
        },
        "revisions": {
          "type": "array",
          "description": "The revision number of each newly registered record (normally `\"1\"`), in the same order as `ids`.",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "UpdateRecordsRequestBody": {
      "type": "object",
      "required": [
        "app",
        "records"
      ],
      "properties": {
        "app": {
          "type": "integer",
          "description": "The App ID. This is the number in the App's URL, for example `31` in `https://example.cybozu.com/k/31/`."
        },
        "upsert": {
          "type": "boolean",
          "description": "If `true`, run in UPSERT mode: an entry whose target record (identified by `updateKey`) does not exist is registered as a new record instead of failing. Requires every entry to use `updateKey` rather than `id`, and requires record-add permission on the App in addition to the usual record-edit permission. Defaults to `false`.",
          "default": false
        },
        "records": {
          "type": "array",
          "maxItems": 100,
          "description": "The records to update, up to 100 per call. Each entry identifies its target record with exactly one of `id` or `updateKey`.",
          "items": {
            "$ref": "#/definitions/UpdateRecordItem"
          }
        }
      }
    },
    "UpdateRecordItem": {
      "type": "object",
      "description": "One record to update. Provide exactly one of `id` or `updateKey` to identify the target record — never both.",
      "properties": {
        "id": {
          "type": "integer",
          "description": "The record ID to update. Mutually exclusive with `updateKey`. Not valid in UPSERT mode (use `updateKey` there, since a new record has no ID yet)."
        },
        "updateKey": {
          "$ref": "#/definitions/UpdateKey"
        },
        "record": {
          "$ref": "#/definitions/RecordInput",
          "description": "The field values to write. If omitted, the record's existing values are left unchanged. See `RecordInput` for the shape of each field's `value`, and which fields cannot be updated through this API."
        },
        "revision": {
          "type": "integer",
          "description": "The revision number this update expects the record to currently be at. If it does not match, the update is rejected (and the whole batch cancelled). Omit, or pass `-1`, to skip this check."
        }
      }
    },
    "UpdateKey": {
      "type": "object",
      "required": [
        "field",
        "value"
      ],
      "description": "Identifies a record by a field value instead of by record ID. The field must be a Single-line text or Number field configured with duplicates disallowed. Mutually exclusive with `id`.",
      "properties": {
        "field": {
          "type": "string",
          "description": "The field code of the unique-value field to match on."
        },
        "value": {
          "type": "string",
          "description": "The value to match against that field."
        }
      }
    },
    "UpdateRecordsResponse": {
      "type": "object",
      "properties": {
        "records": {
          "type": "array",
          "description": "One entry per updated (or, in UPSERT mode, newly registered) record, in the same order as the `records` given in the request.",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "description": "The record ID. In UPSERT mode, this is the newly assigned ID when the record was registered rather than updated."
              },
              "revision": {
                "type": "string",
                "description": "The record's revision number after this update."
              },
              "operation": {
                "type": "string",
                "enum": [
                  "UPDATE",
                  "INSERT"
                ],
                "description": "`UPDATE` if an existing record was modified, `INSERT` if UPSERT mode registered a new record."
              }
            }
          }
        }
      }
    },
    "Error": {
      "type": "object",
      "properties": {
        "message": {
          "type": "string",
          "description": "A human-readable description of the error."
        },
        "id": {
          "type": "string",
          "description": "An identifier for the individual error occurrence."
        },
        "code": {
          "type": "string",
          "description": "The error code, for example `CB_VA01`."
        }
      }
    }
  }
}
tips
補充

可使用 Swagger Editor (External link) 確認所建立的OpenAPI Spec是否有問題。
如下圖所示,將生成的OpenAPI Spec 2.0檔案內容複製貼上,若沒有錯誤或警告即可。
這是定義取得記錄與記錄回覆的REST API的範例。

重點

關於轉換為2.0

本文公開的Skills,參照官方的kintone OpenAPI Spec,並重新建構為OpenAPI 2.0。

轉換為2.0並非單純地將OpenAPI 3.0的描述替換為2.0。
這是因為3.0中有oneOf/anyOf、nullable、parameter serialization等無法直接以OpenAPI 2.0表示的內容,因此依照kintone獨自的設計進行了調整。

只新增必要的API

kintone REST API並不需要全部放入一個OpenAPI檔案中。
例如,若為了與Copilot整合,只需要搜尋記錄,就不要加入刪除記錄等不必要的操作。

只新增必要的API,可獲得以下好處。

  • 可限定整合時允許的操作。
  • OpenAPI Spec會更加簡潔。
  • 可抑制檔案大小。
information

匯入Microsoft自訂連接器的OpenAPI時,檔案大小上限規定為不到1MB。
使用時也請確認Microsoft的官方文件。

結語

從零開始為Microsoft Power Platform自訂連接器建立OpenAPI Spec時,不僅需要了解kintone的API,還需要了解OpenAPI Spec的寫法。
使用Skills,即可將這部分交給AI,輕鬆完成建立。
請務必加以運用。

information

本Tips已於2026年10月版kintone中確認運作。