@jinshuju/mcp 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -63,7 +63,7 @@
63
63
  "createForm": {
64
64
  "operationId": "createForm",
65
65
  "summary": "Create a form",
66
- "description": "Creates a form from a list of field definitions and returns the full form structure. `name` and `fields` are required.\n- `scene` sets the form's scene; exam / evaluation question types (such as `SingleSelect`, `FillInBlank`) can only be used in the scenes that support them.\n- `layout=card` (paged) does not support PageBreak, Matrix*, TableField, GoodsField, ESignatureField, FormAssociation, MultipleBlanksField or any kind of Widget field.\n- The folder named by `folder_token` must be manageable and of kind form; otherwise returns 404 / 400 respectively.\n- `setting` accepts only whitelisted keys (see `FormSettingWriteInput`); `setting.notification_rules` replaces, as a whole, the notification rules created through the API.\n- The returned form structure carries `url` (the public fill URL).",
66
+ "description": "Creates a form from a list of field definitions and returns the full form structure.",
67
67
  "tag": "Forms",
68
68
  "method": "post",
69
69
  "path": "/forms",
@@ -83,7 +83,7 @@
83
83
  "getForm": {
84
84
  "operationId": "getForm",
85
85
  "summary": "Show a form's structure",
86
- "description": "Returns the form's name, `kind`, `scene`, public URL (`url`), description, field definitions and settings. Each element of `fields` is a single-key object `{ \"<api_code>\": {...} }`, in field order.\nAlso answers for a table or a quick payment form token; `kind` says which it is.\nThe following are included when the matching parameter is `true`:\n- `include_analytics`: each analysable field's definition carries `analytics`;\n- `include_theme`: carries `theme` (the form theme);\n- `include_field_rules`: carries `field_rules` (field display rules);\n- `include_extended_attributes`: carries `extended_attributes` (source-tracking links);\n- `include_transactions`: carries `transactions` (payment statistics) when the form is bound to a payment account.",
86
+ "description": "Returns the form's name, `kind`, `scene`, public URL (`url`), description, field definitions and settings. Each element of `fields` is a single-key object `{ \"<api_code>\": {...} }`, in field order.\nAlso answers for a table or a quick payment form token; `kind` says which it is.",
87
87
  "tag": "Forms",
88
88
  "method": "get",
89
89
  "path": "/forms/{form_token}",
@@ -190,10 +190,32 @@
190
190
  ],
191
191
  "responseSchema": "Form"
192
192
  },
193
+ "deleteForm": {
194
+ "operationId": "deleteForm",
195
+ "summary": "Delete a form",
196
+ "description": "Moves the form and its entries to the trash, from which the product can restore it; on success, returns the form token.",
197
+ "tag": "Forms",
198
+ "method": "delete",
199
+ "path": "/forms/{form_token}",
200
+ "params": [
201
+ {
202
+ "name": "form_token",
203
+ "in": "path",
204
+ "required": true
205
+ }
206
+ ],
207
+ "paramNames": [
208
+ "form_token"
209
+ ],
210
+ "successStatus": 200,
211
+ "scopes": [
212
+ "forms"
213
+ ]
214
+ },
193
215
  "checkFormFieldData": {
194
216
  "operationId": "checkFormFieldData",
195
217
  "summary": "Check whether fields already hold data",
196
- "description": "Use before deleting a field or choice: checks in batch whether fields (or single choices of a field) already have submitted entries. Deleting a field or choice that still has entries deletes those entries with it, and they cannot be recovered.\nMatches the check the web editor runs before deleting.\n- Put every deletion target of one edit in the `checks` of a single call (at most 100, may span several fields); `results` matches `checks` one to one, in order.\n- When a target cannot be checked (for example, the field does not exist), its row returns `unavailable` with the reason; the other targets return as usual.\n- Returns 400 when `checks` is missing, empty, over 100, not a JSON array, or has an item that is not an object.",
218
+ "description": "Use before deleting a field or choice: checks in batch whether fields (or single choices of a field) already have submitted entries. Deleting a field or choice that still has entries deletes those entries with it, and they cannot be recovered.\nMatches the check the web editor runs before deleting.\n- Put every deletion target of one edit in the `checks` of a single call (at most 100, may span several fields); `results` matches `checks` one to one, in order.\n- When a target cannot be checked (the field or the `parent_field` column does not exist, or `choice_value` matches nothing of the kind `choice_type` names, or names more than one), its row returns `unavailable` with the reason; the other targets return as usual.\n- Returns 400 when `checks` is missing, empty, over 100, not a JSON array, or has an item that is not an object.",
197
219
  "tag": "Forms",
198
220
  "method": "get",
199
221
  "path": "/forms/{form_token}/fields/check",
@@ -223,7 +245,7 @@
223
245
  "previewFormFieldTypeChange": {
224
246
  "operationId": "previewFormFieldTypeChange",
225
247
  "summary": "Preview a field type change",
226
- "description": "Use before changing a field's type (the field's `type` in PATCH /forms/{form_token}): changes no data, and counts, from the field's current entries, how many would be kept and how many cleared after the conversion.\nThe conversion happens on the original field (api_code and data column unchanged), so only entry data is affected; matches the preview the web editor shows before converting.\n- Put every conversion of one edit in the `checks` of a single call (at most 20); `results` matches `checks` one to one, in order.\n- `supported=false` means the field cannot be converted to the target type, and the actual edit is refused too.\n- When converting multi-line text to a choice field such as single choice / multiple choice, also returns `choice_count`, the number of choices that would be generated from existing entries, and `choice_limit_exceeded`, whether that exceeds the choice limit (when it does, the conversion is refused rather than truncated).\n- When an item cannot be previewed (the field does not exist, `target_type` is not a type available to this form, etc.), its row returns `unavailable` with the reason; the other items return as usual.\n- Returns 400 when `checks` is missing, empty, over 20, not a JSON array, or has an item that is not an object.",
248
+ "description": "Use before changing a field's type (the field's `type` in PATCH /forms/{form_token}): changes no data, and counts, from the field's current entries, how many would be kept and how many cleared after the conversion; matches the preview the web editor shows before converting.\n- Put every conversion of one edit in the `checks` of a single call.\n- `supported: false`, or `choice_limit_exceeded: true` when multi-line text becomes a choice field, means the edit itself would be refused.",
227
249
  "tag": "Forms",
228
250
  "method": "get",
229
251
  "path": "/forms/{form_token}/fields/preview_convert",
@@ -266,7 +288,8 @@
266
288
  {
267
289
  "name": "fields",
268
290
  "in": "query",
269
- "required": false
291
+ "required": false,
292
+ "array": true
270
293
  },
271
294
  {
272
295
  "name": "filters",
@@ -397,7 +420,7 @@
397
420
  "aggregateEntries": {
398
421
  "operationId": "aggregateEntries",
399
422
  "summary": "Aggregate entries",
400
- "description": "Aggregates whole columns on the server over the entries matching the filters (sum, average, count, percentiles, etc.), optionally grouped by 1 to 2 fields. The response size depends only on the number of metrics and groups, not on the amount of data.\n- `metrics` is required, 1 to 20 items; the functions each field takes are in the `analytics.agg_funcs` returned by `GET /forms/{form_token}?include_analytics=true`.\n- `dimensions` is optional, at most 2 items; only fields whose `analytics.groupable` is true (single-value choice fields and date fields) can be grouped by, and a date field must specify `bucket`.\n- Without `dimensions`, `rows` has a single row; when grouped, one row per group, at most `limit` groups; `row_count` is the total number of groups, and `truncated` is true when there are more.\n- `filters` / `keyword` are the same as for the entry list, but `creator_id`, `serial_number` and association field conditions are not supported (returns 400).\n- Returns 400 when a field does not exist or is not readable by the caller, or a function or grouping does not apply to the field, and says which fields / functions / groupings are available.",
423
+ "description": "Aggregates whole columns on the server over the entries matching the filters (sum, average, count, percentiles, etc.), optionally grouped by 1 to 2 fields. The response size depends only on the number of metrics and groups, not on the amount of data.\n- `filters` / `keyword` work as in the entry list, except that `creator_id`, `serial_number` and association field conditions are not supported.",
401
424
  "tag": "Entries",
402
425
  "method": "get",
403
426
  "path": "/forms/{form_token}/entries/aggregate",
@@ -466,7 +489,8 @@
466
489
  {
467
490
  "name": "fields",
468
491
  "in": "query",
469
- "required": false
492
+ "required": false,
493
+ "array": true
470
494
  },
471
495
  {
472
496
  "name": "include_overview",
@@ -623,7 +647,8 @@
623
647
  {
624
648
  "name": "fields",
625
649
  "in": "query",
626
- "required": false
650
+ "required": false,
651
+ "array": true
627
652
  },
628
653
  {
629
654
  "name": "include_overview",
@@ -733,7 +758,8 @@
733
758
  {
734
759
  "name": "fields",
735
760
  "in": "query",
736
- "required": false
761
+ "required": false,
762
+ "array": true
737
763
  },
738
764
  {
739
765
  "name": "include_labels",
@@ -1203,10 +1229,218 @@
1203
1229
  "form_setting"
1204
1230
  ]
1205
1231
  },
1232
+ "listNotificationRules": {
1233
+ "operationId": "listNotificationRules",
1234
+ "summary": "List notification rules",
1235
+ "description": "Lists the form's notification rules, newest first, each with whether it can send right now.",
1236
+ "tag": "Form Settings",
1237
+ "method": "get",
1238
+ "path": "/forms/{form_token}/notification_rules",
1239
+ "params": [
1240
+ {
1241
+ "name": "form_token",
1242
+ "in": "path",
1243
+ "required": true
1244
+ }
1245
+ ],
1246
+ "paramNames": [
1247
+ "form_token"
1248
+ ],
1249
+ "successStatus": 200,
1250
+ "scopes": [
1251
+ "form_setting"
1252
+ ]
1253
+ },
1254
+ "createNotificationRule": {
1255
+ "operationId": "createNotificationRule",
1256
+ "summary": "Create a notification rule",
1257
+ "description": "Creates a notification rule; the body follows the schema of its `approach`. The rule sends for matching entries from then on, using the SMS / email balance, and shows on the form's notification settings page. SMS and WeChat messages are reviewed when they are sent.\n- Saving an enabled `schedule_time` rule also schedules the entries already in the form whose send time is still ahead.",
1258
+ "tag": "Form Settings",
1259
+ "method": "post",
1260
+ "path": "/forms/{form_token}/notification_rules",
1261
+ "params": [
1262
+ {
1263
+ "name": "form_token",
1264
+ "in": "path",
1265
+ "required": true
1266
+ }
1267
+ ],
1268
+ "paramNames": [
1269
+ "form_token"
1270
+ ],
1271
+ "body": {
1272
+ "contentType": "application/json",
1273
+ "required": true,
1274
+ "flatten": false,
1275
+ "param": "body"
1276
+ },
1277
+ "successStatus": 201,
1278
+ "scopes": [
1279
+ "form_setting"
1280
+ ],
1281
+ "responseSchema": "NotificationRule"
1282
+ },
1283
+ "getNotificationRule": {
1284
+ "operationId": "getNotificationRule",
1285
+ "summary": "Get a notification rule",
1286
+ "description": "Returns the rule and whether it can send right now.",
1287
+ "tag": "Form Settings",
1288
+ "method": "get",
1289
+ "path": "/forms/{form_token}/notification_rules/{id}",
1290
+ "params": [
1291
+ {
1292
+ "name": "form_token",
1293
+ "in": "path",
1294
+ "required": true
1295
+ },
1296
+ {
1297
+ "name": "id",
1298
+ "in": "path",
1299
+ "required": true
1300
+ }
1301
+ ],
1302
+ "paramNames": [
1303
+ "form_token",
1304
+ "id"
1305
+ ],
1306
+ "successStatus": 200,
1307
+ "scopes": [
1308
+ "form_setting"
1309
+ ],
1310
+ "responseSchema": "NotificationRule"
1311
+ },
1312
+ "updateNotificationRule": {
1313
+ "operationId": "updateNotificationRule",
1314
+ "summary": "Update a notification rule",
1315
+ "description": "Changes the rule; the body takes only the keys of the rule's own `approach`, and `approach` itself cannot change.\n- Saving an enabled `schedule_time` rule schedules the entries already in the form whose send time is still ahead.",
1316
+ "tag": "Form Settings",
1317
+ "method": "patch",
1318
+ "path": "/forms/{form_token}/notification_rules/{id}",
1319
+ "params": [
1320
+ {
1321
+ "name": "form_token",
1322
+ "in": "path",
1323
+ "required": true
1324
+ },
1325
+ {
1326
+ "name": "id",
1327
+ "in": "path",
1328
+ "required": true
1329
+ }
1330
+ ],
1331
+ "paramNames": [
1332
+ "form_token",
1333
+ "id"
1334
+ ],
1335
+ "body": {
1336
+ "contentType": "application/json",
1337
+ "required": true,
1338
+ "flatten": false,
1339
+ "param": "body"
1340
+ },
1341
+ "successStatus": 200,
1342
+ "scopes": [
1343
+ "form_setting"
1344
+ ],
1345
+ "responseSchema": "NotificationRule"
1346
+ },
1347
+ "deleteNotificationRule": {
1348
+ "operationId": "deleteNotificationRule",
1349
+ "summary": "Delete a notification rule",
1350
+ "description": "Deletes the rule and cancels what it has scheduled but not yet sent.",
1351
+ "tag": "Form Settings",
1352
+ "method": "delete",
1353
+ "path": "/forms/{form_token}/notification_rules/{id}",
1354
+ "params": [
1355
+ {
1356
+ "name": "form_token",
1357
+ "in": "path",
1358
+ "required": true
1359
+ },
1360
+ {
1361
+ "name": "id",
1362
+ "in": "path",
1363
+ "required": true
1364
+ }
1365
+ ],
1366
+ "paramNames": [
1367
+ "form_token",
1368
+ "id"
1369
+ ],
1370
+ "successStatus": 200,
1371
+ "scopes": [
1372
+ "form_setting"
1373
+ ]
1374
+ },
1375
+ "listNotificationRuleNotifications": {
1376
+ "operationId": "listNotificationRuleNotifications",
1377
+ "summary": "List a notification rule's sending records",
1378
+ "description": "Lists the notifications a rule sent, newest first, with a summary of the matching records. Covers the last 180 days, as the form's notification log page does; notifications scheduled for later are not included.",
1379
+ "tag": "Form Settings",
1380
+ "method": "get",
1381
+ "path": "/forms/{form_token}/notification_rules/{id}/notifications",
1382
+ "params": [
1383
+ {
1384
+ "name": "form_token",
1385
+ "in": "path",
1386
+ "required": true
1387
+ },
1388
+ {
1389
+ "name": "id",
1390
+ "in": "path",
1391
+ "required": true
1392
+ },
1393
+ {
1394
+ "name": "start_date",
1395
+ "in": "query",
1396
+ "required": false
1397
+ },
1398
+ {
1399
+ "name": "end_date",
1400
+ "in": "query",
1401
+ "required": false
1402
+ },
1403
+ {
1404
+ "name": "status",
1405
+ "in": "query",
1406
+ "required": false
1407
+ },
1408
+ {
1409
+ "name": "limit",
1410
+ "in": "query",
1411
+ "required": false
1412
+ },
1413
+ {
1414
+ "name": "next",
1415
+ "in": "query",
1416
+ "required": false
1417
+ },
1418
+ {
1419
+ "name": "skip",
1420
+ "in": "query",
1421
+ "required": false
1422
+ }
1423
+ ],
1424
+ "paramNames": [
1425
+ "form_token",
1426
+ "id",
1427
+ "start_date",
1428
+ "end_date",
1429
+ "status",
1430
+ "limit",
1431
+ "next",
1432
+ "skip"
1433
+ ],
1434
+ "successStatus": 200,
1435
+ "scopes": [
1436
+ "form_setting"
1437
+ ],
1438
+ "responseSchema": "NotificationListResponse"
1439
+ },
1206
1440
  "updateFormTheme": {
1207
1441
  "operationId": "updateFormTheme",
1208
1442
  "summary": "Update a form's theme",
1209
- "description": "Partially updates the form's theme (colors, background, header image, typography, form container, submit button); PUT is accepted too.\n- The request body must contain at least one of primary_color / secondary_color / wallpaper / header / typography / form_container / submit_button (`generate_header_image` is turned into header.header_image_url, so it counts too); otherwise 400.\n- Header image source precedence: header_image_upload_token > header_image_base64 > header_image_url > header_image_attachment_id; when a header image is set and header.type is not given, header.type is set to image.\n- Background image source precedence: background_image_upload_token > background_image_url > background_image_attachment_id. Setting or removing the background image also removes the preset background image that comes with the theme template; the background color is kept.\n- `header_image_attachment_id` / `background_image_attachment_id` take the `attachment_id` returned by \"Upload a form image\" (image_type header / wallpaper); pass an empty string `\"\"` to remove the current image.\n- `header.carousel_images` replaces the carousel images as a whole: at most 5, not empty; each item gives one of image_upload_token / image_url / attachment_id, and an attachment_id must be a carousel image the form already has; when it is passed and header.type is not given, header.type is set to carousel.\n- Keys in `text_style` and `typography.*` use underscores (e.g. font_size); the server converts them to hyphenated CSS keys and merges them into the existing styles; the response uses the hyphenated keys.\n- `generate_header_image` has AI generate a header image and consumes AI points; returns 402 when points are insufficient, 400 when generation fails.\n- An image upload_token is the `form_image_token` used by \"Upload a form image\"; the image purpose it records must match (header / wallpaper / carousel), and its upload must be complete.",
1443
+ "description": "Partially updates the form's theme: colors, background, header image, typography, form container and submit button.",
1210
1444
  "tag": "Form Settings",
1211
1445
  "method": "patch",
1212
1446
  "path": "/forms/{form_token}/theme",
@@ -1258,7 +1492,7 @@
1258
1492
  "updateExamSetting": {
1259
1493
  "operationId": "updateExamSetting",
1260
1494
  "summary": "Update a form's exam settings",
1261
- "description": "Partially updates an exam form's settings; PUT is accepted too. Only for exam-scene forms; otherwise returns 400.\n- When the plan does not support \"show the score after submission\", `notice_after_filling_mode` is forced to none and `success_message_rich_text` is ignored.\n- `interval_comments` replaces the existing range comments as a whole; it takes effect only when `notice_after_filling_mode` (passed in this request or already saved) is grade / explaination / answer; passing an empty array does not turn range comments off.\n- Range comments must have end_point ≥ start_point and must not overlap; otherwise 400.\n- Passing `show_timeout: true` returns 400 when the form already has required questions; once the time limit is on, question fields cannot be made required (required candidate info fields are not affected); to turn on the time limit, make the questions optional first.\n- When `notice_after_filling_mode` is not none, the server clears success_redirect_url / success_redirect_fields in the form's settings.\n- A request body with no recognized keys is a no-op and returns the current settings.",
1495
+ "description": "Partially updates an exam form's settings.",
1262
1496
  "tag": "Form Settings",
1263
1497
  "method": "patch",
1264
1498
  "path": "/forms/{form_token}/exam_setting",
@@ -1309,7 +1543,7 @@
1309
1543
  "updateEvaluationSetting": {
1310
1544
  "operationId": "updateEvaluationSetting",
1311
1545
  "summary": "Update a form's evaluation settings",
1312
- "description": "Partially updates an evaluation form's settings; PUT is accepted too. Only for evaluation-scene forms; otherwise returns 400.\n- When the plan does not support \"show the evaluation report after submission\", `notice_after_filling_mode` is forced to none.\n- `evaluation_comments` replaces the total score range comments as a whole, and an empty array turns range comments off; it takes effect only when `notice_after_filling_mode` (passed in this request or already saved) is reports / customize.\n- A comment by score range must give both start_point and end_point; comments written with conditions in the form editor read back as `id` + `conditions` and must be passed back with their original id when updating (only comment / retry may change; the conditions cannot be changed and no range can be added); leaving one out or changing it returns 400.\n- `indicator_setting.indicators` replaces the dimensions as a whole; pass `api_code` back to keep an existing dimension's identity. A dimension can bind only scorable questions visible on the form, referenced by `field_api_codes` or `field_cids`; referencing an unknown field returns 400.\n- When `notice_after_filling_mode` is not none, the server clears success_redirect_url / success_redirect_fields in the form's settings.\n- A request body with no recognized keys is a no-op and returns the current settings.",
1546
+ "description": "Partially updates an evaluation form's settings (schema `EvaluationSettingInput`).\n- `evaluation_comments` replaces the total score range comments as a whole (an empty array turns them off) and takes effect only when `notice_after_filling_mode`, passed or already saved, is reports / customize. A comment written with conditions in the form editor reads back as `id` + `conditions`; pass it back under that id, where only `comment` / `retry` can change.\n- `indicator_setting.indicators` replaces the dimensions as a whole; pass `api_code` back to keep a dimension. A dimension binds visible scorable questions by `field_api_codes` or `field_cids`.",
1313
1547
  "tag": "Form Settings",
1314
1548
  "method": "patch",
1315
1549
  "path": "/forms/{form_token}/evaluation_setting",
@@ -1672,7 +1906,7 @@
1672
1906
  "uploadEntryAttachment": {
1673
1907
  "operationId": "uploadEntryAttachment",
1674
1908
  "summary": "Upload an entry attachment",
1675
- "description": "Uploads a file ahead of time for a form's attachment field (or an attachment column in a table field) and returns the attachment ID; then, when creating / updating an entry, put that ID (or the upload token) into the matching field to bind it. An uploaded attachment that no entry references stays unused.\nAuthenticate in one of two ways:\n1. Regular API credentials (a Bearer token).\n2. A one-time upload token `attachment_token` (instead of regular credentials): the token starts with `entry_att_tok_`, is valid for 30 minutes, can be used only once, and is bound to the form, field, column and file metadata. When the request carries `attachment_token`, regular credentials are not checked and the request acts as the token's user; returns 400 `attachment_token is invalid or expired; prepare upload again` when the token is invalid, expired, already used, its user has been deleted, or it does not match the requested form / field / column; also returns 400 when the uploaded file's name, Content-Type or byte size does not match the token.\nReturns 401 when there are neither credentials nor `attachment_token`.\nSend the file content in one of two ways: the `file` field of `multipart/form-data`; or a Base64 string in the `base64` (or `data`) field of a JSON / form-encoded body (a `data:<mime>;base64,` prefix is allowed), with `file_name` (or `filename`) and `content_type`. Base64 content cannot exceed 5 MB once decoded; larger returns 400 `attachment exceeds 5MB limit`, and a decoding failure returns 400 `base64 is invalid`; returns 400 `file or base64 is required` when neither is given.\n`field_api_code` must point to an attachment field or a table field; for a table field, `dimension_api_code` must also be passed and point to an attachment column in that table field, otherwise returns 400; returns 404 when the field or column does not exist.\nThe file size is capped by the per-file limit set on the field (or column); a larger file returns 422, with the specific validation message in `error_description` (e.g. `文件超过了10 MB`); returns 422 `上传失败,请重试` when uploading to object storage fails (the server's wording is authoritative).\nReturns 404 `form cannot be found` when the form does not exist or is not accessible to the current identity.",
1909
+ "description": "Uploads a file for an attachment field of the form, or for an attachment column of a table field.",
1676
1910
  "tag": "Uploads",
1677
1911
  "method": "post",
1678
1912
  "path": "/forms/{form_token}/entry_attachments",
@@ -1697,10 +1931,38 @@
1697
1931
  ],
1698
1932
  "responseSchema": "UploadedEntryAttachment"
1699
1933
  },
1934
+ "prepareEntryAttachmentUpload": {
1935
+ "operationId": "prepareEntryAttachmentUpload",
1936
+ "summary": "Prepare an entry attachment upload",
1937
+ "description": "Issues a one-time link for uploading a local file to an attachment field, or to an attachment column of a table field, without sending its bytes in a request body.\n- With a shell that can read the file and reach the network, run `curl -F file=@<path> <upload_url>`, then put the returned `attachment_id` in the field when creating or updating an entry.\n- Without one, or when curl fails (for example under a sandbox's network limits), send small content as Base64 to \"Upload an entry attachment\".",
1938
+ "tag": "Uploads",
1939
+ "method": "post",
1940
+ "path": "/forms/{form_token}/entry_attachment_upload_urls",
1941
+ "params": [
1942
+ {
1943
+ "name": "form_token",
1944
+ "in": "path",
1945
+ "required": true
1946
+ }
1947
+ ],
1948
+ "paramNames": [
1949
+ "form_token"
1950
+ ],
1951
+ "body": {
1952
+ "contentType": "application/json",
1953
+ "required": true,
1954
+ "flatten": true
1955
+ },
1956
+ "successStatus": 201,
1957
+ "scopes": [
1958
+ "write_entries"
1959
+ ],
1960
+ "responseSchema": "PreparedUpload"
1961
+ },
1700
1962
  "uploadImportFile": {
1701
1963
  "operationId": "uploadImportFile",
1702
1964
  "summary": "Upload an import file",
1703
- "description": "Uploads a spreadsheet file (`.xlsx` / `.xls` / `.csv`) for bulk-importing entries into the given form, and returns the import attachment ID; then pass it as `attachment_id` to \"Import entries from an uploaded spreadsheet\" (`POST /forms/{form_token}/entry_imports`) to start the import.\nAuthenticate in one of two ways:\n1. Regular API credentials (a Bearer token).\n2. A one-time upload token `import_file_token` (instead of regular credentials): the token starts with `import_file_tok_`, is valid for 30 minutes, can be used only once, and is bound to the form and the file metadata. When the request carries `import_file_token`, regular credentials are not checked, and the request acts as the user the token belongs to; the uploaded file's original file name, Content-Type and byte size must match what the token recorded, otherwise returns 400. Returns 400 `import_file_token is invalid or expired; prepare upload again` when the token does not exist, has expired, has been used, or its user has been deleted; returns 400 `import_file_token was issued for another form; prepare upload again` when the token belongs to another form.\nReturns 401 when there are neither credentials nor an `import_file_token`.\nThe request must be `multipart/form-data`, with the file in the `file` field; a missing file returns 400 `file is required`.\nThe file size is capped by the plan's Excel import limit (in MB), and the extension must be xls / xlsx / csv; otherwise returns 422, with the validation details in `error_description` (with an upload token, the token stays usable, so the file can be uploaded again).\nReturns 404 `form cannot be found` when the form does not exist or is not accessible to the current caller.",
1965
+ "description": "Uploads a spreadsheet (`.xlsx` / `.xls` / `.csv`) for importing entries into the form.",
1704
1966
  "tag": "Uploads",
1705
1967
  "method": "post",
1706
1968
  "path": "/forms/{form_token}/import_files",
@@ -1725,10 +1987,33 @@
1725
1987
  ],
1726
1988
  "responseSchema": "UploadedImportFile"
1727
1989
  },
1990
+ "prepareImportFileUpload": {
1991
+ "operationId": "prepareImportFileUpload",
1992
+ "summary": "Prepare an import file upload",
1993
+ "description": "Issues a one-time link for uploading a local spreadsheet (`.xlsx` / `.xls` / `.csv`) to import into the form, without sending its bytes in a request body.\n- With a shell that can read the file and reach the network, run `curl -F file=@<path> <upload_url>`, then pass the returned `attachment_id` to \"Import entries from an uploaded spreadsheet\"; read the header row from your local copy.\n- Without one, or when curl fails, the file cannot be uploaded: spreadsheets have no Base64 route.",
1994
+ "tag": "Uploads",
1995
+ "method": "post",
1996
+ "path": "/forms/{form_token}/import_file_upload_urls",
1997
+ "params": [
1998
+ {
1999
+ "name": "form_token",
2000
+ "in": "path",
2001
+ "required": true
2002
+ }
2003
+ ],
2004
+ "paramNames": [
2005
+ "form_token"
2006
+ ],
2007
+ "successStatus": 201,
2008
+ "scopes": [
2009
+ "write_entries"
2010
+ ],
2011
+ "responseSchema": "PreparedUpload"
2012
+ },
1728
2013
  "createEntryImport": {
1729
2014
  "operationId": "createEntryImport",
1730
2015
  "summary": "Import entries from an uploaded spreadsheet",
1731
- "description": "Appends each row of a spreadsheet uploaded with \"Upload an import file\" as an entry of this form. Upload the file first to get the import attachment ID (`attachment_id`), then say here which column goes into which field.\nProblems that can be detected up front, such as the file, the plan's import limit, the header row and the column mapping, are checked before the job is created; a rejected import writes no entries. Error messages include the spreadsheet's actual layout (row and column range, and the header contents), so the request can be fixed and retried.\nOnce validation passes, returns `201` and the entries are written in the background: `201` means the job has started, not finished; poll \"Show an entry import job\" for the result.\n- `attachment_id` must be an import file the current user uploaded for this form, otherwise returns 400 (the file cannot be found; upload it again); returns 400 when the file is not xls / xlsx / csv or cannot be read.\n- `columns` must not be empty; each item names the target field with `field_api_code`, and the source column with `column_label` (the header text, which must exactly match the header cell after trimming leading and trailing spaces) or `sheet_column_index` (the column number); when both are given, `sheet_column_index` wins.\n- The field must be one this form can import into; one field cannot map to several columns; when several header columns share a name, use `sheet_column_index` instead; the column number must be within the spreadsheet's column range.\n- `header_row_index` defaults to the first row of the spreadsheet, and there must be data rows below it.\n- Every required field of the form must have a column, otherwise returns 400 listing the missing fields.\n- `unique_field_code` names a field to deduplicate on: when a row's value for that field already exists in the form's entries, the row is skipped (counted in `skip_count`).\n- Returns 404 `form cannot be found` when the form does not exist or is not accessible to the current caller.",
2016
+ "description": "Appends each row of a spreadsheet uploaded with \"Upload an import file\" as an entry of this form. Upload the file first to get the import attachment ID (`attachment_id`), then say here which column goes into which field.\nOnce validation passes, returns `201` and the entries are written in the background: `201` means the job has started, not finished; poll \"Show an entry import job\" for the result.\n- Every required field of the form needs a column, and a field takes at most one column.\n- A refused import writes no entries, and its error shows the row and column range and the header of the spreadsheet, so fix the mapping from it and retry.",
1732
2017
  "tag": "Entries",
1733
2018
  "method": "post",
1734
2019
  "path": "/forms/{form_token}/entry_imports",
@@ -1782,27 +2067,832 @@
1782
2067
  ],
1783
2068
  "responseSchema": "EntryImportJob"
1784
2069
  },
1785
- "uploadFormImage": {
1786
- "operationId": "uploadFormImage",
1787
- "summary": "Upload a form image",
1788
- "description": "Uploads an image that belongs to the form itself (a picture choice image, a header image, a background image, one frame of the header carousel, the header image of the form's public query page, or an inline image in the form description / a field description), and returns the image attachment ID. The image is not bound to any form yet; it takes effect only when a form or theme references it later.\nAuthenticate in one of two ways:\n1. Regular API credentials (a Bearer token). `image_type` must then say what the image is for, and can only be `header` (header image), `wallpaper` (background image), `opensearch_header` (public query page header image) or `description` (inline description image): pass the returned `attachment_id` to `header.header_image_attachment_id` / `wallpaper.background_image_attachment_id` of \"Update a form's theme\", or to `header.background_image.attachment_id` of \"Update a public query\"; `description` also returns the image address `url`, which goes into the `<img src>` of the form `description` or of a field's `notes`. When the form is saved, the image belongs to that form (at most 30 per form); images not used by any form within 7 days are cleaned up. Returns 400 `image_type must be one of: field_choice, header, wallpaper, carousel, opensearch_header, description.` when `image_type` is missing or not one of `field_choice` / `header` / `wallpaper` / `carousel` / `opensearch_header` / `description`; `field_choice` / `carousel` have nowhere that accepts an attachment ID, so they can only be uploaded with an upload token, otherwise returns 400.\n2. A one-time upload token `form_image_token` (instead of regular credentials): the token starts with `form_img_tok_`, is valid for 30 minutes, can be used only once, and has already recorded the image purpose (`field_choice` / `header` / `wallpaper` / `carousel` / `opensearch_header` / `description`; `image_type` is then ignored) and the file metadata; the uploaded file's original file name, Content-Type and byte size must match what the token recorded, otherwise returns 400. After a successful upload, do not use the returned `attachment_id` directly; instead reference the same token in a choice's `image_upload_token` in \"Create a form\" / \"Edit a form\", in `header.header_image_upload_token` / `wallpaper.background_image_upload_token` / `header.carousel_images[].image_upload_token` of \"Update a form's theme\", or in `header.background_image.image_upload_token` of \"Update a public query\"; for the `description` purpose nothing references the token, so use the returned `url` directly. Returns 400 `form_image_token is invalid or expired; prepare upload again` when the token does not exist, has expired, has been used, or its user has been deleted.\nReturns 401 when there are neither credentials nor a `form_image_token`. The request must be `multipart/form-data`, with the image binary in the `file` field; a missing file returns 400 `file is required`.",
1789
- "tag": "Uploads",
1790
- "method": "post",
1791
- "path": "/form_image_attachments",
1792
- "params": [],
1793
- "paramNames": [],
1794
- "body": {
1795
- "contentType": "multipart/form-data",
1796
- "required": true,
1797
- "flatten": true
1798
- },
1799
- "successStatus": 201,
2070
+ "estimateSmsTask": {
2071
+ "operationId": "estimateSmsTask",
2072
+ "summary": "Estimate an SMS task",
2073
+ "description": "Picks the entries an SMS task would send to and counts the messages it would charge, without sending anything.\n- `POST /forms/{form_token}/sms_tasks` with the same parameters picks the entries again when it runs, so its count changes if they changed in between.\n- A cooperator picks only the entries visible to their role.",
2074
+ "tag": "Entries",
2075
+ "method": "get",
2076
+ "path": "/forms/{form_token}/sms_tasks/estimate",
2077
+ "params": [
2078
+ {
2079
+ "name": "form_token",
2080
+ "in": "path",
2081
+ "required": true
2082
+ },
2083
+ {
2084
+ "name": "entry_field",
2085
+ "in": "query",
2086
+ "required": true
2087
+ },
2088
+ {
2089
+ "name": "filters",
2090
+ "in": "query",
2091
+ "required": false,
2092
+ "json": true
2093
+ },
2094
+ {
2095
+ "name": "keyword",
2096
+ "in": "query",
2097
+ "required": false
2098
+ },
2099
+ {
2100
+ "name": "view_token",
2101
+ "in": "query",
2102
+ "required": false
2103
+ },
2104
+ {
2105
+ "name": "select_all",
2106
+ "in": "query",
2107
+ "required": false
2108
+ },
2109
+ {
2110
+ "name": "sms_template_id",
2111
+ "in": "query",
2112
+ "required": true
2113
+ },
2114
+ {
2115
+ "name": "placeholder_keys",
2116
+ "in": "query",
2117
+ "required": false,
2118
+ "array": true
2119
+ }
2120
+ ],
2121
+ "paramNames": [
2122
+ "form_token",
2123
+ "entry_field",
2124
+ "filters",
2125
+ "keyword",
2126
+ "view_token",
2127
+ "select_all",
2128
+ "sms_template_id",
2129
+ "placeholder_keys"
2130
+ ],
2131
+ "successStatus": 200,
2132
+ "scopes": [
2133
+ "read_entries"
2134
+ ],
2135
+ "responseSchema": "SMSTaskEstimate"
2136
+ },
2137
+ "listSmsTasks": {
2138
+ "operationId": "listSmsTasks",
2139
+ "summary": "List SMS tasks",
2140
+ "description": "Lists the SMS tasks of this form.",
2141
+ "tag": "Entries",
2142
+ "method": "get",
2143
+ "path": "/forms/{form_token}/sms_tasks",
2144
+ "params": [
2145
+ {
2146
+ "name": "form_token",
2147
+ "in": "path",
2148
+ "required": true
2149
+ },
2150
+ {
2151
+ "name": "status",
2152
+ "in": "query",
2153
+ "required": false
2154
+ },
2155
+ {
2156
+ "name": "start_date",
2157
+ "in": "query",
2158
+ "required": false
2159
+ },
2160
+ {
2161
+ "name": "end_date",
2162
+ "in": "query",
2163
+ "required": false
2164
+ },
2165
+ {
2166
+ "name": "limit",
2167
+ "in": "query",
2168
+ "required": false
2169
+ },
2170
+ {
2171
+ "name": "next",
2172
+ "in": "query",
2173
+ "required": false
2174
+ },
2175
+ {
2176
+ "name": "skip",
2177
+ "in": "query",
2178
+ "required": false
2179
+ }
2180
+ ],
2181
+ "paramNames": [
2182
+ "form_token",
2183
+ "status",
2184
+ "start_date",
2185
+ "end_date",
2186
+ "limit",
2187
+ "next",
2188
+ "skip"
2189
+ ],
2190
+ "successStatus": 200,
2191
+ "scopes": [
2192
+ "read_entries"
2193
+ ],
2194
+ "responseSchema": "EntryTaskListResponse"
2195
+ },
2196
+ "createSmsTask": {
2197
+ "operationId": "createSmsTask",
2198
+ "summary": "Send SMS to entries",
2199
+ "description": "Creates an SMS task that sends a template message to each entry picked.\n- Estimate first with `GET /forms/{form_token}/sms_tasks/estimate` and send with the same parameters; the entries are picked again when this runs.\n- The task may wait in `template_checking` or `content_auditing` while the template or content is under review, and sends once approved.\n- When the estimate's `can_send` is false the task is still created, and when sending finds the quota and balance short it stops, ending `none_sent` or `partial_sent` with the reason in `failed_reason`.\n- Sent messages cannot be recalled, and retrying a request that succeeded sends again.",
2200
+ "tag": "Entries",
2201
+ "method": "post",
2202
+ "path": "/forms/{form_token}/sms_tasks",
2203
+ "params": [
2204
+ {
2205
+ "name": "form_token",
2206
+ "in": "path",
2207
+ "required": true
2208
+ }
2209
+ ],
2210
+ "paramNames": [
2211
+ "form_token"
2212
+ ],
2213
+ "body": {
2214
+ "contentType": "application/json",
2215
+ "required": true,
2216
+ "flatten": true
2217
+ },
2218
+ "successStatus": 201,
2219
+ "scopes": [
2220
+ "write_entries"
2221
+ ],
2222
+ "responseSchema": "EntryTask"
2223
+ },
2224
+ "getSmsTask": {
2225
+ "operationId": "getSmsTask",
2226
+ "summary": "Show an SMS task",
2227
+ "description": "Returns an SMS task of this form with its status and sending progress.",
2228
+ "tag": "Entries",
2229
+ "method": "get",
2230
+ "path": "/forms/{form_token}/sms_tasks/{id}",
2231
+ "params": [
2232
+ {
2233
+ "name": "form_token",
2234
+ "in": "path",
2235
+ "required": true
2236
+ },
2237
+ {
2238
+ "name": "id",
2239
+ "in": "path",
2240
+ "required": true
2241
+ }
2242
+ ],
2243
+ "paramNames": [
2244
+ "form_token",
2245
+ "id"
2246
+ ],
2247
+ "successStatus": 200,
2248
+ "scopes": [
2249
+ "read_entries"
2250
+ ],
2251
+ "responseSchema": "EntryTask"
2252
+ },
2253
+ "updateSmsTask": {
2254
+ "operationId": "updateSmsTask",
2255
+ "summary": "Revoke an SMS task, or change one whose template failed review and send it again",
2256
+ "description": "Revokes an SMS task that has not started sending, or changes a `template_fail` task and sends it again, keeping its `id`.\n- `revoked: true` revokes a `waiting_for_send` or `content_auditing` task so it never sends.\n- Otherwise the body changes a `template_fail` task, keeping its entries. Estimate first with `GET /forms/{form_token}/sms_tasks/{id}/estimate`.\n- A changed task goes through review and sending as one created by `POST /forms/{form_token}/sms_tasks`; sent messages cannot be recalled.",
2257
+ "tag": "Entries",
2258
+ "method": "patch",
2259
+ "path": "/forms/{form_token}/sms_tasks/{id}",
2260
+ "params": [
2261
+ {
2262
+ "name": "form_token",
2263
+ "in": "path",
2264
+ "required": true
2265
+ },
2266
+ {
2267
+ "name": "id",
2268
+ "in": "path",
2269
+ "required": true
2270
+ }
2271
+ ],
2272
+ "paramNames": [
2273
+ "form_token",
2274
+ "id"
2275
+ ],
2276
+ "body": {
2277
+ "contentType": "application/json",
2278
+ "required": true,
2279
+ "flatten": true
2280
+ },
2281
+ "successStatus": 200,
2282
+ "scopes": [
2283
+ "write_entries"
2284
+ ],
2285
+ "responseSchema": "EntryTask"
2286
+ },
2287
+ "listSmsTaskActivities": {
2288
+ "operationId": "listSmsTaskActivities",
2289
+ "summary": "List an SMS task's sending results",
2290
+ "description": "Lists the sending result of each mobile number an SMS task sent to.",
2291
+ "tag": "Entries",
2292
+ "method": "get",
2293
+ "path": "/forms/{form_token}/sms_tasks/{id}/activities",
2294
+ "params": [
2295
+ {
2296
+ "name": "form_token",
2297
+ "in": "path",
2298
+ "required": true
2299
+ },
2300
+ {
2301
+ "name": "id",
2302
+ "in": "path",
2303
+ "required": true
2304
+ },
2305
+ {
2306
+ "name": "status",
2307
+ "in": "query",
2308
+ "required": false
2309
+ },
2310
+ {
2311
+ "name": "mobile",
2312
+ "in": "query",
2313
+ "required": false
2314
+ },
2315
+ {
2316
+ "name": "limit",
2317
+ "in": "query",
2318
+ "required": false
2319
+ },
2320
+ {
2321
+ "name": "next",
2322
+ "in": "query",
2323
+ "required": false
2324
+ },
2325
+ {
2326
+ "name": "skip",
2327
+ "in": "query",
2328
+ "required": false
2329
+ }
2330
+ ],
2331
+ "paramNames": [
2332
+ "form_token",
2333
+ "id",
2334
+ "status",
2335
+ "mobile",
2336
+ "limit",
2337
+ "next",
2338
+ "skip"
2339
+ ],
2340
+ "successStatus": 200,
2341
+ "scopes": [
2342
+ "read_entries"
2343
+ ],
2344
+ "responseSchema": "SMSTaskActivityListResponse"
2345
+ },
2346
+ "estimateSmsTaskResend": {
2347
+ "operationId": "estimateSmsTaskResend",
2348
+ "summary": "Estimate resending an SMS task",
2349
+ "description": "Picks the entries that resending an SMS task would send to and counts the messages it would charge, without sending anything.\n- The entries are the task's entries with the `status` result in its sending results, narrowed by `filters` / `keyword` / `view_token`; no other entry is ever picked.\n- An entry holding several numbers is picked when any of them got that result, and is sent to on all of them.\n- `POST /forms/{form_token}/sms_tasks/{id}/resend` with the same parameters picks the entries again when it runs, so its count changes if they changed in between.\n- A cooperator picks only the entries visible to their role.",
2350
+ "tag": "Entries",
2351
+ "method": "get",
2352
+ "path": "/forms/{form_token}/sms_tasks/{id}/resend/estimate",
2353
+ "params": [
2354
+ {
2355
+ "name": "form_token",
2356
+ "in": "path",
2357
+ "required": true
2358
+ },
2359
+ {
2360
+ "name": "id",
2361
+ "in": "path",
2362
+ "required": true
2363
+ },
2364
+ {
2365
+ "name": "status",
2366
+ "in": "query",
2367
+ "required": true
2368
+ },
2369
+ {
2370
+ "name": "filters",
2371
+ "in": "query",
2372
+ "required": false,
2373
+ "json": true
2374
+ },
2375
+ {
2376
+ "name": "keyword",
2377
+ "in": "query",
2378
+ "required": false
2379
+ },
2380
+ {
2381
+ "name": "view_token",
2382
+ "in": "query",
2383
+ "required": false
2384
+ }
2385
+ ],
2386
+ "paramNames": [
2387
+ "form_token",
2388
+ "id",
2389
+ "status",
2390
+ "filters",
2391
+ "keyword",
2392
+ "view_token"
2393
+ ],
2394
+ "successStatus": 200,
2395
+ "scopes": [
2396
+ "read_entries"
2397
+ ],
2398
+ "responseSchema": "SMSTaskEstimate"
2399
+ },
2400
+ "resendSmsTask": {
2401
+ "operationId": "resendSmsTask",
2402
+ "summary": "Resend an SMS task",
2403
+ "description": "Sends an SMS task again as a new task, to its entries with a given result in its sending results.\n- Estimate first with `GET /forms/{form_token}/sms_tasks/{id}/resend/estimate` and send with the same parameters; the entries are picked again when this runs.\n- The new task keeps the original's template, `entry_field` and `placeholder_keys` and is sent as by `POST /forms/{form_token}/sms_tasks`; the original task is left unchanged.\n- Sent messages cannot be recalled, and retrying a request that succeeded sends again.",
2404
+ "tag": "Entries",
2405
+ "method": "post",
2406
+ "path": "/forms/{form_token}/sms_tasks/{id}/resend",
2407
+ "params": [
2408
+ {
2409
+ "name": "form_token",
2410
+ "in": "path",
2411
+ "required": true
2412
+ },
2413
+ {
2414
+ "name": "id",
2415
+ "in": "path",
2416
+ "required": true
2417
+ }
2418
+ ],
2419
+ "paramNames": [
2420
+ "form_token",
2421
+ "id"
2422
+ ],
2423
+ "body": {
2424
+ "contentType": "application/json",
2425
+ "required": true,
2426
+ "flatten": true
2427
+ },
2428
+ "successStatus": 201,
2429
+ "scopes": [
2430
+ "write_entries"
2431
+ ],
2432
+ "responseSchema": "EntryTask"
2433
+ },
2434
+ "estimateSmsTaskChange": {
2435
+ "operationId": "estimateSmsTaskChange",
2436
+ "summary": "Estimate changing an SMS task whose template failed review",
2437
+ "description": "Estimates changing a `template_fail` SMS task and sending it again, without changing or sending anything.\n- Counts the entries the task holds without reading them: content with variables gives a lower bound (`at_least`), and a table column `entry_field` gives null numbers and cost.",
2438
+ "tag": "Entries",
2439
+ "method": "get",
2440
+ "path": "/forms/{form_token}/sms_tasks/{id}/estimate",
2441
+ "params": [
2442
+ {
2443
+ "name": "form_token",
2444
+ "in": "path",
2445
+ "required": true
2446
+ },
2447
+ {
2448
+ "name": "id",
2449
+ "in": "path",
2450
+ "required": true
2451
+ },
2452
+ {
2453
+ "name": "entry_field",
2454
+ "in": "query",
2455
+ "required": false
2456
+ },
2457
+ {
2458
+ "name": "sms_template_id",
2459
+ "in": "query",
2460
+ "required": true
2461
+ },
2462
+ {
2463
+ "name": "placeholder_keys",
2464
+ "in": "query",
2465
+ "required": false,
2466
+ "array": true
2467
+ }
2468
+ ],
2469
+ "paramNames": [
2470
+ "form_token",
2471
+ "id",
2472
+ "entry_field",
2473
+ "sms_template_id",
2474
+ "placeholder_keys"
2475
+ ],
2476
+ "successStatus": 200,
2477
+ "scopes": [
2478
+ "read_entries"
2479
+ ],
2480
+ "responseSchema": "SMSTaskEstimate"
2481
+ },
2482
+ "sendSmsTask": {
2483
+ "operationId": "sendSmsTask",
2484
+ "summary": "Send an SMS task created with auto_send false",
2485
+ "description": "Sends a task created with `auto_send` false once it has passed review, to the entries fixed at creation; a task given `send_at` waits until then.",
2486
+ "tag": "Entries",
2487
+ "method": "patch",
2488
+ "path": "/forms/{form_token}/sms_tasks/{id}/auto_send",
2489
+ "params": [
2490
+ {
2491
+ "name": "form_token",
2492
+ "in": "path",
2493
+ "required": true
2494
+ },
2495
+ {
2496
+ "name": "id",
2497
+ "in": "path",
2498
+ "required": true
2499
+ }
2500
+ ],
2501
+ "paramNames": [
2502
+ "form_token",
2503
+ "id"
2504
+ ],
2505
+ "successStatus": 200,
2506
+ "scopes": [
2507
+ "write_entries"
2508
+ ],
2509
+ "responseSchema": "EntryTask"
2510
+ },
2511
+ "estimateEmailTask": {
2512
+ "operationId": "estimateEmailTask",
2513
+ "summary": "Estimate an email task",
2514
+ "description": "Picks the entries an email task would send to and checks the quota, without sending anything.\n- `POST /forms/{form_token}/email_tasks` with the same parameters picks the entries again when it runs, so its count changes if they changed in between.\n- A cooperator picks only the entries visible to their role.",
2515
+ "tag": "Entries",
2516
+ "method": "get",
2517
+ "path": "/forms/{form_token}/email_tasks/estimate",
2518
+ "params": [
2519
+ {
2520
+ "name": "form_token",
2521
+ "in": "path",
2522
+ "required": true
2523
+ },
2524
+ {
2525
+ "name": "entry_field",
2526
+ "in": "query",
2527
+ "required": true
2528
+ },
2529
+ {
2530
+ "name": "filters",
2531
+ "in": "query",
2532
+ "required": false,
2533
+ "json": true
2534
+ },
2535
+ {
2536
+ "name": "keyword",
2537
+ "in": "query",
2538
+ "required": false
2539
+ },
2540
+ {
2541
+ "name": "view_token",
2542
+ "in": "query",
2543
+ "required": false
2544
+ },
2545
+ {
2546
+ "name": "select_all",
2547
+ "in": "query",
2548
+ "required": false
2549
+ },
2550
+ {
2551
+ "name": "smtp_account_id",
2552
+ "in": "query",
2553
+ "required": false
2554
+ }
2555
+ ],
2556
+ "paramNames": [
2557
+ "form_token",
2558
+ "entry_field",
2559
+ "filters",
2560
+ "keyword",
2561
+ "view_token",
2562
+ "select_all",
2563
+ "smtp_account_id"
2564
+ ],
2565
+ "successStatus": 200,
2566
+ "scopes": [
2567
+ "read_entries"
2568
+ ],
2569
+ "responseSchema": "EmailTaskEstimate"
2570
+ },
2571
+ "listEmailTasks": {
2572
+ "operationId": "listEmailTasks",
2573
+ "summary": "List email tasks",
2574
+ "description": "Lists the email tasks of this form.",
2575
+ "tag": "Entries",
2576
+ "method": "get",
2577
+ "path": "/forms/{form_token}/email_tasks",
2578
+ "params": [
2579
+ {
2580
+ "name": "form_token",
2581
+ "in": "path",
2582
+ "required": true
2583
+ },
2584
+ {
2585
+ "name": "status",
2586
+ "in": "query",
2587
+ "required": false
2588
+ },
2589
+ {
2590
+ "name": "start_date",
2591
+ "in": "query",
2592
+ "required": false
2593
+ },
2594
+ {
2595
+ "name": "end_date",
2596
+ "in": "query",
2597
+ "required": false
2598
+ },
2599
+ {
2600
+ "name": "limit",
2601
+ "in": "query",
2602
+ "required": false
2603
+ },
2604
+ {
2605
+ "name": "next",
2606
+ "in": "query",
2607
+ "required": false
2608
+ },
2609
+ {
2610
+ "name": "skip",
2611
+ "in": "query",
2612
+ "required": false
2613
+ }
2614
+ ],
2615
+ "paramNames": [
2616
+ "form_token",
2617
+ "status",
2618
+ "start_date",
2619
+ "end_date",
2620
+ "limit",
2621
+ "next",
2622
+ "skip"
2623
+ ],
2624
+ "successStatus": 200,
2625
+ "scopes": [
2626
+ "read_entries"
2627
+ ],
2628
+ "responseSchema": "EntryTaskListResponse"
2629
+ },
2630
+ "createEmailTask": {
2631
+ "operationId": "createEmailTask",
2632
+ "summary": "Send email to entries",
2633
+ "description": "Creates an email task that sends an email to each entry picked, starting right away.\n- Estimate first with `GET /forms/{form_token}/email_tasks/estimate` and send with the same parameters; the entries are picked again when this runs.\n- When the estimate's `can_send` is false the task is still created, and when sending finds the quota and balance short it stops, ending `none_sent` or `partial_sent` with the reason in `failed_reason`.\n- Sent emails cannot be recalled, and retrying a request that succeeded sends again.",
2634
+ "tag": "Entries",
2635
+ "method": "post",
2636
+ "path": "/forms/{form_token}/email_tasks",
2637
+ "params": [
2638
+ {
2639
+ "name": "form_token",
2640
+ "in": "path",
2641
+ "required": true
2642
+ }
2643
+ ],
2644
+ "paramNames": [
2645
+ "form_token"
2646
+ ],
2647
+ "body": {
2648
+ "contentType": "application/json",
2649
+ "required": true,
2650
+ "flatten": true
2651
+ },
2652
+ "successStatus": 201,
2653
+ "scopes": [
2654
+ "write_entries"
2655
+ ],
2656
+ "responseSchema": "EntryTask"
2657
+ },
2658
+ "getEmailTask": {
2659
+ "operationId": "getEmailTask",
2660
+ "summary": "Show an email task",
2661
+ "description": "Returns an email task of this form with its status and sending progress.",
2662
+ "tag": "Entries",
2663
+ "method": "get",
2664
+ "path": "/forms/{form_token}/email_tasks/{id}",
2665
+ "params": [
2666
+ {
2667
+ "name": "form_token",
2668
+ "in": "path",
2669
+ "required": true
2670
+ },
2671
+ {
2672
+ "name": "id",
2673
+ "in": "path",
2674
+ "required": true
2675
+ }
2676
+ ],
2677
+ "paramNames": [
2678
+ "form_token",
2679
+ "id"
2680
+ ],
2681
+ "successStatus": 200,
2682
+ "scopes": [
2683
+ "read_entries"
2684
+ ],
2685
+ "responseSchema": "EntryTask"
2686
+ },
2687
+ "listEmailTaskActivities": {
2688
+ "operationId": "listEmailTaskActivities",
2689
+ "summary": "List an email task's sending results",
2690
+ "description": "Lists the sending result of each entry an email task sent to.",
2691
+ "tag": "Entries",
2692
+ "method": "get",
2693
+ "path": "/forms/{form_token}/email_tasks/{id}/activities",
2694
+ "params": [
2695
+ {
2696
+ "name": "form_token",
2697
+ "in": "path",
2698
+ "required": true
2699
+ },
2700
+ {
2701
+ "name": "id",
2702
+ "in": "path",
2703
+ "required": true
2704
+ },
2705
+ {
2706
+ "name": "status",
2707
+ "in": "query",
2708
+ "required": false
2709
+ },
2710
+ {
2711
+ "name": "email",
2712
+ "in": "query",
2713
+ "required": false
2714
+ },
2715
+ {
2716
+ "name": "limit",
2717
+ "in": "query",
2718
+ "required": false
2719
+ },
2720
+ {
2721
+ "name": "next",
2722
+ "in": "query",
2723
+ "required": false
2724
+ },
2725
+ {
2726
+ "name": "skip",
2727
+ "in": "query",
2728
+ "required": false
2729
+ }
2730
+ ],
2731
+ "paramNames": [
2732
+ "form_token",
2733
+ "id",
2734
+ "status",
2735
+ "email",
2736
+ "limit",
2737
+ "next",
2738
+ "skip"
2739
+ ],
2740
+ "successStatus": 200,
2741
+ "scopes": [
2742
+ "read_entries"
2743
+ ],
2744
+ "responseSchema": "EmailTaskActivityListResponse"
2745
+ },
2746
+ "estimateEmailTaskResend": {
2747
+ "operationId": "estimateEmailTaskResend",
2748
+ "summary": "Estimate resending an email task",
2749
+ "description": "Picks the entries that resending an email task would send to and checks the quota, without sending anything.\n- The entries are the task's entries with the `status` result in its sending results, narrowed by `filters` / `keyword` / `view_token`; no other entry is ever picked.\n- The emails go through the task's custom SMTP account, using no quota, while the account can still use it.\n- `POST /forms/{form_token}/email_tasks/{id}/resend` with the same parameters picks the entries again when it runs, so its count changes if they changed in between.\n- A cooperator picks only the entries visible to their role.",
2750
+ "tag": "Entries",
2751
+ "method": "get",
2752
+ "path": "/forms/{form_token}/email_tasks/{id}/resend/estimate",
2753
+ "params": [
2754
+ {
2755
+ "name": "form_token",
2756
+ "in": "path",
2757
+ "required": true
2758
+ },
2759
+ {
2760
+ "name": "id",
2761
+ "in": "path",
2762
+ "required": true
2763
+ },
2764
+ {
2765
+ "name": "status",
2766
+ "in": "query",
2767
+ "required": true
2768
+ },
2769
+ {
2770
+ "name": "filters",
2771
+ "in": "query",
2772
+ "required": false,
2773
+ "json": true
2774
+ },
2775
+ {
2776
+ "name": "keyword",
2777
+ "in": "query",
2778
+ "required": false
2779
+ },
2780
+ {
2781
+ "name": "view_token",
2782
+ "in": "query",
2783
+ "required": false
2784
+ }
2785
+ ],
2786
+ "paramNames": [
2787
+ "form_token",
2788
+ "id",
2789
+ "status",
2790
+ "filters",
2791
+ "keyword",
2792
+ "view_token"
2793
+ ],
2794
+ "successStatus": 200,
2795
+ "scopes": [
2796
+ "read_entries"
2797
+ ],
2798
+ "responseSchema": "EmailTaskEstimate"
2799
+ },
2800
+ "resendEmailTask": {
2801
+ "operationId": "resendEmailTask",
2802
+ "summary": "Resend an email task",
2803
+ "description": "Sends an email task again as a new task, to its entries with a given result in its sending results.\n- Estimate first with `GET /forms/{form_token}/email_tasks/{id}/resend/estimate` and send with the same parameters; the entries are picked again when this runs.\n- The new task keeps the original's `entry_field`, subject, body and custom SMTP account and is sent as by `POST /forms/{form_token}/email_tasks`; the original task is left unchanged.\n- Sent emails cannot be recalled, and retrying a request that succeeded sends again.",
2804
+ "tag": "Entries",
2805
+ "method": "post",
2806
+ "path": "/forms/{form_token}/email_tasks/{id}/resend",
2807
+ "params": [
2808
+ {
2809
+ "name": "form_token",
2810
+ "in": "path",
2811
+ "required": true
2812
+ },
2813
+ {
2814
+ "name": "id",
2815
+ "in": "path",
2816
+ "required": true
2817
+ }
2818
+ ],
2819
+ "paramNames": [
2820
+ "form_token",
2821
+ "id"
2822
+ ],
2823
+ "body": {
2824
+ "contentType": "application/json",
2825
+ "required": true,
2826
+ "flatten": true
2827
+ },
2828
+ "successStatus": 201,
2829
+ "scopes": [
2830
+ "write_entries"
2831
+ ],
2832
+ "responseSchema": "EntryTask"
2833
+ },
2834
+ "uploadFormImage": {
2835
+ "operationId": "uploadFormImage",
2836
+ "summary": "Upload a form image",
2837
+ "description": "Uploads an image for a form; it takes effect once a form, theme or public query references it.\n- `description` returns a `url` for an `<img src>` in the form `description` or a field's `notes`, at most 30 per form; every other type returns an `attachment_id` for the property that names that `image_type`.\n- An `attachment_id` is taken only where its `image_type` goes, and only while the image is unused or already that form's; an image nothing uses within 7 days is deleted.",
2838
+ "tag": "Uploads",
2839
+ "method": "post",
2840
+ "path": "/form_image_attachments",
2841
+ "params": [],
2842
+ "paramNames": [],
2843
+ "body": {
2844
+ "contentType": "multipart/form-data",
2845
+ "required": true,
2846
+ "flatten": true
2847
+ },
2848
+ "successStatus": 201,
1800
2849
  "scopes": [
1801
2850
  "forms",
1802
2851
  "form_setting"
1803
2852
  ],
1804
2853
  "responseSchema": "UploadedFormImage"
1805
2854
  },
2855
+ "prepareFormImageUpload": {
2856
+ "operationId": "prepareFormImageUpload",
2857
+ "summary": "Prepare a form image upload",
2858
+ "description": "Issues a one-time link for uploading a local image for a form, without sending its bytes in a request body.\n- With a shell that can read the file and reach the network, run `curl -F file=@<path> <upload_url>`, then use the returned `attachment_id` (or `url`) where its `image_type` goes.\n- Without one, or when curl fails (for example under a sandbox's network limits), give a public image URL or a small Base64 image where the image goes.",
2859
+ "tag": "Uploads",
2860
+ "method": "post",
2861
+ "path": "/form_image_upload_urls",
2862
+ "params": [],
2863
+ "paramNames": [],
2864
+ "body": {
2865
+ "contentType": "application/json",
2866
+ "required": true,
2867
+ "flatten": true
2868
+ },
2869
+ "successStatus": 201,
2870
+ "scopes": [
2871
+ "forms",
2872
+ "form_setting"
2873
+ ],
2874
+ "responseSchema": "PreparedUpload"
2875
+ },
2876
+ "uploadUserLicenseAttachment": {
2877
+ "operationId": "uploadUserLicenseAttachment",
2878
+ "summary": "Upload a license image",
2879
+ "description": "Uploads a business license or ID card image for `POST /sms/signatures` and `PATCH /sms/signatures/{id}`; only the uploader can use it.",
2880
+ "tag": "Uploads",
2881
+ "method": "post",
2882
+ "path": "/user/license_attachments",
2883
+ "params": [],
2884
+ "paramNames": [],
2885
+ "body": {
2886
+ "contentType": "multipart/form-data",
2887
+ "required": true,
2888
+ "flatten": true
2889
+ },
2890
+ "successStatus": 201,
2891
+ "scopes": [
2892
+ "users"
2893
+ ],
2894
+ "responseSchema": "UploadedLicenseAttachment"
2895
+ },
1806
2896
  "listTables": {
1807
2897
  "operationId": "listTables",
1808
2898
  "summary": "List tables",
@@ -1861,7 +2951,7 @@
1861
2951
  "createTable": {
1862
2952
  "operationId": "createTable",
1863
2953
  "summary": "Create a table",
1864
- "description": "Creates a table. Same as creating a form, except:\n- field types can only be the column types tables support: `TextArea`, `RadioButton`, `CheckBox`, `BooleanField`, `MobileField`, `NumberField`, `DateTimeField`, `EmailField`, `LinkField`, `AttachmentField`, `FormulaField`, `RelationField`;\n- attachment columns do not accept limit properties such as count / size limits, and number / formula columns have limited precision; a violation returns 400 listing the offending columns;\n- choice columns do not get an \"Other\" choice; `folder_token` must be a table folder; `layout` is not supported.\n- with `with_default_entries: true`, 3 blank entries are added after creation (as when creating a table in the product); by default none are added.\nReturns the table structure, including fields and views.",
2954
+ "description": "Creates a table. Same as creating a form, except:\n- field types can only be the column types tables support: `TextArea`, `RadioButton`, `CheckBox`, `BooleanField`, `MobileField`, `NumberField`, `DateTimeField`, `EmailField`, `LinkField`, `AttachmentField`, `FormulaField`, `RelationField`, `SequenceField`;\n- attachment columns do not accept limit properties such as count / size limits, and number / formula columns have limited precision; a violation returns 400 listing the offending columns;\n- choice columns do not get an \"Other\" choice; `folder_token` must be a table folder; `layout` is not supported.\n- with `with_default_entries: true`, 3 blank entries are added after creation (as when creating a table in the product); by default none are added.\nReturns the table structure, including fields and views.",
1865
2955
  "tag": "Tables",
1866
2956
  "method": "post",
1867
2957
  "path": "/tables",
@@ -2033,7 +3123,8 @@
2033
3123
  {
2034
3124
  "name": "fields",
2035
3125
  "in": "query",
2036
- "required": false
3126
+ "required": false,
3127
+ "array": true
2037
3128
  },
2038
3129
  {
2039
3130
  "name": "filters",
@@ -2233,7 +3324,8 @@
2233
3324
  {
2234
3325
  "name": "fields",
2235
3326
  "in": "query",
2236
- "required": false
3327
+ "required": false,
3328
+ "array": true
2237
3329
  },
2238
3330
  {
2239
3331
  "name": "include_overview",
@@ -2390,7 +3482,8 @@
2390
3482
  {
2391
3483
  "name": "fields",
2392
3484
  "in": "query",
2393
- "required": false
3485
+ "required": false,
3486
+ "array": true
2394
3487
  },
2395
3488
  {
2396
3489
  "name": "include_overview",
@@ -2444,7 +3537,8 @@
2444
3537
  {
2445
3538
  "name": "fields",
2446
3539
  "in": "query",
2447
- "required": false
3540
+ "required": false,
3541
+ "array": true
2448
3542
  },
2449
3543
  {
2450
3544
  "name": "include_labels",
@@ -2948,7 +4042,7 @@
2948
4042
  "createCheckinCampaign": {
2949
4043
  "operationId": "createCheckinCampaign",
2950
4044
  "summary": "Create a check-in campaign",
2951
- "description": "Turns check-in on for a form. The caller must be able to reach the form.\n- Same limits as picking a form in the web app: returns 400 when the form has WeChat red envelope, lucky wheel, certificate or confirmation code on, is bound to a plugin that takes over the after-submit redirect (a check-in campaign with a check-in code included), or its after-submit action is not the default message. Turn that off first.\n- Only one check-in campaign is in effect on a form at a time; creating one turns off the one that was in effect.\n- Without `checkin_field_api_code`, the form's first mobile field is used, then its first name field; with neither, returns 400. The check-in field cannot be changed afterwards.\n- The campaign belongs to the caller, or to the account owner with an account-level credential. The caller is also added as a verifier.\n- `enable_checkin_code` (on by default) rewrites the form's after-submit redirect and data push URL to the check-in code page, and overrides any Webhook already on the form (and the other way round).",
4045
+ "description": "Turns check-in on for a form.\n- Refused while the after-submit step of the form is taken by something else (WeChat red envelope, lucky wheel, certificate, confirmation code, another plugin or the check-in code of another campaign, or an after-submit action other than the default message); the message names it, so turn that off first.\n- Only one check-in campaign is in effect on a form at a time; creating one turns off the one that was in effect.",
2952
4046
  "tag": "Plugins",
2953
4047
  "method": "post",
2954
4048
  "path": "/plugins/checkin/campaigns",
@@ -3033,7 +4127,7 @@
3033
4127
  "paramNames": [
3034
4128
  "id"
3035
4129
  ],
3036
- "successStatus": 204,
4130
+ "successStatus": 200,
3037
4131
  "scopes": [
3038
4132
  "form_setting"
3039
4133
  ]
@@ -3197,6 +4291,29 @@
3197
4291
  ],
3198
4292
  "responseSchema": "Folder"
3199
4293
  },
4294
+ "getFolder": {
4295
+ "operationId": "getFolder",
4296
+ "summary": "Show a folder",
4297
+ "description": "Returns one folder the current user can manage.",
4298
+ "tag": "Folders",
4299
+ "method": "get",
4300
+ "path": "/folders/{folder_token}",
4301
+ "params": [
4302
+ {
4303
+ "name": "folder_token",
4304
+ "in": "path",
4305
+ "required": true
4306
+ }
4307
+ ],
4308
+ "paramNames": [
4309
+ "folder_token"
4310
+ ],
4311
+ "successStatus": 200,
4312
+ "scopes": [
4313
+ "forms"
4314
+ ],
4315
+ "responseSchema": "Folder"
4316
+ },
3200
4317
  "listPredefinedChoicesSets": {
3201
4318
  "operationId": "listPredefinedChoicesSets",
3202
4319
  "summary": "List predefined choice sets",
@@ -3340,7 +4457,7 @@
3340
4457
  "getBillingAccount": {
3341
4458
  "operationId": "getBillingAccount",
3342
4459
  "summary": "Show the current account",
3343
- "description": "Returns the account the current credential belongs to: plan, this month's usage and quota, feature trial status, and the bound payment merchant / WeChat Pay / Alipay accounts.\n`usage` is null when the account has no usage record for this month yet; for an add-on service with unlimited quota, only `unit`, `unlimited: true` and `consumed_quota` are returned.",
4460
+ "description": "Returns the account the current credential belongs to: plan, this month's usage and quota, feature trial status, and the bound payment merchant / WeChat Pay / Alipay accounts.\n`usage` is null when the account has no usage record for this month yet.",
3344
4461
  "tag": "Account",
3345
4462
  "method": "get",
3346
4463
  "path": "/billing_account",
@@ -3436,10 +4553,25 @@
3436
4553
  ],
3437
4554
  "responseSchema": "BillingAccountPaymentHistoriesResponse"
3438
4555
  },
4556
+ "listBillingAccountPaidFeatures": {
4557
+ "operationId": "listBillingAccountPaidFeatures",
4558
+ "summary": "List the paid features and whether the account has each",
4559
+ "description": "Lists every paid feature with its Chinese name and whether the account has it now; one bought or granted on its own counts. Quotas, and whether each add-on service can still be used, are in `usage` of `GET /billing_account`.",
4560
+ "tag": "Account",
4561
+ "method": "get",
4562
+ "path": "/billing_account/paid_features",
4563
+ "params": [],
4564
+ "paramNames": [],
4565
+ "successStatus": 200,
4566
+ "scopes": [
4567
+ "users"
4568
+ ],
4569
+ "responseSchema": "BillingAccountPaidFeaturesResponse"
4570
+ },
3439
4571
  "getTradeMerchant": {
3440
4572
  "operationId": "getTradeMerchant",
3441
4573
  "summary": "Show the 小金商户",
3442
- "description": "Returns the current account's 小金商户 (the merchant Jinshuju collects payments for) and its onboarding progress: merchant profile, legal entity and contact details, balance, settlement details, payment channel authorization, freeze status, bound payment forms, and the URL for the next step.\n- When the account has no 小金商户 yet, it still returns 200: `merchant_stage` says how far the onboarding application has got, `merchant` / `subject` / `contact` / `balance` / `settlement` / `freeze` are null, and `channels` is an empty array.\n- An account that collects payments through its own WeChat Pay / Alipay merchant (direct connection) has no rate, settlement account, balance or onboarding status on Jinshuju, and none of these is given here.\n- Sensitive fields such as bank card numbers, mobile numbers, emails and ID numbers are masked by the rules of the product's \"Fund account information\" page: 4 characters are kept at each end (mobile numbers keep the first 3 and last 4), and the middle is replaced with `*`.\n- `payment_form_tokens` holds only the payment forms the current identity can access (quick payment forms included); with an account-level credential, it is computed for the account owner.\n- `balance` is null when the balance lookup fails; that does not mean the balance is 0.",
4574
+ "description": "Returns the current account's 小金商户 (the merchant Jinshuju collects payments for) and its onboarding progress: merchant profile, legal entity and contact details, balance, settlement details, payment channel authorization, freeze status, bound payment forms, and the URL for the next step.\n- An account without a 小金商户 still gets 200, with `merchant_stage` saying how far onboarding has got; an account that collects payments through its own WeChat Pay / Alipay merchant (direct connection) has no rate, settlement account, balance or onboarding status on Jinshuju.\n- `balance` is null when the balance lookup failed, which does not mean the balance is 0.",
3443
4575
  "tag": "Trade",
3444
4576
  "method": "get",
3445
4577
  "path": "/trade/merchant",
@@ -3454,7 +4586,7 @@
3454
4586
  "listTradeTransactions": {
3455
4587
  "operationId": "listTradeTransactions",
3456
4588
  "summary": "List payment orders",
3457
- "description": "Lists the payment orders of the 小金商户 (orders customers place by paying on payment forms), newest order first. Only 小金商户 orders are included: orders collected through a direct connection, simulated-payment orders, and unpaid quick payment records are all left out.\n- The date range filters on order time, in Beijing time (UTC+8); `start_date` / `end_date` are both inclusive and must be passed together. Without them, it defaults to the last 90 days; with `order_no` and no dates, there is no time limit. `queried_range` in the response gives the range actually used (both ends null when there is no time limit).\n- Detail limits: the date range spans at most 90 days, and at most 200 orders match. Past either limit, no details are returned: `transactions` is null, `detail_limit` says why, and `stats` gives the transaction amount, paid count and order count for the range; narrow the range and query again.\n- Paging: `limit` is the page size (default and maximum 50), and `next` is a page number. When there is another page, the response carries `next` (the next page number); pass it back as is. `page_info.total` is the total number of matching orders.\n- Returns 404 when the account has no 小金商户.",
4589
+ "description": "Lists the payment orders of the 小金商户 (orders customers place by paying on payment forms), filtered and sorted newest first by order time. Only 小金商户 orders are included: orders collected through a direct connection, simulated-payment orders, and unpaid quick payment records are all left out.\n- Without dates, covers the last 90 days.\n- Records are returned only for a range of at most 90 days with at most 200 matching orders: past either, `transactions` is null, `detail_limit` says why, and `stats` still covers the range; narrow the range to get the records.",
3458
4590
  "tag": "Trade",
3459
4591
  "method": "get",
3460
4592
  "path": "/trade/transactions",
@@ -3574,7 +4706,7 @@
3574
4706
  "listTradeRefunds": {
3575
4707
  "operationId": "listTradeRefunds",
3576
4708
  "summary": "List refunds",
3577
- "description": "Lists the refunds the 小金商户 issued to customers, with statistics. Refunds are filtered and sorted newest first by the time they were issued, not by the order time or the time the refund arrived. Only a 小金商户 has refund records; an account collecting payments through a direct connection has no data here.\n- The date range is in Beijing time (UTC+8); both ends are inclusive and must be passed together. Without them, it defaults to the last 90 days; with `refund_no` and no dates, there is no time limit. `queried_range` gives the range actually used.\n- `include` decides what is returned: `both` (default) returns `stats` and `refunds` together, `stats` returns the statistics alone, `records` returns the records alone.\n- The detail limits are those of \"List payment orders\" (at most 90 days, 200 records): past them, `refunds` is null, `detail_limit` says why, and `stats` is returned as usual.\n- `stats`: the count and amount of all matching refunds, the count and amount per status, the number of distinct orders with a successful refund, and the trend by refund issue date (only when there is a date range; by day under 90 days, by week from 90 days, by month from 365 days, periods without refunds filled with zero). In the trend, order counts are deduplicated within each period, so the sum over periods can exceed the total order count.\n- Paging works as in \"List payment orders\"; with `include=stats`, `next` is not returned.\n- Returns 404 when the account has no 小金商户.",
4709
+ "description": "Lists the refunds the 小金商户 issued to customers, with statistics. Refunds are filtered and sorted newest first by the time they were issued, not by the order time or the time the refund arrived.\n- Without dates, covers the last 90 days.\n- Records are returned only for a range of at most 90 days with at most 200 matching refunds: past either, `refunds` is null, `detail_limit` says why, and `stats` still covers the range.",
3578
4710
  "tag": "Trade",
3579
4711
  "method": "get",
3580
4712
  "path": "/trade/refunds",
@@ -3633,28 +4765,207 @@
3633
4765
  "listTradePayouts": {
3634
4766
  "operationId": "listTradePayouts",
3635
4767
  "summary": "List payouts",
3636
- "description": "Lists the payouts the 小金商户 made from its Jinshuju balance to its own bank account, with statistics. Payouts are filtered and sorted newest first by request time; each record carries the settlement date range the payout covers, but it cannot be filtered on. Only a 小金商户 has payout records; an account collecting payments through a direct connection has no data here.\n- The date range is in Beijing time (UTC+8); both ends are inclusive and must be passed together. Without them, it defaults to the last 90 days. `queried_range` gives the range actually used.\n- `include` decides what is returned: `both` (default) returns `stats` and `payouts` together, `stats` returns the statistics alone, `records` returns the records alone.\n- The detail limits are those of \"List payment orders\" (at most 90 days, 200 records): past them, `payouts` is null, `detail_limit` says why, and `stats` is returned as usual.\n- `stats`: the count and amount of all matching payouts, and the count and amount per status.\n- Paging works as in \"List payment orders\"; with `include=stats`, `next` is not returned.\n- For the balance awaiting payout and the settlement cycle, see \"Show the 小金商户\". Returns 404 when the account has no 小金商户.",
4768
+ "description": "Lists the payouts the 小金商户 made from its Jinshuju balance to its own bank account, with statistics. Payouts are filtered and sorted newest first by request time.\n- Without dates, covers the last 90 days.\n- Records are returned only for a range of at most 90 days with at most 200 matching payouts: past either, `payouts` is null, `detail_limit` says why, and `stats` still covers the range.",
3637
4769
  "tag": "Trade",
3638
4770
  "method": "get",
3639
- "path": "/trade/payouts",
4771
+ "path": "/trade/payouts",
4772
+ "params": [
4773
+ {
4774
+ "name": "start_date",
4775
+ "in": "query",
4776
+ "required": false
4777
+ },
4778
+ {
4779
+ "name": "end_date",
4780
+ "in": "query",
4781
+ "required": false
4782
+ },
4783
+ {
4784
+ "name": "status",
4785
+ "in": "query",
4786
+ "required": false
4787
+ },
4788
+ {
4789
+ "name": "include",
4790
+ "in": "query",
4791
+ "required": false
4792
+ },
4793
+ {
4794
+ "name": "limit",
4795
+ "in": "query",
4796
+ "required": false
4797
+ },
4798
+ {
4799
+ "name": "next",
4800
+ "in": "query",
4801
+ "required": false
4802
+ }
4803
+ ],
4804
+ "paramNames": [
4805
+ "start_date",
4806
+ "end_date",
4807
+ "status",
4808
+ "include",
4809
+ "limit",
4810
+ "next"
4811
+ ],
4812
+ "successStatus": 200,
4813
+ "scopes": [
4814
+ "users"
4815
+ ],
4816
+ "responseSchema": "TradePayoutListResponse"
4817
+ },
4818
+ "listSmsAuditStatuses": {
4819
+ "operationId": "listSmsAuditStatuses",
4820
+ "summary": "Show SMS signature review status",
4821
+ "description": "Lists the SMS signatures the current user applied for and the SMS templates under each signature, with review status and the reason a review failed, newest signature first.\nReturns only the current user's own signatures, not those of other members of the account; with an account-level credential, returns the account owner's signatures. Any member can call it; admin is not required.",
4822
+ "tag": "Account",
4823
+ "method": "get",
4824
+ "path": "/sms/audit_statuses",
4825
+ "params": [],
4826
+ "paramNames": [],
4827
+ "successStatus": 200,
4828
+ "scopes": [
4829
+ "users"
4830
+ ],
4831
+ "responseSchema": "SMSAuditStatusResponse"
4832
+ },
4833
+ "listSmsSignatures": {
4834
+ "operationId": "listSmsSignatures",
4835
+ "summary": "List SMS signatures",
4836
+ "description": "Lists the account's SMS signatures, newest first. Pagination as in the form list; 50 per page by default, up to 1000.",
4837
+ "tag": "Account",
4838
+ "method": "get",
4839
+ "path": "/sms/signatures",
4840
+ "params": [
4841
+ {
4842
+ "name": "status",
4843
+ "in": "query",
4844
+ "required": false
4845
+ },
4846
+ {
4847
+ "name": "limit",
4848
+ "in": "query",
4849
+ "required": false
4850
+ },
4851
+ {
4852
+ "name": "next",
4853
+ "in": "query",
4854
+ "required": false
4855
+ },
4856
+ {
4857
+ "name": "skip",
4858
+ "in": "query",
4859
+ "required": false
4860
+ }
4861
+ ],
4862
+ "paramNames": [
4863
+ "status",
4864
+ "limit",
4865
+ "next",
4866
+ "skip"
4867
+ ],
4868
+ "successStatus": 200,
4869
+ "scopes": [
4870
+ "users"
4871
+ ],
4872
+ "responseSchema": "SMSSignatureListResponse"
4873
+ },
4874
+ "createSmsSignature": {
4875
+ "operationId": "createSmsSignature",
4876
+ "summary": "Apply for an SMS signature",
4877
+ "description": "Applies for an SMS signature and submits it for review; follow the result with `GET /sms/signatures`.\n- Upload the images with `POST /user/license_attachments` first.\n- A signature the account already has under review or approved is returned unchanged.\n- The materials replace the user's ID card information, and the account's business license unless the account has passed license verification.",
4878
+ "tag": "Account",
4879
+ "method": "post",
4880
+ "path": "/sms/signatures",
4881
+ "params": [],
4882
+ "paramNames": [],
4883
+ "body": {
4884
+ "contentType": "application/json",
4885
+ "required": true,
4886
+ "flatten": true
4887
+ },
4888
+ "successStatus": 201,
4889
+ "scopes": [
4890
+ "users"
4891
+ ],
4892
+ "responseSchema": "SMSSignature"
4893
+ },
4894
+ "updateSmsSignature": {
4895
+ "operationId": "updateSmsSignature",
4896
+ "summary": "Update an SMS signature",
4897
+ "description": "Changes a rejected signature of the account and submits it for review again.\n- After a text change, its templates stay under the old text until each is updated with `PATCH /sms/templates/{id}`.\n- The materials replace the user's ID card information, and the account's business license unless the account has passed license verification.",
4898
+ "tag": "Account",
4899
+ "method": "patch",
4900
+ "path": "/sms/signatures/{id}",
4901
+ "params": [
4902
+ {
4903
+ "name": "id",
4904
+ "in": "path",
4905
+ "required": true
4906
+ }
4907
+ ],
4908
+ "paramNames": [
4909
+ "id"
4910
+ ],
4911
+ "body": {
4912
+ "contentType": "application/json",
4913
+ "required": true,
4914
+ "flatten": true
4915
+ },
4916
+ "successStatus": 200,
4917
+ "scopes": [
4918
+ "users"
4919
+ ],
4920
+ "responseSchema": "SMSSignature"
4921
+ },
4922
+ "deleteSmsSignature": {
4923
+ "operationId": "deleteSmsSignature",
4924
+ "summary": "Delete an SMS signature",
4925
+ "description": "Deletes a rejected signature of the account together with all its templates, including other members' ones.",
4926
+ "tag": "Account",
4927
+ "method": "delete",
4928
+ "path": "/sms/signatures/{id}",
4929
+ "params": [
4930
+ {
4931
+ "name": "id",
4932
+ "in": "path",
4933
+ "required": true
4934
+ }
4935
+ ],
4936
+ "paramNames": [
4937
+ "id"
4938
+ ],
4939
+ "successStatus": 200,
4940
+ "scopes": [
4941
+ "users"
4942
+ ]
4943
+ },
4944
+ "listSmsTemplates": {
4945
+ "operationId": "listSmsTemplates",
4946
+ "summary": "List SMS templates",
4947
+ "description": "Lists the templates the current user created and the ones other members share, newest first. Pagination as in the form list; 50 per page by default, up to 1000.",
4948
+ "tag": "Account",
4949
+ "method": "get",
4950
+ "path": "/sms/templates",
3640
4951
  "params": [
3641
4952
  {
3642
- "name": "start_date",
4953
+ "name": "q",
3643
4954
  "in": "query",
3644
4955
  "required": false
3645
4956
  },
3646
4957
  {
3647
- "name": "end_date",
4958
+ "name": "scope",
3648
4959
  "in": "query",
3649
4960
  "required": false
3650
4961
  },
3651
4962
  {
3652
- "name": "status",
4963
+ "name": "signature_id",
3653
4964
  "in": "query",
3654
4965
  "required": false
3655
4966
  },
3656
4967
  {
3657
- "name": "include",
4968
+ "name": "status",
3658
4969
  "in": "query",
3659
4970
  "required": false
3660
4971
  },
@@ -3667,36 +4978,112 @@
3667
4978
  "name": "next",
3668
4979
  "in": "query",
3669
4980
  "required": false
4981
+ },
4982
+ {
4983
+ "name": "skip",
4984
+ "in": "query",
4985
+ "required": false
3670
4986
  }
3671
4987
  ],
3672
4988
  "paramNames": [
3673
- "start_date",
3674
- "end_date",
4989
+ "q",
4990
+ "scope",
4991
+ "signature_id",
3675
4992
  "status",
3676
- "include",
3677
4993
  "limit",
3678
- "next"
4994
+ "next",
4995
+ "skip"
3679
4996
  ],
3680
4997
  "successStatus": 200,
3681
4998
  "scopes": [
3682
4999
  "users"
3683
5000
  ],
3684
- "responseSchema": "TradePayoutListResponse"
5001
+ "responseSchema": "SMSTemplateListResponse"
3685
5002
  },
3686
- "listSmsAuditStatuses": {
3687
- "operationId": "listSmsAuditStatuses",
3688
- "summary": "Show SMS signature review status",
3689
- "description": "Lists the SMS signatures the current user applied for and the SMS templates under each signature, with review status and the reason a review failed, newest signature first.\nReturns only the current user's own signatures, not those of other members of the account; with an account-level credential, returns the account owner's signatures. Any member can call it; admin is not required.",
5003
+ "createSmsTemplate": {
5004
+ "operationId": "createSmsTemplate",
5005
+ "summary": "Submit an SMS template for review",
5006
+ "description": "Submits an SMS template for review under a notification signature the account has applied for, even one still under review; follow the result with `GET /sms/templates`.",
5007
+ "tag": "Account",
5008
+ "method": "post",
5009
+ "path": "/sms/templates",
5010
+ "params": [],
5011
+ "paramNames": [],
5012
+ "body": {
5013
+ "contentType": "application/json",
5014
+ "required": true,
5015
+ "flatten": true
5016
+ },
5017
+ "successStatus": 201,
5018
+ "scopes": [
5019
+ "users"
5020
+ ],
5021
+ "responseSchema": "SMSTemplate"
5022
+ },
5023
+ "updateSmsTemplate": {
5024
+ "operationId": "updateSmsTemplate",
5025
+ "summary": "Update an SMS template",
5026
+ "description": "Changes a template the current user created and submits it for review again; an approved one cannot be sent until approved again.",
5027
+ "tag": "Account",
5028
+ "method": "patch",
5029
+ "path": "/sms/templates/{id}",
5030
+ "params": [
5031
+ {
5032
+ "name": "id",
5033
+ "in": "path",
5034
+ "required": true
5035
+ }
5036
+ ],
5037
+ "paramNames": [
5038
+ "id"
5039
+ ],
5040
+ "body": {
5041
+ "contentType": "application/json",
5042
+ "required": true,
5043
+ "flatten": true
5044
+ },
5045
+ "successStatus": 200,
5046
+ "scopes": [
5047
+ "users"
5048
+ ],
5049
+ "responseSchema": "SMSTemplate"
5050
+ },
5051
+ "deleteSmsTemplate": {
5052
+ "operationId": "deleteSmsTemplate",
5053
+ "summary": "Delete an SMS template",
5054
+ "description": "Deletes a template the current user created; notifications that send it stop sending SMS.",
5055
+ "tag": "Account",
5056
+ "method": "delete",
5057
+ "path": "/sms/templates/{id}",
5058
+ "params": [
5059
+ {
5060
+ "name": "id",
5061
+ "in": "path",
5062
+ "required": true
5063
+ }
5064
+ ],
5065
+ "paramNames": [
5066
+ "id"
5067
+ ],
5068
+ "successStatus": 200,
5069
+ "scopes": [
5070
+ "users"
5071
+ ]
5072
+ },
5073
+ "listSmtpAccounts": {
5074
+ "operationId": "listSmtpAccounts",
5075
+ "summary": "List SMTP accounts",
5076
+ "description": "Lists the verified custom SMTP accounts the account can send email through.\n- The list is empty when the plan does not include custom SMTP.\n- Every member gets the same list; admin is not required.",
3690
5077
  "tag": "Account",
3691
5078
  "method": "get",
3692
- "path": "/sms/audit_statuses",
5079
+ "path": "/smtp_accounts",
3693
5080
  "params": [],
3694
5081
  "paramNames": [],
3695
5082
  "successStatus": 200,
3696
5083
  "scopes": [
3697
5084
  "users"
3698
5085
  ],
3699
- "responseSchema": "SMSAuditStatusResponse"
5086
+ "responseSchema": "SMTPAccountListResponse"
3700
5087
  },
3701
5088
  "verifyRedirectParams": {
3702
5089
  "operationId": "verifyRedirectParams",
@@ -3787,7 +5174,8 @@
3787
5174
  {
3788
5175
  "name": "form_tokens",
3789
5176
  "in": "query",
3790
- "required": false
5177
+ "required": false,
5178
+ "array": true
3791
5179
  },
3792
5180
  {
3793
5181
  "name": "filters",
@@ -3822,7 +5210,8 @@
3822
5210
  {
3823
5211
  "name": "form_tokens",
3824
5212
  "in": "query",
3825
- "required": false
5213
+ "required": false,
5214
+ "array": true
3826
5215
  },
3827
5216
  {
3828
5217
  "name": "filters",
@@ -3927,7 +5316,7 @@
3927
5316
  "listMySubmittedEntries": {
3928
5317
  "operationId": "listMySubmittedEntries",
3929
5318
  "summary": "List my entries in a form",
3930
- "description": "Returns the entries the current user submitted to the given form, newest first. The form need not be created by or shared with the current user: only the user's own entries are returned,\nwith only the fields the form makes public to submitters (hidden fields are neither returned nor usable for filtering or searching). Returns an empty list when the user has not submitted.\n- `fields` keeps only the given public fields; unknown or non-public api_codes are skipped (no error).\n- `filters` works as in the entry list, but cannot filter by `creator_id` (results are already limited to the user's own) or on non-public fields; doing so returns 400.\n- `keyword` searches only the user's own entries and is not subject to the form's entry-count limit.\n- Pagination: `next` is a row offset; `skip` and `next` cannot be combined. 50 rows per page by default, adjustable with `limit` (at most 1000).\n- An account access token has no \"current user\"; calling with one returns 400. Returns 404 when the form does not exist.",
5319
+ "description": "Returns the entries the current user submitted to the given form, newest first. The form need not be created by or shared with the current user: only the user's own entries are returned,\nwith only the fields the form makes public to submitters (hidden fields are neither returned nor usable for filtering or searching).\n- `filters` works as in the entry list, but cannot use `creator_id`, since results are already the user's own.\n- `keyword` is not subject to the form's entry-count limit.\n- An account access token has no \"current user\"; calling with one returns 400.",
3931
5320
  "tag": "Entries",
3932
5321
  "method": "get",
3933
5322
  "path": "/my/forms/{form_token}/entries",
@@ -3940,7 +5329,8 @@
3940
5329
  {
3941
5330
  "name": "fields",
3942
5331
  "in": "query",
3943
- "required": false
5332
+ "required": false,
5333
+ "array": true
3944
5334
  },
3945
5335
  {
3946
5336
  "name": "filters",
@@ -3993,7 +5383,7 @@
3993
5383
  "searchMySubmittedEntries": {
3994
5384
  "operationId": "searchMySubmittedEntries",
3995
5385
  "summary": "Search my entries",
3996
- "description": "Searches the current user's own entries across forms with one keyword (only fields the form makes public to submitters are searched), and returns the match count and serial numbers for each matching form.\n- `keyword` is required; returns 400 when empty.\n- By default, searches every form the current user has submitted to (quick payment forms excluded); `form_tokens` limits it to some of them, and tokens not in the user's submission history are ignored.\n Returns 400 when more than 20 forms are in the search; narrow them with `form_tokens`.\n- Forms with no match are left out of the result; when a form cannot be searched (e.g. the keyword is too long), that form returns `unavailable` with an explanation and the other forms return as usual.\n- At most 50 serial numbers are returned per form; `matched` is the form's actual total match count.\n- An account access token has no \"current user\"; calling with one returns 400.",
5386
+ "description": "Searches the current user's own entries in every form they submitted to (quick payment forms excluded) with one keyword, over the fields each form makes public to submitters, and returns the match count and serial numbers for each matching form.\n- One call searches at most 20 forms; past that, name the ones to search in `form_tokens`.\n- An account access token has no \"current user\"; calling with one returns 400.",
3997
5387
  "tag": "Entries",
3998
5388
  "method": "get",
3999
5389
  "path": "/my/search",
@@ -4006,7 +5396,8 @@
4006
5396
  {
4007
5397
  "name": "form_tokens",
4008
5398
  "in": "query",
4009
- "required": false
5399
+ "required": false,
5400
+ "array": true
4010
5401
  }
4011
5402
  ],
4012
5403
  "paramNames": [
@@ -4022,7 +5413,7 @@
4022
5413
  "getFillFormPage": {
4023
5414
  "operationId": "getFillFormPage",
4024
5415
  "summary": "Show a form's fill page",
4025
- "description": "Reads the fill page behind a fill link (`/f/<token>`): its title, description, the fields to fill in, and whether the current user can submit right now.\nThe caller reads it as a submitter holding the link and needs no permission on the form or table; only what the fill page shows is returned.\n- `fields` lists the public fields in fill-page order, shaped like `fields` when reading a form; for a form view, column names, descriptions and required flags already follow the view's settings.\n- Submission is possible only when `status.key` is `open`; other values (such as `not_opened`, `closed`, `password_required`, `unauthorized`, `submission_prevented`,\n `fill_frequency_limited`: the current user has used up the submissions the fill page allows) are explained in `status.message`; when the page is not yet open or has closed on schedule, `status.open_info` gives the open period.\n- `blockers` lists requirements only the submitter can complete on the page (captcha, SMS verification, WeChat identity, payment, etc.); when it is not empty, submission is refused and the form must be filled in a browser.\n- `manageable` is returned only when the current user can already read the underlying form or table, and gives its token and name.\n- An account access token has no \"current user\"; calling with one returns 400.",
5416
+ "description": "Reads the fill page behind a fill link (`/f/<token>`): its title, description, the fields to fill in, and whether the current user can submit right now.\nThe caller reads it as a submitter holding the link and needs no permission on the form or table; only what the fill page shows is returned.\n- Submit through the API only when `status.key` is `open` and `blockers` is empty; otherwise `status.message` says why, and a non-empty `blockers` means the page must be filled in a browser.\n- An account access token has no \"current user\"; calling with one returns 400.",
4026
5417
  "tag": "Fill",
4027
5418
  "method": "get",
4028
5419
  "path": "/fill/f/{form_token}",
@@ -4073,7 +5464,7 @@
4073
5464
  "getFillViewPage": {
4074
5465
  "operationId": "getFillViewPage",
4075
5466
  "summary": "Show a form view's fill page",
4076
- "description": "Reads the fill page behind a fill link (`/v/<token>`): its title, description, the fields to fill in, and whether the current user can submit right now.\nThe caller reads it as a submitter holding the link and needs no permission on the form or table; only what the fill page shows is returned.\n- `fields` lists the public fields in fill-page order, shaped like `fields` when reading a form; for a form view, column names, descriptions and required flags already follow the view's settings.\n- Submission is possible only when `status.key` is `open`; other values (such as `not_opened`, `closed`, `password_required`, `unauthorized`, `submission_prevented`,\n `fill_frequency_limited`: the current user has used up the submissions the fill page allows) are explained in `status.message`; when the page is not yet open or has closed on schedule, `status.open_info` gives the open period.\n- `blockers` lists requirements only the submitter can complete on the page (captcha, SMS verification, WeChat identity, payment, etc.); when it is not empty, submission is refused and the form must be filled in a browser.\n- `manageable` is returned only when the current user can already read the underlying form or table, and gives its token and name.\n- An account access token has no \"current user\"; calling with one returns 400.",
5467
+ "description": "Reads the fill page behind a fill link (`/v/<token>`): its title, description, the fields to fill in, and whether the current user can submit right now.\nThe caller reads it as a submitter holding the link and needs no permission on the form or table; only what the fill page shows is returned.\n- Submit through the API only when `status.key` is `open` and `blockers` is empty; otherwise `status.message` says why, and a non-empty `blockers` means the page must be filled in a browser.\n- An account access token has no \"current user\"; calling with one returns 400.",
4077
5468
  "tag": "Fill",
4078
5469
  "method": "get",
4079
5470
  "path": "/fill/v/{view_token}",
@@ -4148,5 +5539,248 @@
4148
5539
  "forms"
4149
5540
  ],
4150
5541
  "responseSchema": "TableFolderMoveResult"
5542
+ },
5543
+ "listRoutines": {
5544
+ "operationId": "listRoutines",
5545
+ "summary": "List routines",
5546
+ "description": "The current user's own routines, newest first.\n- Paging: `next` is a row offset; pass it back unchanged for the next page. `skip` and `next` cannot be passed together. 50 per page by default, `limit` changes it (at most 1000).\n- Routines belong to a person: a credential with no current user (an account access token and the like) returns `403`, and so does an account that cannot use routines yet.",
5547
+ "tag": "Routines",
5548
+ "method": "get",
5549
+ "path": "/routines",
5550
+ "params": [
5551
+ {
5552
+ "name": "state",
5553
+ "in": "query",
5554
+ "required": false
5555
+ },
5556
+ {
5557
+ "name": "limit",
5558
+ "in": "query",
5559
+ "required": false
5560
+ },
5561
+ {
5562
+ "name": "next",
5563
+ "in": "query",
5564
+ "required": false
5565
+ },
5566
+ {
5567
+ "name": "skip",
5568
+ "in": "query",
5569
+ "required": false
5570
+ }
5571
+ ],
5572
+ "paramNames": [
5573
+ "state",
5574
+ "limit",
5575
+ "next",
5576
+ "skip"
5577
+ ],
5578
+ "successStatus": 200,
5579
+ "scopes": [
5580
+ "routines"
5581
+ ],
5582
+ "responseSchema": "RoutineListResponse"
5583
+ },
5584
+ "createRoutine": {
5585
+ "operationId": "createRoutine",
5586
+ "summary": "Create a routine",
5587
+ "description": "Creates a routine, enabled from the start. The rules and messages are those of the routines page: a name of at most 60 characters, an instruction of at most 2000, only forms, tables, views and query pages you can reach as objects, at most 20 enabled routines per person, and a one-off time later than now. A data trigger's source is added to the objects.\n- A routine against the rules returns `422`, and `errors` gives each reason with its `code` and the page's message.\n- Delete authorization (`delete_authorized`) cannot be set here: a body that names it, true or false, returns `422` `delete_authorization_page_only` and creates nothing.\n- Jiri running a routine cannot create routines (`403`).",
5588
+ "tag": "Routines",
5589
+ "method": "post",
5590
+ "path": "/routines",
5591
+ "params": [],
5592
+ "paramNames": [],
5593
+ "body": {
5594
+ "contentType": "application/json",
5595
+ "required": true,
5596
+ "flatten": true
5597
+ },
5598
+ "successStatus": 201,
5599
+ "scopes": [
5600
+ "routines"
5601
+ ],
5602
+ "responseSchema": "Routine"
5603
+ },
5604
+ "getRoutine": {
5605
+ "operationId": "getRoutine",
5606
+ "summary": "Get a routine",
5607
+ "description": "One of your own routines. Someone else's, or one that does not exist, returns `404`.",
5608
+ "tag": "Routines",
5609
+ "method": "get",
5610
+ "path": "/routines/{id}",
5611
+ "params": [
5612
+ {
5613
+ "name": "id",
5614
+ "in": "path",
5615
+ "required": true
5616
+ }
5617
+ ],
5618
+ "paramNames": [
5619
+ "id"
5620
+ ],
5621
+ "successStatus": 200,
5622
+ "scopes": [
5623
+ "routines"
5624
+ ],
5625
+ "responseSchema": "Routine"
5626
+ },
5627
+ "updateRoutine": {
5628
+ "operationId": "updateRoutine",
5629
+ "summary": "Update a routine",
5630
+ "description": "Changes the name, instruction, objects, trigger kind, schedule or data trigger. A key left out, or null, keeps what the routine has; `objects` given replaces them all.\n- The rules and messages are those of creating. A changed schedule cancels the runs queued for the old one; a changed instruction or objects stops a run in progress.\n- A body that names `delete_authorized`, true or false, returns `422` `delete_authorization_page_only` and changes nothing.\n- Jiri running a routine cannot update routines (`403`).",
5631
+ "tag": "Routines",
5632
+ "method": "patch",
5633
+ "path": "/routines/{id}",
5634
+ "params": [
5635
+ {
5636
+ "name": "id",
5637
+ "in": "path",
5638
+ "required": true
5639
+ }
5640
+ ],
5641
+ "paramNames": [
5642
+ "id"
5643
+ ],
5644
+ "body": {
5645
+ "contentType": "application/json",
5646
+ "required": true,
5647
+ "flatten": true
5648
+ },
5649
+ "successStatus": 200,
5650
+ "scopes": [
5651
+ "routines"
5652
+ ],
5653
+ "responseSchema": "Routine"
5654
+ },
5655
+ "deleteRoutine": {
5656
+ "operationId": "deleteRoutine",
5657
+ "summary": "Delete a routine",
5658
+ "description": "Deletes the routine for good: it never runs again, and its queued and running runs are cancelled. The conversations of its past runs stay.\n- Jiri running a routine cannot delete routines (`403`).",
5659
+ "tag": "Routines",
5660
+ "method": "delete",
5661
+ "path": "/routines/{id}",
5662
+ "params": [
5663
+ {
5664
+ "name": "id",
5665
+ "in": "path",
5666
+ "required": true
5667
+ }
5668
+ ],
5669
+ "paramNames": [
5670
+ "id"
5671
+ ],
5672
+ "successStatus": 200,
5673
+ "scopes": [
5674
+ "routines"
5675
+ ]
5676
+ },
5677
+ "pauseRoutine": {
5678
+ "operationId": "pauseRoutine",
5679
+ "summary": "Pause a routine",
5680
+ "description": "The routine stops running, and its queued and running runs are cancelled. Pausing a paused routine returns it as it is.\n- Jiri running a routine cannot pause routines (`403`).",
5681
+ "tag": "Routines",
5682
+ "method": "post",
5683
+ "path": "/routines/{routine_id}/pause",
5684
+ "params": [
5685
+ {
5686
+ "name": "routine_id",
5687
+ "in": "path",
5688
+ "required": true
5689
+ }
5690
+ ],
5691
+ "paramNames": [
5692
+ "routine_id"
5693
+ ],
5694
+ "successStatus": 200,
5695
+ "scopes": [
5696
+ "routines"
5697
+ ],
5698
+ "responseSchema": "Routine"
5699
+ },
5700
+ "resumeRoutine": {
5701
+ "operationId": "resumeRoutine",
5702
+ "summary": "Resume a routine",
5703
+ "description": "Enables a paused routine, which runs again from its next time after now; times missed while it was paused are not made up. Resuming an enabled routine returns it as it is.\n- As with creating, at most 20 routines can be enabled (`422` `active_limit`). A one-off whose time has passed returns `422` `once_passed`; a data trigger whose fields no longer work returns `422`.\n- Jiri running a routine cannot resume routines (`403`).",
5704
+ "tag": "Routines",
5705
+ "method": "post",
5706
+ "path": "/routines/{routine_id}/resume",
5707
+ "params": [
5708
+ {
5709
+ "name": "routine_id",
5710
+ "in": "path",
5711
+ "required": true
5712
+ }
5713
+ ],
5714
+ "paramNames": [
5715
+ "routine_id"
5716
+ ],
5717
+ "successStatus": 200,
5718
+ "scopes": [
5719
+ "routines"
5720
+ ],
5721
+ "responseSchema": "Routine"
5722
+ },
5723
+ "runRoutine": {
5724
+ "operationId": "runRoutine",
5725
+ "summary": "Run a routine now",
5726
+ "description": "Queues one run now, paused routines included, and returns it. When it starts it is checked as a scheduled run is: AI points, the daily run limit, and access to the objects. Its outcome shows in the routine's runs.\n- A run still in progress returns `422` `invalid`; so does a data trigger whose fields no longer work.\n- Jiri running a routine cannot run routines (`403`).",
5727
+ "tag": "Routines",
5728
+ "method": "post",
5729
+ "path": "/routines/{routine_id}/run",
5730
+ "params": [
5731
+ {
5732
+ "name": "routine_id",
5733
+ "in": "path",
5734
+ "required": true
5735
+ }
5736
+ ],
5737
+ "paramNames": [
5738
+ "routine_id"
5739
+ ],
5740
+ "successStatus": 202,
5741
+ "scopes": [
5742
+ "routines"
5743
+ ]
5744
+ },
5745
+ "listRoutineRuns": {
5746
+ "operationId": "listRoutineRuns",
5747
+ "summary": "List a routine's runs",
5748
+ "description": "The runs of one of your own routines, latest planned time first. Paged as `GET /routines` is.",
5749
+ "tag": "Routines",
5750
+ "method": "get",
5751
+ "path": "/routines/{routine_id}/runs",
5752
+ "params": [
5753
+ {
5754
+ "name": "routine_id",
5755
+ "in": "path",
5756
+ "required": true
5757
+ },
5758
+ {
5759
+ "name": "limit",
5760
+ "in": "query",
5761
+ "required": false
5762
+ },
5763
+ {
5764
+ "name": "next",
5765
+ "in": "query",
5766
+ "required": false
5767
+ },
5768
+ {
5769
+ "name": "skip",
5770
+ "in": "query",
5771
+ "required": false
5772
+ }
5773
+ ],
5774
+ "paramNames": [
5775
+ "routine_id",
5776
+ "limit",
5777
+ "next",
5778
+ "skip"
5779
+ ],
5780
+ "successStatus": 200,
5781
+ "scopes": [
5782
+ "routines"
5783
+ ],
5784
+ "responseSchema": "RoutineRunListResponse"
4151
5785
  }
4152
5786
  }