@jinshuju/mcp 0.4.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}",
@@ -215,7 +215,7 @@
215
215
  "checkFormFieldData": {
216
216
  "operationId": "checkFormFieldData",
217
217
  "summary": "Check whether fields already hold data",
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 (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.",
219
219
  "tag": "Forms",
220
220
  "method": "get",
221
221
  "path": "/forms/{form_token}/fields/check",
@@ -245,7 +245,7 @@
245
245
  "previewFormFieldTypeChange": {
246
246
  "operationId": "previewFormFieldTypeChange",
247
247
  "summary": "Preview a field type change",
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.\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.",
249
249
  "tag": "Forms",
250
250
  "method": "get",
251
251
  "path": "/forms/{form_token}/fields/preview_convert",
@@ -420,7 +420,7 @@
420
420
  "aggregateEntries": {
421
421
  "operationId": "aggregateEntries",
422
422
  "summary": "Aggregate entries",
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- `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.",
424
424
  "tag": "Entries",
425
425
  "method": "get",
426
426
  "path": "/forms/{form_token}/entries/aggregate",
@@ -1229,10 +1229,218 @@
1229
1229
  "form_setting"
1230
1230
  ]
1231
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
+ },
1232
1440
  "updateFormTheme": {
1233
1441
  "operationId": "updateFormTheme",
1234
1442
  "summary": "Update a form's theme",
1235
- "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.",
1236
1444
  "tag": "Form Settings",
1237
1445
  "method": "patch",
1238
1446
  "path": "/forms/{form_token}/theme",
@@ -1284,7 +1492,7 @@
1284
1492
  "updateExamSetting": {
1285
1493
  "operationId": "updateExamSetting",
1286
1494
  "summary": "Update a form's exam settings",
1287
- "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.",
1288
1496
  "tag": "Form Settings",
1289
1497
  "method": "patch",
1290
1498
  "path": "/forms/{form_token}/exam_setting",
@@ -1335,7 +1543,7 @@
1335
1543
  "updateEvaluationSetting": {
1336
1544
  "operationId": "updateEvaluationSetting",
1337
1545
  "summary": "Update a form's evaluation settings",
1338
- "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`.",
1339
1547
  "tag": "Form Settings",
1340
1548
  "method": "patch",
1341
1549
  "path": "/forms/{form_token}/evaluation_setting",
@@ -1698,7 +1906,7 @@
1698
1906
  "uploadEntryAttachment": {
1699
1907
  "operationId": "uploadEntryAttachment",
1700
1908
  "summary": "Upload an entry attachment",
1701
- "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.",
1702
1910
  "tag": "Uploads",
1703
1911
  "method": "post",
1704
1912
  "path": "/forms/{form_token}/entry_attachments",
@@ -1723,10 +1931,38 @@
1723
1931
  ],
1724
1932
  "responseSchema": "UploadedEntryAttachment"
1725
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
+ },
1726
1962
  "uploadImportFile": {
1727
1963
  "operationId": "uploadImportFile",
1728
1964
  "summary": "Upload an import file",
1729
- "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.",
1730
1966
  "tag": "Uploads",
1731
1967
  "method": "post",
1732
1968
  "path": "/forms/{form_token}/import_files",
@@ -1751,10 +1987,33 @@
1751
1987
  ],
1752
1988
  "responseSchema": "UploadedImportFile"
1753
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
+ },
1754
2013
  "createEntryImport": {
1755
2014
  "operationId": "createEntryImport",
1756
2015
  "summary": "Import entries from an uploaded spreadsheet",
1757
- "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.",
1758
2017
  "tag": "Entries",
1759
2018
  "method": "post",
1760
2019
  "path": "/forms/{form_token}/entry_imports",
@@ -1808,39 +2067,23 @@
1808
2067
  ],
1809
2068
  "responseSchema": "EntryImportJob"
1810
2069
  },
1811
- "uploadFormImage": {
1812
- "operationId": "uploadFormImage",
1813
- "summary": "Upload a form image",
1814
- "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`.",
1815
- "tag": "Uploads",
1816
- "method": "post",
1817
- "path": "/form_image_attachments",
1818
- "params": [],
1819
- "paramNames": [],
1820
- "body": {
1821
- "contentType": "multipart/form-data",
1822
- "required": true,
1823
- "flatten": true
1824
- },
1825
- "successStatus": 201,
1826
- "scopes": [
1827
- "forms",
1828
- "form_setting"
1829
- ],
1830
- "responseSchema": "UploadedFormImage"
1831
- },
1832
- "listTables": {
1833
- "operationId": "listTables",
1834
- "summary": "List tables",
1835
- "description": "Returns the tables (`kind=table`) visible to the current caller. Pagination, `q`, `filters`, `sort`, `next`, `skip` and `limit` mean the same as in the form list; 50 per page by default, up to 1000.",
1836
- "tag": "Tables",
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",
1837
2075
  "method": "get",
1838
- "path": "/tables",
2076
+ "path": "/forms/{form_token}/sms_tasks/estimate",
1839
2077
  "params": [
1840
2078
  {
1841
- "name": "q",
2079
+ "name": "form_token",
2080
+ "in": "path",
2081
+ "required": true
2082
+ },
2083
+ {
2084
+ "name": "entry_field",
1842
2085
  "in": "query",
1843
- "required": false
2086
+ "required": true
1844
2087
  },
1845
2088
  {
1846
2089
  "name": "filters",
@@ -1849,23 +2092,844 @@
1849
2092
  "json": true
1850
2093
  },
1851
2094
  {
1852
- "name": "sort",
2095
+ "name": "keyword",
1853
2096
  "in": "query",
1854
- "required": false,
1855
- "json": true
2097
+ "required": false
1856
2098
  },
1857
2099
  {
1858
- "name": "next",
2100
+ "name": "view_token",
1859
2101
  "in": "query",
1860
2102
  "required": false
1861
2103
  },
1862
2104
  {
1863
- "name": "skip",
2105
+ "name": "select_all",
1864
2106
  "in": "query",
1865
2107
  "required": false
1866
2108
  },
1867
2109
  {
1868
- "name": "limit",
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,
2849
+ "scopes": [
2850
+ "forms",
2851
+ "form_setting"
2852
+ ],
2853
+ "responseSchema": "UploadedFormImage"
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
+ },
2896
+ "listTables": {
2897
+ "operationId": "listTables",
2898
+ "summary": "List tables",
2899
+ "description": "Returns the tables (`kind=table`) visible to the current caller. Pagination, `q`, `filters`, `sort`, `next`, `skip` and `limit` mean the same as in the form list; 50 per page by default, up to 1000.",
2900
+ "tag": "Tables",
2901
+ "method": "get",
2902
+ "path": "/tables",
2903
+ "params": [
2904
+ {
2905
+ "name": "q",
2906
+ "in": "query",
2907
+ "required": false
2908
+ },
2909
+ {
2910
+ "name": "filters",
2911
+ "in": "query",
2912
+ "required": false,
2913
+ "json": true
2914
+ },
2915
+ {
2916
+ "name": "sort",
2917
+ "in": "query",
2918
+ "required": false,
2919
+ "json": true
2920
+ },
2921
+ {
2922
+ "name": "next",
2923
+ "in": "query",
2924
+ "required": false
2925
+ },
2926
+ {
2927
+ "name": "skip",
2928
+ "in": "query",
2929
+ "required": false
2930
+ },
2931
+ {
2932
+ "name": "limit",
1869
2933
  "in": "query",
1870
2934
  "required": false
1871
2935
  }
@@ -1887,7 +2951,7 @@
1887
2951
  "createTable": {
1888
2952
  "operationId": "createTable",
1889
2953
  "summary": "Create a table",
1890
- "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.",
1891
2955
  "tag": "Tables",
1892
2956
  "method": "post",
1893
2957
  "path": "/tables",
@@ -2978,7 +4042,7 @@
2978
4042
  "createCheckinCampaign": {
2979
4043
  "operationId": "createCheckinCampaign",
2980
4044
  "summary": "Create a check-in campaign",
2981
- "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, already has a check-in campaign with a check-in code in effect, is bound to another plugin that takes over the after-submit redirect, 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.",
2982
4046
  "tag": "Plugins",
2983
4047
  "method": "post",
2984
4048
  "path": "/plugins/checkin/campaigns",
@@ -3227,6 +4291,29 @@
3227
4291
  ],
3228
4292
  "responseSchema": "Folder"
3229
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
+ },
3230
4317
  "listPredefinedChoicesSets": {
3231
4318
  "operationId": "listPredefinedChoicesSets",
3232
4319
  "summary": "List predefined choice sets",
@@ -3370,7 +4457,7 @@
3370
4457
  "getBillingAccount": {
3371
4458
  "operationId": "getBillingAccount",
3372
4459
  "summary": "Show the current account",
3373
- "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.",
3374
4461
  "tag": "Account",
3375
4462
  "method": "get",
3376
4463
  "path": "/billing_account",
@@ -3466,10 +4553,25 @@
3466
4553
  ],
3467
4554
  "responseSchema": "BillingAccountPaymentHistoriesResponse"
3468
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
+ },
3469
4571
  "getTradeMerchant": {
3470
4572
  "operationId": "getTradeMerchant",
3471
4573
  "summary": "Show the 小金商户",
3472
- "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.",
3473
4575
  "tag": "Trade",
3474
4576
  "method": "get",
3475
4577
  "path": "/trade/merchant",
@@ -3484,7 +4586,7 @@
3484
4586
  "listTradeTransactions": {
3485
4587
  "operationId": "listTradeTransactions",
3486
4588
  "summary": "List payment orders",
3487
- "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.",
3488
4590
  "tag": "Trade",
3489
4591
  "method": "get",
3490
4592
  "path": "/trade/transactions",
@@ -3604,7 +4706,7 @@
3604
4706
  "listTradeRefunds": {
3605
4707
  "operationId": "listTradeRefunds",
3606
4708
  "summary": "List refunds",
3607
- "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.",
3608
4710
  "tag": "Trade",
3609
4711
  "method": "get",
3610
4712
  "path": "/trade/refunds",
@@ -3657,34 +4759,213 @@
3657
4759
  "successStatus": 200,
3658
4760
  "scopes": [
3659
4761
  "users"
3660
- ],
3661
- "responseSchema": "TradeRefundListResponse"
4762
+ ],
4763
+ "responseSchema": "TradeRefundListResponse"
4764
+ },
4765
+ "listTradePayouts": {
4766
+ "operationId": "listTradePayouts",
4767
+ "summary": "List payouts",
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.",
4769
+ "tag": "Trade",
4770
+ "method": "get",
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
+ ]
3662
4943
  },
3663
- "listTradePayouts": {
3664
- "operationId": "listTradePayouts",
3665
- "summary": "List payouts",
3666
- "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 小金商户.",
3667
- "tag": "Trade",
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",
3668
4949
  "method": "get",
3669
- "path": "/trade/payouts",
4950
+ "path": "/sms/templates",
3670
4951
  "params": [
3671
4952
  {
3672
- "name": "start_date",
4953
+ "name": "q",
3673
4954
  "in": "query",
3674
4955
  "required": false
3675
4956
  },
3676
4957
  {
3677
- "name": "end_date",
4958
+ "name": "scope",
3678
4959
  "in": "query",
3679
4960
  "required": false
3680
4961
  },
3681
4962
  {
3682
- "name": "status",
4963
+ "name": "signature_id",
3683
4964
  "in": "query",
3684
4965
  "required": false
3685
4966
  },
3686
4967
  {
3687
- "name": "include",
4968
+ "name": "status",
3688
4969
  "in": "query",
3689
4970
  "required": false
3690
4971
  },
@@ -3697,36 +4978,112 @@
3697
4978
  "name": "next",
3698
4979
  "in": "query",
3699
4980
  "required": false
4981
+ },
4982
+ {
4983
+ "name": "skip",
4984
+ "in": "query",
4985
+ "required": false
3700
4986
  }
3701
4987
  ],
3702
4988
  "paramNames": [
3703
- "start_date",
3704
- "end_date",
4989
+ "q",
4990
+ "scope",
4991
+ "signature_id",
3705
4992
  "status",
3706
- "include",
3707
4993
  "limit",
3708
- "next"
4994
+ "next",
4995
+ "skip"
3709
4996
  ],
3710
4997
  "successStatus": 200,
3711
4998
  "scopes": [
3712
4999
  "users"
3713
5000
  ],
3714
- "responseSchema": "TradePayoutListResponse"
5001
+ "responseSchema": "SMSTemplateListResponse"
3715
5002
  },
3716
- "listSmsAuditStatuses": {
3717
- "operationId": "listSmsAuditStatuses",
3718
- "summary": "Show SMS signature review status",
3719
- "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.",
3720
5077
  "tag": "Account",
3721
5078
  "method": "get",
3722
- "path": "/sms/audit_statuses",
5079
+ "path": "/smtp_accounts",
3723
5080
  "params": [],
3724
5081
  "paramNames": [],
3725
5082
  "successStatus": 200,
3726
5083
  "scopes": [
3727
5084
  "users"
3728
5085
  ],
3729
- "responseSchema": "SMSAuditStatusResponse"
5086
+ "responseSchema": "SMTPAccountListResponse"
3730
5087
  },
3731
5088
  "verifyRedirectParams": {
3732
5089
  "operationId": "verifyRedirectParams",
@@ -3959,7 +5316,7 @@
3959
5316
  "listMySubmittedEntries": {
3960
5317
  "operationId": "listMySubmittedEntries",
3961
5318
  "summary": "List my entries in a form",
3962
- "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.",
3963
5320
  "tag": "Entries",
3964
5321
  "method": "get",
3965
5322
  "path": "/my/forms/{form_token}/entries",
@@ -4026,7 +5383,7 @@
4026
5383
  "searchMySubmittedEntries": {
4027
5384
  "operationId": "searchMySubmittedEntries",
4028
5385
  "summary": "Search my entries",
4029
- "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.",
4030
5387
  "tag": "Entries",
4031
5388
  "method": "get",
4032
5389
  "path": "/my/search",
@@ -4056,7 +5413,7 @@
4056
5413
  "getFillFormPage": {
4057
5414
  "operationId": "getFillFormPage",
4058
5415
  "summary": "Show a form's fill page",
4059
- "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.",
4060
5417
  "tag": "Fill",
4061
5418
  "method": "get",
4062
5419
  "path": "/fill/f/{form_token}",
@@ -4107,7 +5464,7 @@
4107
5464
  "getFillViewPage": {
4108
5465
  "operationId": "getFillViewPage",
4109
5466
  "summary": "Show a form view's fill page",
4110
- "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.",
4111
5468
  "tag": "Fill",
4112
5469
  "method": "get",
4113
5470
  "path": "/fill/v/{view_token}",
@@ -4182,5 +5539,248 @@
4182
5539
  "forms"
4183
5540
  ],
4184
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"
4185
5785
  }
4186
5786
  }