From 8759f1877c841c0334178e9c7596c6df2de95cc3 Mon Sep 17 00:00:00 2001 From: chrisdengso <8185683+chrisdengso@users.noreply.github.com> Date: Thu, 29 Oct 2026 12:13:32 +0000 Subject: [PATCH] feat: v0.7.0, API changes --- .speakeasy/gen.lock | 56 +++-- .speakeasy/gen.yaml | 2 +- .speakeasy/openapi.json | 138 ++++++++++- CHANGELOG.md | 10 + README.md | 1 + internal/cli/queue/items/list.go | 2 +- internal/cli/queue/items/resolve.go | 4 +- internal/cli/queue/items/resolvemany.go | 4 +- internal/cli/queue/items/unresolve.go | 4 +- internal/cli/version.go | 2 +- internal/cli/wordlist/delete.go | 94 +++++++ internal/cli/wordlist/root.go | 4 + internal/sdk/items.go | 8 +- internal/sdk/models/components/queueitem.go | 4 +- .../sdk/models/operations/deletewordlist.go | 91 +++++++ .../sdk/models/operations/resolvequeueitem.go | 2 +- .../models/operations/resolvequeueitems.go | 4 +- .../sdk/models/operations/submitcontent.go | 2 +- .../models/operations/unresolvequeueitem.go | 2 +- internal/sdk/moderationapi.go | 6 +- internal/sdk/wordlist.go | 232 ++++++++++++++++++ internal/usage/bodyschemas.go | 4 +- internal/usage/schema.go | 11 +- 23 files changed, 625 insertions(+), 62 deletions(-) create mode 100644 internal/cli/wordlist/delete.go create mode 100644 internal/sdk/models/operations/deletewordlist.go diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index 2e3a3f2..98cd705 100644 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,12 +1,12 @@ lockVersion: 2.0.0 id: f6fec850-ef66-45dd-9a18-cec7cc0801ce management: - docChecksum: d6bce8a263f0ec11d54bdf026957ba2f + docChecksum: 29c7bb10b9655dcfe925eacf9d5f4758 docVersion: 1.1.0 speakeasyVersion: internal generationVersion: internal - releaseVersion: 0.6.0 - configChecksum: 4cc78732410808a71d9e1e939e1e4dc3 + releaseVersion: 0.7.0 + configChecksum: c6470f96a29a41e5ea56355f4ff08bc6 repoURL: https://github.com/moderation-api/cli installationURL: https://github.com/moderation-api/cli published: true @@ -145,15 +145,15 @@ trackedFiles: internal/cli/queue/getstats.go: last_write_checksum: sha1:dccfb6a53167375a75d7a41b243f93e3938fb475 internal/cli/queue/items/list.go: - last_write_checksum: sha1:9472e50b9528c24b556b64d4acd269dfcbffbde0 + last_write_checksum: sha1:09acae5af2de89fe61b89ef8658a5cd2685f1fa7 internal/cli/queue/items/resolve.go: - last_write_checksum: sha1:82554a72880ca640f629bd4e37593bd9e638b595 + last_write_checksum: sha1:a5ac2373401a91ce4acc8e12d6655a87525e68c7 internal/cli/queue/items/resolvemany.go: - last_write_checksum: sha1:6bafd91917a5fa29a12dfa448d2d8d7b72e66517 + last_write_checksum: sha1:952ff62c546fc4635719a61936c526fa8a96b94a internal/cli/queue/items/root.go: last_write_checksum: sha1:b90044575e57963575795c052a3e3b20b5bccf1f internal/cli/queue/items/unresolve.go: - last_write_checksum: sha1:2ca0793ed1596af844d545e617ae27f902f19186 + last_write_checksum: sha1:6b6f849526d53d2b59446f09126a5fe35a5f28aa internal/cli/queue/list.go: last_write_checksum: sha1:6c90a4b14db41fdc45b41c4b8a05adb9159e109a internal/cli/queue/retrieve.go: @@ -163,7 +163,7 @@ trackedFiles: internal/cli/root.go: last_write_checksum: sha1:e5d1b2211d9b4aaa93e95f248ce7db2af0a1844c internal/cli/version.go: - last_write_checksum: sha1:8ec1fd280177b7de452c747511fd0ad1c8619898 + last_write_checksum: sha1:058d6ec27532e6d65e428c9d9599289e72281336 internal/cli/webhooks/create.go: last_write_checksum: sha1:98c73266c8954efcf0da2493ae41909df45a38f0 internal/cli/webhooks/delete.go: @@ -184,6 +184,8 @@ trackedFiles: last_write_checksum: sha1:1cf0058687544cf792329d1944330e3a86b2bb7d internal/cli/wordlist/create.go: last_write_checksum: sha1:40c5b64716f2bcadf2d668ac1e0d63137780b288 + internal/cli/wordlist/delete.go: + last_write_checksum: sha1:102c9488b7a13d0159ea312fa47ab08e4e572931 internal/cli/wordlist/getembeddingstatus.go: last_write_checksum: sha1:127a47ac52060279377b439b9d53722ff2ce3836 internal/cli/wordlist/list.go: @@ -191,7 +193,7 @@ trackedFiles: internal/cli/wordlist/retrieve.go: last_write_checksum: sha1:12b90a87f77658b316d8f66a8f31159da76e909c internal/cli/wordlist/root.go: - last_write_checksum: sha1:ad86a0ad77b173379c1d373cf95d33996e2fe88c + last_write_checksum: sha1:b98758cc7f7ece90d255cff18f44c61f9c327650 internal/cli/wordlist/update.go: last_write_checksum: sha1:f59d133aa483d707135b86e4239a9d0d962e0170 internal/cli/wordlist/words/add.go: @@ -275,7 +277,7 @@ trackedFiles: internal/sdk/feedback.go: last_write_checksum: sha1:e851165bd315e8c590b4df1a5b6d40d4e2fa4a92 internal/sdk/items.go: - last_write_checksum: sha1:6da4b4e01374a5310ddd040e15cef9ec9c02d58f + last_write_checksum: sha1:30cd6aad0ca8f601038acf5c99cf03acc1570534 internal/sdk/models/components/authenticationerror.go: last_write_checksum: sha1:a3e65f0561efe3e332c01c0966f9b34f017e92eb internal/sdk/models/components/badrequesterror.go: @@ -307,7 +309,7 @@ trackedFiles: internal/sdk/models/components/queue.go: last_write_checksum: sha1:7fbe5aa0d9bf34eacb6eeb3dd1c3621d6c44b1d0 internal/sdk/models/components/queueitem.go: - last_write_checksum: sha1:2cd9946435953cc326610016dee84c1ac5e9ed66 + last_write_checksum: sha1:bbbbd2a941908c3dcaabde2c3a024d686aac2763 internal/sdk/models/components/queuewithcounts.go: last_write_checksum: sha1:7955e2097d8a045e8c91fc7e95a03ebfe32d6286 internal/sdk/models/components/ratelimiterror.go: @@ -346,6 +348,8 @@ trackedFiles: last_write_checksum: sha1:f0c5345c149027a90591b4dedf592d321bb00162 internal/sdk/models/operations/deletewebhook.go: last_write_checksum: sha1:ec51074ff96306e513685d48124863e5fe0d7f4d + internal/sdk/models/operations/deletewordlist.go: + last_write_checksum: sha1:2437abfd126b0f79971794cbd633ab75a460c473 internal/sdk/models/operations/duplicatechannel.go: last_write_checksum: sha1:80fb60a28f7301aae451e519ccdcafb734a92ade internal/sdk/models/operations/duplicateproject.go: @@ -409,17 +413,17 @@ trackedFiles: internal/sdk/models/operations/removewordlistwords.go: last_write_checksum: sha1:3f05a9e7df2be903164ba8bc07835473087b7f64 internal/sdk/models/operations/resolvequeueitem.go: - last_write_checksum: sha1:eb416c24aa5b611629925fd8caa77f4ce33bd475 + last_write_checksum: sha1:df2b43fe7abfc79d057a0b15481d6093311b83d7 internal/sdk/models/operations/resolvequeueitems.go: - last_write_checksum: sha1:f43daa692b1e8203ce8ebf2adf80b79e9f04b8ae + last_write_checksum: sha1:67cb09a546f1c617a9e239a64776d5d69244bfdd internal/sdk/models/operations/submitcontent.go: - last_write_checksum: sha1:cb670d0bf2fdcd9c7ac68031c7512e2c7d0604bf + last_write_checksum: sha1:9cce6e5ba0a74616d1be4427478571e979ff7a16 internal/sdk/models/operations/suspendauthor.go: last_write_checksum: sha1:af885ce2174abc6695e4dbaddd7b468d50d47bdd internal/sdk/models/operations/unblockauthor.go: last_write_checksum: sha1:b3ad277887e69ba140a561cde376dc0d17716ed5 internal/sdk/models/operations/unresolvequeueitem.go: - last_write_checksum: sha1:093fc225a3915f232e5fc18512eb0df40cfc956d + last_write_checksum: sha1:da5eb701b541f7ef8005acf308623b4ff4e840a2 internal/sdk/models/operations/updateaction.go: last_write_checksum: sha1:bdd668d1dd1d7d164eac482e10050cd4b8a80bf9 internal/sdk/models/operations/updateauthor.go: @@ -451,7 +455,7 @@ trackedFiles: internal/sdk/models/sdkerrors/sdkdefaulterror.go: last_write_checksum: sha1:437b3a3eacb54d8648a68f80b3cd38fa5019f006 internal/sdk/moderationapi.go: - last_write_checksum: sha1:23253dcf72960a999e32bf25f7b5bb41c216d3e4 + last_write_checksum: sha1:957bdbbe55d80861e078308e112b82cd2bbad463 internal/sdk/optionalnullable/optionalnullable.go: last_write_checksum: sha1:b828c94c45116e632925027e4e2c211e971e04e2 internal/sdk/optionalnullable/optionalnullable_test.go: @@ -505,15 +509,15 @@ trackedFiles: internal/sdk/webhooksecret.go: last_write_checksum: sha1:dba657bf0d16c657a8dbca1ae9f620394a15b83c internal/sdk/wordlist.go: - last_write_checksum: sha1:3e9a9d7460d5fa4594d078c798d713e1fad2c9dc + last_write_checksum: sha1:8ece3f9b112924179c5a80cc5daed35b438e4631 internal/sdk/words.go: last_write_checksum: sha1:444eb505ec11550665c14531b19b6ef451b2f4cf internal/testclient/testclient.go: last_write_checksum: sha1:7107e59d336235c44af152c5a61ee3896a7841fa internal/usage/bodyschemas.go: - last_write_checksum: sha1:27959b7df8d97c2b4639655c81c330a43114d7d6 + last_write_checksum: sha1:b7a71350264254881d21f50b539474c00d1e50f2 internal/usage/schema.go: - last_write_checksum: sha1:3ca5ea9c7c1891b13d1e360a33e7075bd5db1f0b + last_write_checksum: sha1:391d00fc76c6b80eb8a33503f07d8889e17c5d9f scripts/install.ps1: last_write_checksum: sha1:bd3a52c28f647ed881c6e8d72abf2d84216976c9 scripts/install.sh: @@ -1567,4 +1571,18 @@ examples: application/json: {"type": "not_found_error", "code": "resource_not_found", "message": "Not found", "request_id": "h8fkz6kkzoa446ffve37atys", "doc_url": "https://confused-bell.org"} "429": application/json: {"type": "rate_limit_error", "code": "rate_limit_exceeded", "message": "Rate limit exceeded", "request_id": "h8fkz6kkzoa446ffve37atys", "doc_url": "https://nervous-partridge.org/"} + deleteWordlist: + speakeasy-default-delete-wordlist: + parameters: + path: + id: "" + responses: + "200": + application/json: {"id": "", "deleted": true, "detachedChannelIds": ["", "", ""]} + "401": + application/json: {"type": "authentication_error", "code": "unauthorized", "message": "Authorization not provided", "request_id": "h8fkz6kkzoa446ffve37atys", "doc_url": "https://rotating-exterior.biz/"} + "404": + application/json: {"type": "not_found_error", "code": "resource_not_found", "message": "Not found", "request_id": "h8fkz6kkzoa446ffve37atys", "doc_url": "https://flickering-airmail.org/"} + "429": + application/json: {"type": "rate_limit_error", "code": "rate_limit_exceeded", "message": "Rate limit exceeded", "request_id": "h8fkz6kkzoa446ffve37atys", "doc_url": "https://decent-millet.biz"} examplesVersion: 1.0.2 diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml index 14a289f..19b5772 100644 --- a/.speakeasy/gen.yaml +++ b/.speakeasy/gen.yaml @@ -32,7 +32,7 @@ generation: generateNewTests: true skipResponseBodyAssertions: false cli: - version: 0.6.0 + version: 0.7.0 additionalDependencies: {} agentEnvironmentDetection: true arrayFlagFormat: repeatable diff --git a/.speakeasy/openapi.json b/.speakeasy/openapi.json index 9a2cf7e..e6db07f 100644 --- a/.speakeasy/openapi.json +++ b/.speakeasy/openapi.json @@ -3033,7 +3033,7 @@ "get": { "operationId": "listQueueItems", "summary": "List queue items", - "description": "List the items of a review queue, with the filters and the date window of the queue applied. By default only pending items. The list is paginated. Use the item `id` to resolve or unresolve an item.", + "description": "List the items of a review queue, with the filters and the date window of the queue applied. By default only pending items. The list is paginated. Use the item `id` or `contentId` to resolve or unresolve an item.", "tags": [ "Review queues" ], @@ -3281,7 +3281,7 @@ "post": { "operationId": "resolveQueueItem", "summary": "Resolve a queue item", - "description": "Mark an item of a queue as resolved. It is the same as a check-off in the dashboard: the item leaves the pending list and a QUEUE_ITEM_COMPLETED webhook is sent. To also take a moderation action, use `POST /actions/execute`.", + "description": "Mark an item of a queue as resolved. It is the same as a check-off in the dashboard: the item leaves the pending list and a QUEUE_ITEM_COMPLETED webhook is sent. `itemId` is the item `id` or your `contentId`. To also take a moderation action, use `POST /actions/execute`.", "tags": [ "Review queues" ], @@ -3306,10 +3306,10 @@ "name": "itemId", "schema": { "type": "string", - "description": "The item ID to resolve." + "description": "The item to resolve: the item `id` from `GET /queue/{id}/items`, or the `contentId` you sent to `POST /moderate` (webhooks send it as `item.id`). For a content ID, the newest submission with that content ID is used, as the queue shows it." }, "required": true, - "description": "The item ID to resolve." + "description": "The item to resolve: the item `id` from `GET /queue/{id}/items`, or the `contentId` you sent to `POST /moderate` (webhooks send it as `item.id`). For a content ID, the newest submission with that content ID is used, as the queue shows it." }, { "in": "header", @@ -3414,7 +3414,7 @@ "post": { "operationId": "resolveQueueItems", "summary": "Resolve queue items", - "description": "Mark up to 100 items of a queue as resolved in one request. Each item is resolved the same as with `POST /queue/{id}/items/{itemId}/resolve`. IDs that do not exist in the project are skipped and returned in `notFoundIds`.", + "description": "Mark up to 100 items of a queue as resolved in one request. Each item is resolved the same as with `POST /queue/{id}/items/{itemId}/resolve`, and each ID is an item `id` or your `contentId`. IDs that match no item in the project are skipped and returned in `notFoundIds`.", "tags": [ "Review queues" ], @@ -3459,7 +3459,7 @@ "items": { "type": "string" }, - "description": "The item IDs to resolve. Up to 100." + "description": "The items to resolve, up to 100. Each is the item `id` from `GET /queue/{id}/items`, or the `contentId` you sent to `POST /moderate` (webhooks send it as `item.id`). For a content ID, the newest submission with that content ID is used, as the queue shows it." } }, "required": [ @@ -3492,7 +3492,7 @@ "items": { "type": "string" }, - "description": "Item IDs that do not exist in the project." + "description": "IDs from `itemIds` that match no item or content ID in the project." } }, "required": [ @@ -3571,7 +3571,7 @@ "post": { "operationId": "unresolveQueueItem", "summary": "Unresolve a queue item", - "description": "Mark a resolved item of a queue as pending again. It is the same as removing a check-off in the dashboard: the item comes back in the pending list.", + "description": "Mark a resolved item of a queue as pending again. It is the same as removing a check-off in the dashboard: the item comes back in the pending list. `itemId` is the item `id` or your `contentId`.", "tags": [ "Review queues" ], @@ -3596,10 +3596,10 @@ "name": "itemId", "schema": { "type": "string", - "description": "The item ID to unresolve." + "description": "The item to unresolve: the item `id` from `GET /queue/{id}/items`, or the `contentId` you sent to `POST /moderate` (webhooks send it as `item.id`). For a content ID, the newest submission with that content ID is used, as the queue shows it." }, "required": true, - "description": "The item ID to unresolve." + "description": "The item to unresolve: the item `id` from `GET /queue/{id}/items`, or the `contentId` you sent to `POST /moderate` (webhooks send it as `item.id`). For a content ID, the newest submission with that content ID is used, as the queue shows it." }, { "in": "header", @@ -8622,6 +8622,116 @@ "readOnlyHint": false, "destructiveHint": true } + }, + "delete": { + "operationId": "deleteWordlist", + "summary": "Delete wordlist", + "description": "Delete a wordlist and its words. Channels that use the wordlist do not block the delete: the wordlist is removed from their policies. `detachedChannelIds` lists those channels. This cannot be undone.", + "tags": [ + "Wordlists" + ], + "security": [ + { + "Authorization": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "string", + "description": "ID of the wordlist to delete" + }, + "required": true, + "description": "ID of the wordlist to delete" + }, + { + "in": "header", + "name": "Project-Id", + "required": false, + "schema": { + "type": "string" + }, + "description": "The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.", + "x-speakeasy-name-override": "projectId" + } + ], + "responses": { + "200": { + "description": "Wordlist deleted successfully", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "ID of the deleted wordlist" + }, + "deleted": { + "type": "boolean", + "description": "Whether the wordlist was deleted" + }, + "detachedChannelIds": { + "type": "array", + "items": { + "type": "string" + }, + "description": "IDs of the channels that used the wordlist. It was removed from them." + } + }, + "required": [ + "id", + "deleted", + "detachedChannelIds" + ], + "additionalProperties": false + } + } + } + }, + "401": { + "description": "Authorization not provided", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationError" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundError" + } + } + } + }, + "429": { + "description": "Too many requests. Retry after the number of seconds in the `Retry-After` header.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RateLimitError" + } + } + } + } + }, + "x-speakeasy-group": "wordlist", + "x-speakeasy-name-override": "delete", + "x-speakeasy-mcp": { + "title": "Delete wordlist", + "scopes": [ + "destructive" + ], + "readOnlyHint": false, + "destructiveHint": true + } } }, "/wordlist/{id}/embedding-status": { @@ -10174,13 +10284,15 @@ "const": "unicode_spoofing" }, "signals": { - "description": "Per-signal flag toggles. Omitted spoofing signals are enabled; encoding_damage defaults to off because decode damage (U+FFFD) marks a broken pipeline, not an attack. A disabled signal is still detected and reported as a label, but does not by itself flag the policy.", + "description": "Per-signal flag toggles. Omitted spoofing signals are enabled; encoding_damage defaults to off because decode damage (U+FFFD) marks a broken pipeline, not an attack, and hidden_lookalike and leetspeak default to off because they are opt-in. A disabled signal is still detected and reported as a label, but does not by itself flag the policy. Lookalike words are only spelled out in the cleaned text when their signal is enabled.", "type": "object", "propertyNames": { "type": "string", "enum": [ "mixed_script", "confusable_word", + "hidden_lookalike", + "leetspeak", "invisible", "zalgo", "illegal", @@ -13528,11 +13640,11 @@ "properties": { "id": { "type": "string", - "description": "The item ID. Use it to resolve or unresolve the item in this queue." + "description": "The item ID: the ID of this submission. Use it, or `contentId`, to resolve or unresolve the item in this queue." }, "contentId": { "type": "string", - "description": "Your content ID, as you sent it to `POST /moderate`." + "description": "Your content ID, as you sent it to `POST /moderate`. Webhooks send it as `item.id`. The resolve endpoints accept it in place of `id`." }, "content": { "anyOf": [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 5632536..fb89cca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.7.0 (2026-10-29) + +Full Changelog: [v0.6.0...v0.7.0](https://github.com/moderation-api/cli/compare/v0.6.0...v0.7.0) + +### API changes + +- `POST /moderate`: added the new `hidden_lookalike` enum value to the request property `policies/items/oneOf[subschema #36: Unicode Spoofing]/signals/propertyNames/` +- `POST /moderate`: added the new `leetspeak` enum value to the request property `policies/items/oneOf[subschema #36: Unicode Spoofing]/signals/propertyNames/` +- `DELETE /wordlist/{id}`: endpoint added + ## 0.6.0 (2026-10-28) Full Changelog: [v0.5.0...v0.6.0](https://github.com/moderation-api/cli/compare/v0.5.0...v0.6.0) diff --git a/README.md b/README.md index 81a7f11..b5d7dbf 100644 --- a/README.md +++ b/README.md @@ -417,6 +417,7 @@ modapi projects list --secret-key "$MODAPI_ORGANIZATION_KEY" * [`create`](docs/modapi_wordlist_create.md) - Create wordlist * [`retrieve`](docs/modapi_wordlist_retrieve.md) - Get wordlist * [`update`](docs/modapi_wordlist_update.md) - Update wordlist + * [`delete`](docs/modapi_wordlist_delete.md) - Delete wordlist * [`get-embedding-status`](docs/modapi_wordlist_get-embedding-status.md) - Get embedding status * [`words`](docs/modapi_wordlist_words.md) - Operations for words * [`add`](docs/modapi_wordlist_words_add.md) - Add words to wordlist diff --git a/internal/cli/queue/items/list.go b/internal/cli/queue/items/list.go index 5890aec..7a2e85b 100644 --- a/internal/cli/queue/items/list.go +++ b/internal/cli/queue/items/list.go @@ -34,7 +34,7 @@ func initListCmd(parent *cobra.Command) error { var cmd = &cobra.Command{ Use: "list [id]", Short: "List queue items", - Long: "List the items of a review queue, with the filters and the date window of the queue applied. By default only pending items. The list is paginated. Use the item `id` to resolve or unresolve an item.", + Long: "List the items of a review queue, with the filters and the date window of the queue applied. By default only pending items. The list is paginated. Use the item `id` or `contentId` to resolve or unresolve an item.", Example: " modapi queue items list --id ", Args: flagutil.PositionalFlagArgs, RunE: runListCmd, diff --git a/internal/cli/queue/items/resolve.go b/internal/cli/queue/items/resolve.go index 32ada46..c204e5b 100644 --- a/internal/cli/queue/items/resolve.go +++ b/internal/cli/queue/items/resolve.go @@ -15,7 +15,7 @@ import ( var resolveCmdMeta = []flagutil.FlagMeta{ {FlagName: "id", FieldPath: "ID", Kind: flagutil.FlagKindString, Required: true, Description: "The queue ID. [required]"}, - {FlagName: "item-id", FieldPath: "ItemID", Kind: flagutil.FlagKindString, Required: true, Description: "The item ID to resolve. [required]"}, + {FlagName: "item-id", FieldPath: "ItemID", Kind: flagutil.FlagKindString, Required: true, Description: "The item to resolve: the item 'id' from 'GET /queue/{id}/items', or the 'contentId' you sent to 'POST /moderate' (webhooks send it as 'item.id'). For a content ID, the newest submission with that content ID is used, as the queue shows it. [required]"}, {FlagName: "project-id", Shorthand: "p", FieldPath: "ProjectID", Kind: flagutil.FlagKindString, Optional: true, Description: "The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error."}, } @@ -24,7 +24,7 @@ func initResolveCmd(parent *cobra.Command) error { var cmd = &cobra.Command{ Use: "resolve", Short: "Resolve a queue item", - Long: "Mark an item of a queue as resolved. It is the same as a check-off in the dashboard: the item leaves the pending list and a QUEUE_ITEM_COMPLETED webhook is sent. To also take a moderation action, use `POST /actions/execute`.", + Long: "Mark an item of a queue as resolved. It is the same as a check-off in the dashboard: the item leaves the pending list and a QUEUE_ITEM_COMPLETED webhook is sent. `itemId` is the item `id` or your `contentId`. To also take a moderation action, use `POST /actions/execute`.", Example: " modapi queue items resolve --id --item-id ", Args: cobra.NoArgs, RunE: runResolveCmd, diff --git a/internal/cli/queue/items/resolvemany.go b/internal/cli/queue/items/resolvemany.go index ec1a83b..443e91e 100644 --- a/internal/cli/queue/items/resolvemany.go +++ b/internal/cli/queue/items/resolvemany.go @@ -17,7 +17,7 @@ import ( var resolveManyCmdMeta = []flagutil.FlagMeta{ {FlagName: "id", Shorthand: "i", FieldPath: "ID", Kind: flagutil.FlagKindString, Required: true, Description: "The queue ID. [required]"}, {FlagName: "project-id", Shorthand: "p", FieldPath: "ProjectID", Kind: flagutil.FlagKindString, Optional: true, Description: "The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error."}, - {FlagName: "item-ids", FieldPath: "Body.ItemIds", Kind: flagutil.FlagKindStringArray, Required: true, Description: "The item IDs to resolve. Up to 100. [required]"}, + {FlagName: "item-ids", FieldPath: "Body.ItemIds", Kind: flagutil.FlagKindStringArray, Required: true, Description: "The items to resolve, up to 100. Each is the item 'id' from 'GET /queue/{id}/items', or the 'contentId' you sent to 'POST /moderate' (webhooks send it as 'item.id'). For a content ID, the newest submission with that content ID is used, as the queue shows it. [required]"}, } // initResolveManyCmd initializes the resolve-many command. @@ -25,7 +25,7 @@ func initResolveManyCmd(parent *cobra.Command) error { var cmd = &cobra.Command{ Use: "resolve-many [id]", Short: "Resolve queue items", - Long: "Mark up to 100 items of a queue as resolved in one request. Each item is resolved the same as with `POST /queue/{id}/items/{itemId}/resolve`. IDs that do not exist in the project are skipped and returned in `notFoundIds`.", + Long: "Mark up to 100 items of a queue as resolved in one request. Each item is resolved the same as with `POST /queue/{id}/items/{itemId}/resolve`, and each ID is an item `id` or your `contentId`. IDs that match no item in the project are skipped and returned in `notFoundIds`.", Example: " modapi queue items resolve-many --id --item-ids clx8k2v0e000108l4a9bz3f7q --item-ids clx8k2v0e000208l4h2mc6d1r", Args: flagutil.PositionalFlagArgs, RunE: runResolveManyCmd, diff --git a/internal/cli/queue/items/unresolve.go b/internal/cli/queue/items/unresolve.go index 77f64f6..f459117 100644 --- a/internal/cli/queue/items/unresolve.go +++ b/internal/cli/queue/items/unresolve.go @@ -15,7 +15,7 @@ import ( var unresolveCmdMeta = []flagutil.FlagMeta{ {FlagName: "id", FieldPath: "ID", Kind: flagutil.FlagKindString, Required: true, Description: "The queue ID. [required]"}, - {FlagName: "item-id", FieldPath: "ItemID", Kind: flagutil.FlagKindString, Required: true, Description: "The item ID to unresolve. [required]"}, + {FlagName: "item-id", FieldPath: "ItemID", Kind: flagutil.FlagKindString, Required: true, Description: "The item to unresolve: the item 'id' from 'GET /queue/{id}/items', or the 'contentId' you sent to 'POST /moderate' (webhooks send it as 'item.id'). For a content ID, the newest submission with that content ID is used, as the queue shows it. [required]"}, {FlagName: "project-id", Shorthand: "p", FieldPath: "ProjectID", Kind: flagutil.FlagKindString, Optional: true, Description: "The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error."}, } @@ -24,7 +24,7 @@ func initUnresolveCmd(parent *cobra.Command) error { var cmd = &cobra.Command{ Use: "unresolve", Short: "Unresolve a queue item", - Long: "Mark a resolved item of a queue as pending again. It is the same as removing a check-off in the dashboard: the item comes back in the pending list.", + Long: "Mark a resolved item of a queue as pending again. It is the same as removing a check-off in the dashboard: the item comes back in the pending list. `itemId` is the item `id` or your `contentId`.", Example: " modapi queue items unresolve --id --item-id ", Args: cobra.NoArgs, RunE: runUnresolveCmd, diff --git a/internal/cli/version.go b/internal/cli/version.go index c1573ad..edb4b92 100644 --- a/internal/cli/version.go +++ b/internal/cli/version.go @@ -14,7 +14,7 @@ import ( // which propagates the value here (see cmd/modapi/main.go): // // go build -ldflags "-X main.version=x.y.z" ./cmd/modapi -var Version = "0.6.0" +var Version = "0.7.0" // BuildTime is optionally set at build time via ldflags targeting the main package. var BuildTime string diff --git a/internal/cli/wordlist/delete.go b/internal/cli/wordlist/delete.go new file mode 100644 index 0000000..f4a6907 --- /dev/null +++ b/internal/cli/wordlist/delete.go @@ -0,0 +1,94 @@ +// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + +package wordlist + +import ( + "fmt" + "github.com/moderation-api/cli/internal/client" + "github.com/moderation-api/cli/internal/flagutil" + "github.com/moderation-api/cli/internal/interactive" + "github.com/moderation-api/cli/internal/output" + "github.com/moderation-api/cli/internal/sdk" + "github.com/moderation-api/cli/internal/sdk/models/operations" + "github.com/moderation-api/cli/internal/usage" + "github.com/spf13/cobra" +) + +var deleteCmdMeta = []flagutil.FlagMeta{ + {FlagName: "id", Shorthand: "i", FieldPath: "ID", Kind: flagutil.FlagKindString, Required: true, Description: "ID of the wordlist to delete [required]"}, + {FlagName: "project-id", Shorthand: "p", FieldPath: "ProjectID", Kind: flagutil.FlagKindString, Optional: true, Description: "The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error."}, +} + +// initDeleteCmd initializes the delete command. +func initDeleteCmd(parent *cobra.Command) error { + var cmd = &cobra.Command{ + Use: "delete [id]", + Short: "Delete wordlist", + Long: "Delete a wordlist and its words. Channels that use the wordlist do not block the delete: the wordlist is removed from their policies. `detachedChannelIds` lists those channels. This cannot be undone.", + Example: " modapi wordlist delete --id ", + Args: flagutil.PositionalFlagArgs, + RunE: runDeleteCmd, + Annotations: map[string]string{ + "speakeasy_operation": "deleteWordlist", + }, + } + flagutil.RegisterFlags(cmd, deleteCmdMeta) + if err := flagutil.ValidateMeta[operations.DeleteWordlistRequest](deleteCmdMeta); err != nil { + return fmt.Errorf("invalid metadata for delete: %w", err) + } + if err := flagutil.DeclarePositionalFlag(cmd, "id", "ID of the wordlist to delete (or pass it as the [id] argument)", true); err != nil { + return err + } + if err := interactive.Declare(cmd, interactive.CommandSpec{Args: []interactive.ArgSpec{ + {Name: "id", Summary: "ID of the wordlist to delete", Required: true, SatisfiedBy: []string{"id"}}, + }}); err != nil { + return fmt.Errorf("declare interactive arguments for delete: %w", err) + } + parent.AddCommand(cmd) + return nil +} + +// runDeleteCmd executes the delete command. +func runDeleteCmd(cmd *cobra.Command, args []string) error { + if usage.UsageRequested(cmd) { + return usage.EmitSchema(cmd, cmd.OutOrStdout()) + } + if err := flagutil.ResolvePositionalFlag(cmd, args); err != nil { + return err + } + req, err := flagutil.BuildRequest[operations.DeleteWordlistRequest](cmd, deleteCmdMeta, "", "") + if err != nil { + return flagutil.WithCLIValidation(err) + } + s, err := client.NewClient(cmd) + if err != nil { + return err + } + sdkOpts, err := output.PrepareCallOpts(cmd) + if err != nil { + return err + } + // Dry-run: force skip deserialization so the synthetic empty response + // does not cause parse failures in typed response handling. + if client.IsDryRun(cmd) { + sdkOpts = append(sdkOpts, operations.WithSkipDeserialization()) + } + if err := output.ValidateGlobalServerIndex(cmd, len(sdk.ServerList)); err != nil { + return err + } + if output.WantsRawJSON(cmd) { + sdkOpts = append(sdkOpts, operations.WithSkipDeserialization()) + } + stopProgress := output.StartRequestProgress(cmd) + defer stopProgress() + res, err := s.Wordlist.Delete(cmd.Context(), *req, sdkOpts...) + stopProgress() + if err != nil { + return output.Error(cmd, err) + } + + if err := output.Result(cmd, res); err != nil { + return err + } + return nil +} diff --git a/internal/cli/wordlist/root.go b/internal/cli/wordlist/root.go index 61a0cc3..0a569ba 100644 --- a/internal/cli/wordlist/root.go +++ b/internal/cli/wordlist/root.go @@ -43,6 +43,10 @@ func InitWordlistRoot(parent *cobra.Command) error { return err } + if err := initDeleteCmd(WordlistCmd); err != nil { + return err + } + if err := initGetEmbeddingStatusCmd(WordlistCmd); err != nil { return err } diff --git a/internal/sdk/items.go b/internal/sdk/items.go index 2f3d207..a8b5cf5 100644 --- a/internal/sdk/items.go +++ b/internal/sdk/items.go @@ -31,7 +31,7 @@ func newItems(rootSDK *ModerationAPI, sdkConfig config.SDKConfiguration, hooks * } // List queue items -// List the items of a review queue, with the filters and the date window of the queue applied. By default only pending items. The list is paginated. Use the item `id` to resolve or unresolve an item. +// List the items of a review queue, with the filters and the date window of the queue applied. By default only pending items. The list is paginated. Use the item `id` or `contentId` to resolve or unresolve an item. func (s *Items) List(ctx context.Context, request operations.ListQueueItemsRequest, opts ...operations.Option) (*operations.ListQueueItemsResponse, error) { o := operations.Options{} supportedOptions := []string{ @@ -404,7 +404,7 @@ func (s *Items) List(ctx context.Context, request operations.ListQueueItemsReque } // Resolve a queue item -// Mark an item of a queue as resolved. It is the same as a check-off in the dashboard: the item leaves the pending list and a QUEUE_ITEM_COMPLETED webhook is sent. To also take a moderation action, use `POST /actions/execute`. +// Mark an item of a queue as resolved. It is the same as a check-off in the dashboard: the item leaves the pending list and a QUEUE_ITEM_COMPLETED webhook is sent. `itemId` is the item `id` or your `contentId`. To also take a moderation action, use `POST /actions/execute`. func (s *Items) Resolve(ctx context.Context, request operations.ResolveQueueItemRequest, opts ...operations.Option) (*operations.ResolveQueueItemResponse, error) { o := operations.Options{} supportedOptions := []string{ @@ -686,7 +686,7 @@ func (s *Items) Resolve(ctx context.Context, request operations.ResolveQueueItem } // ResolveMany - Resolve queue items -// Mark up to 100 items of a queue as resolved in one request. Each item is resolved the same as with `POST /queue/{id}/items/{itemId}/resolve`. IDs that do not exist in the project are skipped and returned in `notFoundIds`. +// Mark up to 100 items of a queue as resolved in one request. Each item is resolved the same as with `POST /queue/{id}/items/{itemId}/resolve`, and each ID is an item `id` or your `contentId`. IDs that match no item in the project are skipped and returned in `notFoundIds`. func (s *Items) ResolveMany(ctx context.Context, request operations.ResolveQueueItemsRequest, opts ...operations.Option) (*operations.ResolveQueueItemsResponse, error) { o := operations.Options{} supportedOptions := []string{ @@ -975,7 +975,7 @@ func (s *Items) ResolveMany(ctx context.Context, request operations.ResolveQueue } // Unresolve a queue item -// Mark a resolved item of a queue as pending again. It is the same as removing a check-off in the dashboard: the item comes back in the pending list. +// Mark a resolved item of a queue as pending again. It is the same as removing a check-off in the dashboard: the item comes back in the pending list. `itemId` is the item `id` or your `contentId`. func (s *Items) Unresolve(ctx context.Context, request operations.UnresolveQueueItemRequest, opts ...operations.Option) (*operations.UnresolveQueueItemResponse, error) { o := operations.Options{} supportedOptions := []string{ diff --git a/internal/sdk/models/components/queueitem.go b/internal/sdk/models/components/queueitem.go index 0b5f5c3..b7ad61a 100644 --- a/internal/sdk/models/components/queueitem.go +++ b/internal/sdk/models/components/queueitem.go @@ -932,9 +932,9 @@ func (e *QueueItemStatus) IsExact() bool { } type QueueItem struct { - // The item ID. Use it to resolve or unresolve the item in this queue. + // The item ID: the ID of this submission. Use it, or `contentId`, to resolve or unresolve the item in this queue. ID string `json:"id"` - // Your content ID, as you sent it to `POST /moderate`. + // Your content ID, as you sent it to `POST /moderate`. Webhooks send it as `item.id`. The resolve endpoints accept it in place of `id`. ContentID string `json:"contentId"` // The submitted content. Content *QueueItemContent `json:"content"` diff --git a/internal/sdk/models/operations/deletewordlist.go b/internal/sdk/models/operations/deletewordlist.go new file mode 100644 index 0000000..e0e8ecb --- /dev/null +++ b/internal/sdk/models/operations/deletewordlist.go @@ -0,0 +1,91 @@ +// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + +package operations + +import ( + "github.com/moderation-api/cli/internal/sdk/models/components" + "github.com/moderation-api/cli/internal/sdk/sdkinternal/utils" +) + +type DeleteWordlistRequest struct { + // ID of the wordlist to delete + ID string `pathParam:"style=simple,explode=false,name=id"` + // The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error. + ProjectID *string `header:"style=simple,explode=false,name=Project-Id"` +} + +func (d *DeleteWordlistRequest) GetID() string { + if d == nil { + return "" + } + return d.ID +} + +func (d *DeleteWordlistRequest) GetProjectID() *string { + if d == nil { + return nil + } + return d.ProjectID +} + +// DeleteWordlistResponseBody - Wordlist deleted successfully +type DeleteWordlistResponseBody struct { + // ID of the deleted wordlist + ID string `json:"id"` + // Whether the wordlist was deleted + Deleted bool `json:"deleted"` + // IDs of the channels that used the wordlist. It was removed from them. + DetachedChannelIds []string `json:"detachedChannelIds"` +} + +func (d *DeleteWordlistResponseBody) GetID() string { + if d == nil { + return "" + } + return d.ID +} + +func (d *DeleteWordlistResponseBody) GetDeleted() bool { + if d == nil { + return false + } + return d.Deleted +} + +func (d *DeleteWordlistResponseBody) GetDetachedChannelIds() []string { + if d == nil { + return []string{} + } + return d.DetachedChannelIds +} + +type DeleteWordlistResponse struct { + HTTPMeta components.HTTPMetadata `json:"-"` + // Wordlist deleted successfully + Object *DeleteWordlistResponseBody +} + +func (d DeleteWordlistResponse) MarshalJSON() ([]byte, error) { + return utils.MarshalJSON(d, "", false) +} + +func (d *DeleteWordlistResponse) UnmarshalJSON(data []byte) error { + if err := utils.UnmarshalJSON(data, &d, "", false, nil); err != nil { + return err + } + return nil +} + +func (d *DeleteWordlistResponse) GetHTTPMeta() components.HTTPMetadata { + if d == nil { + return components.HTTPMetadata{} + } + return d.HTTPMeta +} + +func (d *DeleteWordlistResponse) GetObject() *DeleteWordlistResponseBody { + if d == nil { + return nil + } + return d.Object +} diff --git a/internal/sdk/models/operations/resolvequeueitem.go b/internal/sdk/models/operations/resolvequeueitem.go index a04ece6..5c91ca7 100644 --- a/internal/sdk/models/operations/resolvequeueitem.go +++ b/internal/sdk/models/operations/resolvequeueitem.go @@ -10,7 +10,7 @@ import ( type ResolveQueueItemRequest struct { // The queue ID. ID string `pathParam:"style=simple,explode=false,name=id"` - // The item ID to resolve. + // The item to resolve: the item `id` from `GET /queue/{id}/items`, or the `contentId` you sent to `POST /moderate` (webhooks send it as `item.id`). For a content ID, the newest submission with that content ID is used, as the queue shows it. ItemID string `pathParam:"style=simple,explode=false,name=itemId"` // The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error. ProjectID *string `header:"style=simple,explode=false,name=Project-Id"` diff --git a/internal/sdk/models/operations/resolvequeueitems.go b/internal/sdk/models/operations/resolvequeueitems.go index c785a9e..2770a47 100644 --- a/internal/sdk/models/operations/resolvequeueitems.go +++ b/internal/sdk/models/operations/resolvequeueitems.go @@ -8,7 +8,7 @@ import ( ) type ResolveQueueItemsRequestBody struct { - // The item IDs to resolve. Up to 100. + // The items to resolve, up to 100. Each is the item `id` from `GET /queue/{id}/items`, or the `contentId` you sent to `POST /moderate` (webhooks send it as `item.id`). For a content ID, the newest submission with that content ID is used, as the queue shows it. ItemIds []string `json:"itemIds"` } @@ -52,7 +52,7 @@ func (r *ResolveQueueItemsRequest) GetBody() ResolveQueueItemsRequestBody { type ResolveQueueItemsResponseBody struct { // Number of items resolved. Resolved int64 `json:"resolved"` - // Item IDs that do not exist in the project. + // IDs from `itemIds` that match no item or content ID in the project. NotFoundIds []string `json:"notFoundIds"` } diff --git a/internal/sdk/models/operations/submitcontent.go b/internal/sdk/models/operations/submitcontent.go index 7c5f540..de23620 100644 --- a/internal/sdk/models/operations/submitcontent.go +++ b/internal/sdk/models/operations/submitcontent.go @@ -1049,7 +1049,7 @@ type UnicodeSpoofing struct { Threshold *float64 `json:"threshold,omitzero"` //lint:ignore U1000 accessed via reflection for JSON marshaling id string `const:"unicode_spoofing" json:"id"` - // Per-signal flag toggles. Omitted spoofing signals are enabled; encoding_damage defaults to off because decode damage (U+FFFD) marks a broken pipeline, not an attack. A disabled signal is still detected and reported as a label, but does not by itself flag the policy. + // Per-signal flag toggles. Omitted spoofing signals are enabled; encoding_damage defaults to off because decode damage (U+FFFD) marks a broken pipeline, not an attack, and hidden_lookalike and leetspeak default to off because they are opt-in. A disabled signal is still detected and reported as a label, but does not by itself flag the policy. Lookalike words are only spelled out in the cleaned text when their signal is enabled. Signals map[string]SignalsRequest `json:"signals,omitzero"` } diff --git a/internal/sdk/models/operations/unresolvequeueitem.go b/internal/sdk/models/operations/unresolvequeueitem.go index 8d74756..94c666b 100644 --- a/internal/sdk/models/operations/unresolvequeueitem.go +++ b/internal/sdk/models/operations/unresolvequeueitem.go @@ -10,7 +10,7 @@ import ( type UnresolveQueueItemRequest struct { // The queue ID. ID string `pathParam:"style=simple,explode=false,name=id"` - // The item ID to unresolve. + // The item to unresolve: the item `id` from `GET /queue/{id}/items`, or the `contentId` you sent to `POST /moderate` (webhooks send it as `item.id`). For a content ID, the newest submission with that content ID is used, as the queue shows it. ItemID string `pathParam:"style=simple,explode=false,name=itemId"` // The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error. ProjectID *string `header:"style=simple,explode=false,name=Project-Id"` diff --git a/internal/sdk/moderationapi.go b/internal/sdk/moderationapi.go index 8afdde1..0c1bde3 100644 --- a/internal/sdk/moderationapi.go +++ b/internal/sdk/moderationapi.go @@ -139,10 +139,10 @@ func WithTimeout(timeout time.Duration) SDKOption { // New creates a new instance of the SDK with the provided options func New(opts ...SDKOption) *ModerationAPI { sdk := &ModerationAPI{ - SDKVersion: "0.6.0", + SDKVersion: "0.7.0", sdkConfiguration: config.SDKConfiguration{ - UserAgent: "speakeasy-sdk/go 0.6.0 internal 1.1.0 github.com/moderation-api/cli/internal/sdk", - SDKVersion: "0.6.0", + UserAgent: "speakeasy-sdk/go 0.7.0 internal 1.1.0 github.com/moderation-api/cli/internal/sdk", + SDKVersion: "0.7.0", GenVersion: "internal", OpenAPIDocVersion: "1.1.0", ServerList: ServerList, diff --git a/internal/sdk/wordlist.go b/internal/sdk/wordlist.go index 79e264e..7473a82 100644 --- a/internal/sdk/wordlist.go +++ b/internal/sdk/wordlist.go @@ -1220,6 +1220,238 @@ func (s *Wordlist) Update(ctx context.Context, request operations.UpdateWordlist } +// Delete wordlist +// Delete a wordlist and its words. Channels that use the wordlist do not block the delete: the wordlist is removed from their policies. `detachedChannelIds` lists those channels. This cannot be undone. +func (s *Wordlist) Delete(ctx context.Context, request operations.DeleteWordlistRequest, opts ...operations.Option) (*operations.DeleteWordlistResponse, error) { + o := operations.Options{} + supportedOptions := []string{ + operations.SupportedOptionTimeout, + operations.SupportedOptionSkipDeserialization, + } + + for _, opt := range opts { + if err := opt(&o, supportedOptions...); err != nil { + return nil, fmt.Errorf("error applying option: %w", err) + } + } + + var baseURL string + if o.ServerURL == nil { + baseURL = utils.ReplaceParameters(s.sdkConfiguration.GetServerDetails()) + } else { + baseURL = *o.ServerURL + } + opURL, err := utils.GenerateURL(ctx, baseURL, "/wordlist/{id}", request, nil) + if err != nil { + return nil, fmt.Errorf("error generating URL: %w", err) + } + + hookCtx := hooks.HookContext{ + SDK: s.rootSDK, + SDKConfiguration: s.sdkConfiguration, + BaseURL: baseURL, + Context: ctx, + OperationID: "deleteWordlist", + SecuritySource: s.sdkConfiguration.Security, + } + + timeout := o.Timeout + if timeout == nil { + timeout = s.sdkConfiguration.Timeout + } + + var streamCancel context.CancelFunc + + if timeout != nil { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, *timeout) + streamCancel = cancel + defer func() { + if streamCancel != nil { + streamCancel() + } + }() + } + + req, err := http.NewRequestWithContext(ctx, "DELETE", opURL, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + req.Header.Set("Accept", "application/json") + req.Header.Set("User-Agent", s.sdkConfiguration.UserAgent) + + utils.PopulateHeaders(ctx, req, request, nil) + + if err := utils.PopulateSecurity(ctx, req, s.sdkConfiguration.Security); err != nil { + return nil, err + } + + for k, v := range o.SetHeaders { + req.Header.Set(k, v) + } + + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + if err != nil { + return nil, err + } + + httpRes, err := s.sdkConfiguration.Client.Do(req) + if err != nil || httpRes == nil { + if err != nil { + err = fmt.Errorf("error sending request: %w", err) + } else { + err = fmt.Errorf("error sending request: no response") + } + + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + return nil, err + } else if utils.MatchStatusCodes([]string{"4XX", "5XX"}, httpRes.StatusCode) { + _httpRes, err := s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + if err != nil { + return nil, err + } else if _httpRes != nil { + httpRes = _httpRes + } + } else { + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + if err != nil { + return nil, err + } + } + + res := &operations.DeleteWordlistResponse{ + HTTPMeta: components.HTTPMetadata{ + Request: req, + Response: httpRes, + }, + } + + switch { + case httpRes.StatusCode == 200: + switch { + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): + if o.SkipDeserialization != nil && *o.SkipDeserialization { + httpRes.Body = utils.BodyWithCancel(httpRes.Body, streamCancel) + streamCancel = nil + } else { + rawBody, err := utils.ConsumeRawBody(httpRes) + if err != nil { + return nil, err + } + + var out operations.DeleteWordlistResponseBody + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { + return nil, err + } + + res.Object = &out + } + default: + rawBody, err := utils.ConsumeRawBody(httpRes) + if err != nil { + return nil, err + } + return nil, sdkerrors.NewSDKDefaultError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) + } + case httpRes.StatusCode == 401: + switch { + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): + rawBody, err := utils.ConsumeRawBody(httpRes) + if err != nil { + return nil, err + } + + var out sdkerrors.AuthenticationError + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { + return nil, sdkerrors.NewResponseValidationError("response did not match the declared error schema", httpRes.StatusCode, string(rawBody), httpRes, err) + } + + out.HTTPMeta = components.HTTPMetadata{ + Request: req, + Response: httpRes, + } + return nil, &out + default: + rawBody, err := utils.ConsumeRawBody(httpRes) + if err != nil { + return nil, err + } + return nil, sdkerrors.NewSDKDefaultError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) + } + case httpRes.StatusCode == 404: + switch { + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): + rawBody, err := utils.ConsumeRawBody(httpRes) + if err != nil { + return nil, err + } + + var out sdkerrors.NotFoundError + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { + return nil, sdkerrors.NewResponseValidationError("response did not match the declared error schema", httpRes.StatusCode, string(rawBody), httpRes, err) + } + + out.HTTPMeta = components.HTTPMetadata{ + Request: req, + Response: httpRes, + } + return nil, &out + default: + rawBody, err := utils.ConsumeRawBody(httpRes) + if err != nil { + return nil, err + } + return nil, sdkerrors.NewSDKDefaultError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) + } + case httpRes.StatusCode == 429: + switch { + case utils.MatchContentType(httpRes.Header.Get("Content-Type"), `application/json`): + rawBody, err := utils.ConsumeRawBody(httpRes) + if err != nil { + return nil, err + } + + var out sdkerrors.RateLimitError + if err := utils.UnmarshalJsonFromResponseBody(bytes.NewBuffer(rawBody), &out, ""); err != nil { + return nil, sdkerrors.NewResponseValidationError("response did not match the declared error schema", httpRes.StatusCode, string(rawBody), httpRes, err) + } + + out.HTTPMeta = components.HTTPMetadata{ + Request: req, + Response: httpRes, + } + return nil, &out + default: + rawBody, err := utils.ConsumeRawBody(httpRes) + if err != nil { + return nil, err + } + return nil, sdkerrors.NewSDKDefaultError(fmt.Sprintf("unknown content-type received: %s", httpRes.Header.Get("Content-Type")), httpRes.StatusCode, string(rawBody), httpRes) + } + case httpRes.StatusCode >= 400 && httpRes.StatusCode < 500: + rawBody, err := utils.ConsumeRawBody(httpRes) + if err != nil { + return nil, err + } + return nil, sdkerrors.NewSDKDefaultError("API error occurred", httpRes.StatusCode, string(rawBody), httpRes) + case httpRes.StatusCode >= 500 && httpRes.StatusCode < 600: + rawBody, err := utils.ConsumeRawBody(httpRes) + if err != nil { + return nil, err + } + return nil, sdkerrors.NewSDKDefaultError("API error occurred", httpRes.StatusCode, string(rawBody), httpRes) + default: + rawBody, err := utils.ConsumeRawBody(httpRes) + if err != nil { + return nil, err + } + return nil, sdkerrors.NewSDKDefaultError("unknown status code returned", httpRes.StatusCode, string(rawBody), httpRes) + } + + return res, nil + +} + // GetEmbeddingStatus - Get embedding status // Get the current embedding progress status for a wordlist func (s *Wordlist) GetEmbeddingStatus(ctx context.Context, request operations.GetWordlistEmbeddingStatusRequest, opts ...operations.Option) (*operations.GetWordlistEmbeddingStatusResponse, error) { diff --git a/internal/usage/bodyschemas.go b/internal/usage/bodyschemas.go index 34c7e07..f23de81 100644 --- a/internal/usage/bodyschemas.go +++ b/internal/usage/bodyschemas.go @@ -23,8 +23,8 @@ var bodySchemas = map[string]string{ "duplicateProject": "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"name\":{\"description\":\"Name of the copy. Defaults to the original name with \\\"(copy)\\\".\",\"maxLength\":75,\"minLength\":1,\"type\":\"string\"\x7d\x7d,\"type\":\"object\"}", "executeAction": "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"description\":\"Input parameters\",\"properties\":{\"actionKey\":{\"description\":\"ID or key of the action to execute\",\"type\":\"string\"},\"authorIds\":{\"description\":\"IDs of the authors to apply the action to. Provide this or contentIds.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"contentIds\":{\"description\":\"IDs of the content items to apply the action to. Provide this or authorIds.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"createCasebookCase\":{\"description\":\"Record an allow or reject decision as a casebook case. Defaults to true when `moderator` is set, else false. Without `moderator`, the case is recorded as System.\",\"type\":\"boolean\"},\"duration\":{\"description\":\"Optional duration in milliseconds for actions with timeouts\",\"minimum\":0,\"type\":\"number\"},\"moderator\":{\"description\":\"The user ID or email of the organization member who made the decision. The action is recorded as theirs.\",\"type\":\"string\"},\"queueId\":{\"description\":\"Optional queue ID if the action is queue-specific\",\"type\":\"string\"},\"value\":{\"description\":\"Optional value to provide with the action\",\"type\":\"string\"\x7d\x7d,\"required\":[\"actionKey\"],\"type\":\"object\"}", "rejectContent": "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"createCasebookCase\":{\"description\":\"Record an allow or reject decision as a casebook case. Defaults to true when `moderator` is set, else false. Without `moderator`, the case is recorded as System.\",\"type\":\"boolean\"},\"moderator\":{\"description\":\"The user ID or email of the organization member who made the decision. The action is recorded as theirs.\",\"type\":\"string\"},\"queueId\":{\"description\":\"The queue the content is reviewed in. Defaults to the project's default queue.\",\"type\":\"string\"},\"reason\":{\"description\":\"Why the action was taken. Saved with the action and sent in the webhook event.\",\"type\":\"string\"\x7d\x7d,\"type\":\"object\"}", - "resolveQueueItems": "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"itemIds\":{\"description\":\"The item IDs to resolve. Up to 100.\",\"items\":{\"type\":\"string\"},\"maxItems\":100,\"minItems\":1,\"type\":\"array\"\x7d\x7d,\"required\":[\"itemIds\"],\"type\":\"object\"}", - "submitContent": "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"authorId\":{\"description\":\"The author of the content.\",\"type\":\"string\"},\"channel\":{\"description\":\"Provide a channel ID or key. Will use the project's default channel if not provided.\",\"type\":\"string\"},\"clientAction\":{\"description\":\"A recommendation from your own client-side flagging (e.g. a banned-IP list or a third-party tool). Feeds the rules engine and can escalate or override the recommended action. Does not change whether our analysis flagged the content.\",\"properties\":{\"action\":{\"description\":\"Your recommendation for the content: allow, review, or reject.\",\"enum\":[\"review\",\"allow\",\"reject\"],\"type\":\"string\"},\"behavior\":{\"description\":\"How your recommendation combines with ours. Defaults to 'escalate', which only applies it when stricter than ours; 'override' replaces ours outright.\",\"enum\":[\"override\",\"escalate\"],\"type\":\"string\"},\"reason\":{\"description\":\"A human-readable explanation for your recommendation.\",\"maxLength\":500,\"type\":\"string\"},\"source\":{\"description\":\"Where your recommendation came from, e.g. \\\"banned-ip\\\".\",\"maxLength\":100,\"type\":\"string\"\x7d\x7d,\"required\":[\"action\"],\"type\":\"object\"},\"content\":{\"description\":\"The content sent for moderation\",\"oneOf\":[{\"description\":\"Text\",\"properties\":{\"text\":{\"description\":\"The content text\",\"maxLength\":100000,\"type\":\"string\"},\"type\":{\"const\":\"text\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"text\"],\"title\":\"Text\",\"type\":\"object\"},{\"description\":\"Image\",\"properties\":{\"data\":{\"description\":\"Base64-encoded image data. Either url or data must be provided. Note: base64 images are not stored and will not appear in the review queue.\",\"maxLength\":5500000,\"type\":\"string\"},\"type\":{\"const\":\"image\",\"type\":\"string\"},\"url\":{\"description\":\"A public URL of the image content. Either url or data must be provided.\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\"],\"title\":\"Image\",\"type\":\"object\"},{\"description\":\"Video\",\"properties\":{\"type\":{\"const\":\"video\",\"type\":\"string\"},\"url\":{\"description\":\"A public URL of the video content\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"url\"],\"title\":\"Video\",\"type\":\"object\"},{\"description\":\"Audio\",\"properties\":{\"type\":{\"const\":\"audio\",\"type\":\"string\"},\"url\":{\"description\":\"The URL of the audio content\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"url\"],\"title\":\"Audio\",\"type\":\"object\"},{\"description\":\"Object\",\"properties\":{\"data\":{\"additionalProperties\":{\"oneOf\":[{\"description\":\"Text\",\"properties\":{\"text\":{\"description\":\"The content text\",\"maxLength\":100000,\"type\":\"string\"},\"type\":{\"const\":\"text\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"text\"],\"title\":\"Text\",\"type\":\"object\"},{\"description\":\"Image\",\"properties\":{\"data\":{\"description\":\"Base64-encoded image data. Either url or data must be provided. Note: base64 images are not stored and will not appear in the review queue.\",\"maxLength\":5500000,\"type\":\"string\"},\"type\":{\"const\":\"image\",\"type\":\"string\"},\"url\":{\"description\":\"A public URL of the image content. Either url or data must be provided.\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\"],\"title\":\"Image\",\"type\":\"object\"},{\"description\":\"Video\",\"properties\":{\"type\":{\"const\":\"video\",\"type\":\"string\"},\"url\":{\"description\":\"A public URL of the video content\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"url\"],\"title\":\"Video\",\"type\":\"object\"},{\"description\":\"Audio\",\"properties\":{\"type\":{\"const\":\"audio\",\"type\":\"string\"},\"url\":{\"description\":\"The URL of the audio content\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"url\"],\"title\":\"Audio\",\"type\":\"object\"}],\"title\":\"Content leaf\",\"type\":\"object\"},\"description\":\"Values in the object. Can be mixed content types.\",\"propertyNames\":{\"description\":\"The key of the content node\",\"type\":\"string\"},\"type\":\"object\"},\"type\":{\"const\":\"object\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"data\"],\"title\":\"Object\",\"type\":\"object\"}],\"type\":\"object\"},\"contentId\":{\"description\":\"The unique ID of the content in your database.\",\"maxLength\":100,\"type\":\"string\"},\"conversationId\":{\"description\":\"For example the ID of a chat room or a post\",\"type\":\"string\"},\"doNotStore\":{\"description\":\"Process the content without saving it on our servers. The item is still created and appears in the review queue, analytics and author history with its moderation results, but its content is empty, so reviewers cannot see what was submitted.\",\"type\":\"boolean\"},\"metaType\":{\"description\":\"The meta type of content being moderated\",\"enum\":[\"profile\",\"message\",\"post\",\"comment\",\"event\",\"product\",\"review\",\"voice\",\"other\"],\"type\":\"string\"},\"metadata\":{\"additionalProperties\":{},\"description\":\"Any metadata you want to store with the content\",\"propertyNames\":{\"type\":\"string\"},\"type\":\"object\"},\"policies\":{\"description\":\"(Enterprise) override the channel policies for this moderation request only.\",\"items\":{\"oneOf\":[{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"toxicity\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Toxicity\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"personal_information\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Personal Information\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"hate\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Hate\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit_drugs\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit Drugs\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit_alcohol\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit Alcohol\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit_firearms\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit Firearms\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit_tobacco\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit Tobacco\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit_gambling\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit Gambling\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"cannabis\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Cannabis\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"adult\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Adult\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"crypto\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Crypto\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"sexual\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Sexual\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"sexual_minors\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Sexual Minors\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"profanity\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Profanity\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"violence\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Violence\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"self_harm\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Self Harm\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"spam\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Spam\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"low_quality\",\"type\":\"string\"},\"minWords\":{\"description\":\"Flag content with fewer than this many words as low-effort. Defaults to 3. Set to disable by omitting.\",\"minimum\":1,\"type\":\"integer\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Low-quality content\",\"type\":\"object\"},{\"properties\":{\"comparator\":{\"description\":\"Flag images that contain \\\"at least\\\" or \\\"fewer than\\\" the configured number of faces. Defaults to at_least.\",\"enum\":[\"at_least\",\"fewer_than\"],\"type\":\"string\"},\"count\":{\"description\":\"Number of faces the comparator applies to. Defaults to 1, so the default rule flags any image containing a face.\",\"minimum\":0,\"type\":\"integer\"},\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"face_detection\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Face detection\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"self_promotion\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Self Promotion\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"political\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Political\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"religion\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Religion\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"threat\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Threat\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"sexual_solicitation\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Sexual Solicitation\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"channel_circumvent\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Channel Circumvention\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illegal_trade\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illegal Trade\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"alcohol\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Alcohol\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"drugs\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Drugs\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"gambling\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Gambling\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"tobacco\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Tobacco\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"weapons\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Weapons\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"promotion\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Promotion\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"extremism\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Extremism\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"code_abuse\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Code Abuse\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"unicode_spoofing\",\"type\":\"string\"},\"signals\":{\"additionalProperties\":{\"properties\":{\"flag\":{\"type\":\"boolean\"\x7d\x7d,\"type\":\"object\"},\"description\":\"Per-signal flag toggles. Omitted spoofing signals are enabled; encoding_damage defaults to off because decode damage (U+FFFD) marks a broken pipeline, not an attack. A disabled signal is still detected and reported as a label, but does not by itself flag the policy.\",\"propertyNames\":{\"enum\":[\"mixed_script\",\"confusable_word\",\"invisible\",\"zalgo\",\"illegal\",\"encoding_damage\"],\"type\":\"string\"},\"type\":\"object\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Unicode Spoofing\",\"type\":\"object\"},{\"properties\":{\"entities\":{\"additionalProperties\":{\"additionalProperties\":{},\"properties\":{\"enable\":{\"type\":\"boolean\"},\"flag\":{\"type\":\"boolean\"},\"mask\":{\"type\":\"string\"},\"shouldMask\":{\"type\":\"boolean\"\x7d\x7d,\"type\":\"object\"},\"propertyNames\":{\"enum\":[\"email\",\"phone\",\"url\",\"address\",\"name\",\"username\",\"ip_address\",\"credit_card\",\"sensitive_other\"],\"type\":\"string\"},\"type\":\"object\"},\"id\":{\"const\":\"pii\",\"type\":\"string\"\x7d\x7d,\"required\":[\"id\",\"entities\"],\"title\":\"PII Masking\",\"type\":\"object\"},{\"properties\":{\"entities\":{\"additionalProperties\":{\"additionalProperties\":{},\"properties\":{\"enable\":{\"type\":\"boolean\"},\"flag\":{\"type\":\"boolean\"},\"mask\":{\"type\":\"string\"},\"shouldMask\":{\"type\":\"boolean\"\x7d\x7d,\"type\":\"object\"},\"propertyNames\":{\"enum\":[\"email\",\"phone\",\"url\",\"address\",\"name\",\"username\",\"ip_address\",\"credit_card\",\"sensitive_other\"],\"type\":\"string\"},\"type\":\"object\"},\"id\":{\"const\":\"url\",\"type\":\"string\"\x7d\x7d,\"required\":[\"id\",\"entities\"],\"title\":\"URL Masking\",\"type\":\"object\"},{\"properties\":{\"allowlistWordlistIds\":{\"description\":\"IDs of wordlists whose entries are treated as allowed URL domains. Matches short-circuit the risk model and are never flagged.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"blocklistWordlistIds\":{\"description\":\"IDs of wordlists whose entries are treated as blocked URL domains. Matches short-circuit the risk model and are always flagged. Blocklists take precedence over allowlists.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"flag\":{\"type\":\"boolean\"},\"flagLinkShorteners\":{\"description\":\"When true, any URL detected as a free link shortener (bit.ly, t.co, tinyurl, etc.) is always flagged regardless of risk score. Allowlist matches still win.\",\"type\":\"boolean\"},\"id\":{\"const\":\"url_risk\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"URL Risk\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"guidelineKey\":{\"type\":\"string\"},\"id\":{\"const\":\"guideline\",\"type\":\"string\"},\"instructions\":{\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\",\"instructions\",\"guidelineKey\"],\"title\":\"Guideline\",\"type\":\"object\"}],\"type\":\"object\"},\"type\":\"array\"},\"timestamp\":{\"description\":\"Unix timestamp (in milliseconds) of when the content was created. Use if content is not submitted in real-time.\",\"type\":\"number\"\x7d\x7d,\"required\":[\"content\"],\"type\":\"object\"}", + "resolveQueueItems": "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"itemIds\":{\"description\":\"The items to resolve, up to 100. Each is the item `id` from `GET /queue/{id}/items`, or the `contentId` you sent to `POST /moderate` (webhooks send it as `item.id`). For a content ID, the newest submission with that content ID is used, as the queue shows it.\",\"items\":{\"type\":\"string\"},\"maxItems\":100,\"minItems\":1,\"type\":\"array\"\x7d\x7d,\"required\":[\"itemIds\"],\"type\":\"object\"}", + "submitContent": "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"authorId\":{\"description\":\"The author of the content.\",\"type\":\"string\"},\"channel\":{\"description\":\"Provide a channel ID or key. Will use the project's default channel if not provided.\",\"type\":\"string\"},\"clientAction\":{\"description\":\"A recommendation from your own client-side flagging (e.g. a banned-IP list or a third-party tool). Feeds the rules engine and can escalate or override the recommended action. Does not change whether our analysis flagged the content.\",\"properties\":{\"action\":{\"description\":\"Your recommendation for the content: allow, review, or reject.\",\"enum\":[\"review\",\"allow\",\"reject\"],\"type\":\"string\"},\"behavior\":{\"description\":\"How your recommendation combines with ours. Defaults to 'escalate', which only applies it when stricter than ours; 'override' replaces ours outright.\",\"enum\":[\"override\",\"escalate\"],\"type\":\"string\"},\"reason\":{\"description\":\"A human-readable explanation for your recommendation.\",\"maxLength\":500,\"type\":\"string\"},\"source\":{\"description\":\"Where your recommendation came from, e.g. \\\"banned-ip\\\".\",\"maxLength\":100,\"type\":\"string\"\x7d\x7d,\"required\":[\"action\"],\"type\":\"object\"},\"content\":{\"description\":\"The content sent for moderation\",\"oneOf\":[{\"description\":\"Text\",\"properties\":{\"text\":{\"description\":\"The content text\",\"maxLength\":100000,\"type\":\"string\"},\"type\":{\"const\":\"text\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"text\"],\"title\":\"Text\",\"type\":\"object\"},{\"description\":\"Image\",\"properties\":{\"data\":{\"description\":\"Base64-encoded image data. Either url or data must be provided. Note: base64 images are not stored and will not appear in the review queue.\",\"maxLength\":5500000,\"type\":\"string\"},\"type\":{\"const\":\"image\",\"type\":\"string\"},\"url\":{\"description\":\"A public URL of the image content. Either url or data must be provided.\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\"],\"title\":\"Image\",\"type\":\"object\"},{\"description\":\"Video\",\"properties\":{\"type\":{\"const\":\"video\",\"type\":\"string\"},\"url\":{\"description\":\"A public URL of the video content\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"url\"],\"title\":\"Video\",\"type\":\"object\"},{\"description\":\"Audio\",\"properties\":{\"type\":{\"const\":\"audio\",\"type\":\"string\"},\"url\":{\"description\":\"The URL of the audio content\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"url\"],\"title\":\"Audio\",\"type\":\"object\"},{\"description\":\"Object\",\"properties\":{\"data\":{\"additionalProperties\":{\"oneOf\":[{\"description\":\"Text\",\"properties\":{\"text\":{\"description\":\"The content text\",\"maxLength\":100000,\"type\":\"string\"},\"type\":{\"const\":\"text\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"text\"],\"title\":\"Text\",\"type\":\"object\"},{\"description\":\"Image\",\"properties\":{\"data\":{\"description\":\"Base64-encoded image data. Either url or data must be provided. Note: base64 images are not stored and will not appear in the review queue.\",\"maxLength\":5500000,\"type\":\"string\"},\"type\":{\"const\":\"image\",\"type\":\"string\"},\"url\":{\"description\":\"A public URL of the image content. Either url or data must be provided.\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\"],\"title\":\"Image\",\"type\":\"object\"},{\"description\":\"Video\",\"properties\":{\"type\":{\"const\":\"video\",\"type\":\"string\"},\"url\":{\"description\":\"A public URL of the video content\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"url\"],\"title\":\"Video\",\"type\":\"object\"},{\"description\":\"Audio\",\"properties\":{\"type\":{\"const\":\"audio\",\"type\":\"string\"},\"url\":{\"description\":\"The URL of the audio content\",\"format\":\"uri\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"url\"],\"title\":\"Audio\",\"type\":\"object\"}],\"title\":\"Content leaf\",\"type\":\"object\"},\"description\":\"Values in the object. Can be mixed content types.\",\"propertyNames\":{\"description\":\"The key of the content node\",\"type\":\"string\"},\"type\":\"object\"},\"type\":{\"const\":\"object\",\"type\":\"string\"\x7d\x7d,\"required\":[\"type\",\"data\"],\"title\":\"Object\",\"type\":\"object\"}],\"type\":\"object\"},\"contentId\":{\"description\":\"The unique ID of the content in your database.\",\"maxLength\":100,\"type\":\"string\"},\"conversationId\":{\"description\":\"For example the ID of a chat room or a post\",\"type\":\"string\"},\"doNotStore\":{\"description\":\"Process the content without saving it on our servers. The item is still created and appears in the review queue, analytics and author history with its moderation results, but its content is empty, so reviewers cannot see what was submitted.\",\"type\":\"boolean\"},\"metaType\":{\"description\":\"The meta type of content being moderated\",\"enum\":[\"profile\",\"message\",\"post\",\"comment\",\"event\",\"product\",\"review\",\"voice\",\"other\"],\"type\":\"string\"},\"metadata\":{\"additionalProperties\":{},\"description\":\"Any metadata you want to store with the content\",\"propertyNames\":{\"type\":\"string\"},\"type\":\"object\"},\"policies\":{\"description\":\"(Enterprise) override the channel policies for this moderation request only.\",\"items\":{\"oneOf\":[{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"toxicity\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Toxicity\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"personal_information\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Personal Information\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"hate\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Hate\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit_drugs\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit Drugs\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit_alcohol\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit Alcohol\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit_firearms\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit Firearms\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit_tobacco\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit Tobacco\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illicit_gambling\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illicit Gambling\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"cannabis\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Cannabis\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"adult\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Adult\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"crypto\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Crypto\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"sexual\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Sexual\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"sexual_minors\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Sexual Minors\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"profanity\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Profanity\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"violence\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Violence\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"self_harm\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Self Harm\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"spam\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Spam\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"low_quality\",\"type\":\"string\"},\"minWords\":{\"description\":\"Flag content with fewer than this many words as low-effort. Defaults to 3. Set to disable by omitting.\",\"minimum\":1,\"type\":\"integer\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Low-quality content\",\"type\":\"object\"},{\"properties\":{\"comparator\":{\"description\":\"Flag images that contain \\\"at least\\\" or \\\"fewer than\\\" the configured number of faces. Defaults to at_least.\",\"enum\":[\"at_least\",\"fewer_than\"],\"type\":\"string\"},\"count\":{\"description\":\"Number of faces the comparator applies to. Defaults to 1, so the default rule flags any image containing a face.\",\"minimum\":0,\"type\":\"integer\"},\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"face_detection\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Face detection\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"self_promotion\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Self Promotion\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"political\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Political\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"religion\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Religion\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"threat\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Threat\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"sexual_solicitation\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Sexual Solicitation\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"channel_circumvent\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Channel Circumvention\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"illegal_trade\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Illegal Trade\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"alcohol\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Alcohol\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"drugs\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Drugs\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"gambling\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Gambling\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"tobacco\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Tobacco\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"weapons\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Weapons\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"promotion\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Promotion\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"extremism\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Extremism\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"code_abuse\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Code Abuse\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"id\":{\"const\":\"unicode_spoofing\",\"type\":\"string\"},\"signals\":{\"additionalProperties\":{\"properties\":{\"flag\":{\"type\":\"boolean\"\x7d\x7d,\"type\":\"object\"},\"description\":\"Per-signal flag toggles. Omitted spoofing signals are enabled; encoding_damage defaults to off because decode damage (U+FFFD) marks a broken pipeline, not an attack, and hidden_lookalike and leetspeak default to off because they are opt-in. A disabled signal is still detected and reported as a label, but does not by itself flag the policy. Lookalike words are only spelled out in the cleaned text when their signal is enabled.\",\"propertyNames\":{\"enum\":[\"mixed_script\",\"confusable_word\",\"hidden_lookalike\",\"leetspeak\",\"invisible\",\"zalgo\",\"illegal\",\"encoding_damage\"],\"type\":\"string\"},\"type\":\"object\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"Unicode Spoofing\",\"type\":\"object\"},{\"properties\":{\"entities\":{\"additionalProperties\":{\"additionalProperties\":{},\"properties\":{\"enable\":{\"type\":\"boolean\"},\"flag\":{\"type\":\"boolean\"},\"mask\":{\"type\":\"string\"},\"shouldMask\":{\"type\":\"boolean\"\x7d\x7d,\"type\":\"object\"},\"propertyNames\":{\"enum\":[\"email\",\"phone\",\"url\",\"address\",\"name\",\"username\",\"ip_address\",\"credit_card\",\"sensitive_other\"],\"type\":\"string\"},\"type\":\"object\"},\"id\":{\"const\":\"pii\",\"type\":\"string\"\x7d\x7d,\"required\":[\"id\",\"entities\"],\"title\":\"PII Masking\",\"type\":\"object\"},{\"properties\":{\"entities\":{\"additionalProperties\":{\"additionalProperties\":{},\"properties\":{\"enable\":{\"type\":\"boolean\"},\"flag\":{\"type\":\"boolean\"},\"mask\":{\"type\":\"string\"},\"shouldMask\":{\"type\":\"boolean\"\x7d\x7d,\"type\":\"object\"},\"propertyNames\":{\"enum\":[\"email\",\"phone\",\"url\",\"address\",\"name\",\"username\",\"ip_address\",\"credit_card\",\"sensitive_other\"],\"type\":\"string\"},\"type\":\"object\"},\"id\":{\"const\":\"url\",\"type\":\"string\"\x7d\x7d,\"required\":[\"id\",\"entities\"],\"title\":\"URL Masking\",\"type\":\"object\"},{\"properties\":{\"allowlistWordlistIds\":{\"description\":\"IDs of wordlists whose entries are treated as allowed URL domains. Matches short-circuit the risk model and are never flagged.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"blocklistWordlistIds\":{\"description\":\"IDs of wordlists whose entries are treated as blocked URL domains. Matches short-circuit the risk model and are always flagged. Blocklists take precedence over allowlists.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"flag\":{\"type\":\"boolean\"},\"flagLinkShorteners\":{\"description\":\"When true, any URL detected as a free link shortener (bit.ly, t.co, tinyurl, etc.) is always flagged regardless of risk score. Allowlist matches still win.\",\"type\":\"boolean\"},\"id\":{\"const\":\"url_risk\",\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\"],\"title\":\"URL Risk\",\"type\":\"object\"},{\"properties\":{\"flag\":{\"type\":\"boolean\"},\"guidelineKey\":{\"type\":\"string\"},\"id\":{\"const\":\"guideline\",\"type\":\"string\"},\"instructions\":{\"type\":\"string\"},\"threshold\":{\"maximum\":1,\"minimum\":0,\"type\":\"number\"\x7d\x7d,\"required\":[\"flag\",\"id\",\"instructions\",\"guidelineKey\"],\"title\":\"Guideline\",\"type\":\"object\"}],\"type\":\"object\"},\"type\":\"array\"},\"timestamp\":{\"description\":\"Unix timestamp (in milliseconds) of when the content was created. Use if content is not submitted in real-time.\",\"type\":\"number\"\x7d\x7d,\"required\":[\"content\"],\"type\":\"object\"}", "suspendAuthor": "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"duration\":{\"description\":\"How long the suspension lasts, in milliseconds. Defaults to 24 hours.\",\"minimum\":0,\"type\":\"number\"},\"reason\":{\"description\":\"Why the action was taken. Saved with the action and sent in the webhook event.\",\"type\":\"string\"\x7d\x7d,\"type\":\"object\"}", "unblockAuthor": "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"reason\":{\"description\":\"Why the action was taken. Saved with the action and sent in the webhook event.\",\"type\":\"string\"\x7d\x7d,\"type\":\"object\"}", "updateAction": "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"builtIn\":{\"default\":false,\"description\":\"Whether the action is a built-in action or a custom one.\",\"type\":[\"boolean\",\"null\"]},\"description\":{\"description\":\"The description of the action.\",\"type\":[\"string\",\"null\"]},\"filterInQueueIds\":{\"default\":[],\"description\":\"The IDs of the queues the action is available in.\",\"items\":{\"type\":\"string\"},\"type\":\"array\"},\"freeText\":{\"default\":false,\"description\":\"Whether the action allows any text to be entered as a value or if it must be one of the possible values.\",\"type\":\"boolean\"},\"key\":{\"description\":\"User defined key of the action.\",\"type\":[\"string\",\"null\"]},\"name\":{\"description\":\"The name of the action.\",\"type\":\"string\"},\"position\":{\"default\":\"ALL_QUEUES\",\"description\":\"Show the action in all queues, selected queues or no queues (to use via API only).\",\"enum\":[\"ALL_QUEUES\",\"SOME_QUEUES\",\"HIDDEN\"],\"type\":\"string\"},\"possibleValues\":{\"default\":[],\"description\":\"The possible values of the action. The user will be prompted to select one of these values when executing the action.\",\"items\":{\"properties\":{\"value\":{\"description\":\"The value of the action.\",\"type\":\"string\"\x7d\x7d,\"required\":[\"value\"],\"type\":\"object\"},\"type\":\"array\"},\"queueBehaviour\":{\"default\":\"NO_CHANGE\",\"description\":\"Whether the action resolves and removes the item, unresolves and re-add it to the queue, or does not change the resolve status.\",\"enum\":[\"REMOVE\",\"ADD\",\"NO_CHANGE\"],\"type\":\"string\"},\"type\":{\"anyOf\":[{\"enum\":[\"AUTHOR_BLOCK\",\"AUTHOR_BLOCK_TEMP\",\"AUTHOR_UNBLOCK\",\"AUTHOR_DELETE\",\"AUTHOR_REPORT\",\"AUTHOR_WARN\",\"AUTHOR_CUSTOM\",\"ITEM_REJECT\",\"ITEM_ALLOW\",\"ITEM_CUSTOM\"],\"type\":\"string\"},{\"type\":\"null\"}],\"description\":\"The type of the action.\"},\"valueRequired\":{\"default\":false,\"description\":\"Whether the action requires a value to be executed.\",\"type\":\"boolean\"\x7d\x7d,\"type\":\"object\"}", diff --git a/internal/usage/schema.go b/internal/usage/schema.go index f804666..723c716 100644 --- a/internal/usage/schema.go +++ b/internal/usage/schema.go @@ -28,7 +28,7 @@ type usageNode struct { } var usageNodes = map[string]usageNode{ - "": {head: "name \"modapi\"\nbin \"modapi\"\nabout \"Moderation API: Moderate text, images, video and audio in real time, manage authors, review queues and actions, and receive webhooks.\"\nversion \"0.6.0\"\nconfig {\n file \"~/.config/modapi/config.yaml\"\n}", flags: []usageItem{{key: "usage", text: "flag \"--usage\" help=\"Print the CLI Usage schema in KDL format\" global=#true"}, {key: "version", text: "flag \"-v --version\" help=\"Print the CLI version\""}, {key: "output-format", text: "flag \"-o --output-format \" help=\"Specify the output format. Options: pretty, json, yaml, table, toon.\" global=#true config=\"output_format\" default=\"pretty\""}, {key: "color", text: "flag \"--color \" help=\"Control colored output: auto (color when output is a TTY), always, or never. Respects NO_COLOR and FORCE_COLOR env vars.\" global=#true default=\"auto\""}, {key: "jq", text: "flag \"-q --jq \" help=\"Filter and transform output using a jq expression (e.g., '.name', '.items[] | .id')\" global=#true"}, {key: "raw-output", text: "flag \"--raw-output\" help=\"Write --jq string results as raw text instead of JSON strings (like jq -r); non-string results stay JSON\" global=#true default=#false"}, {key: "server-url", text: "flag \"--server-url \" help=\"Override the default server URL\" global=#true"}, {key: "server", text: "flag \"--server \" help=\"Select a server by index (for indexed servers) or name (for named servers)\" global=#true"}, {key: "header", text: "flag \"-H --header \" help=\"Set a custom HTTP request header (format: \\\"Key: Value\\\"). Can be specified multiple times.\" global=#true var=#true"}, {key: "include-headers", text: "flag \"--include-headers\" help=\"Include HTTP response headers in the output\" global=#true default=#false"}, {key: "timeout", text: "flag \"--timeout \" help=\"HTTP request timeout (e.g., 30s, 5m, 100ms)\" global=#true config=\"timeout\""}, {key: "interactive", text: "flag \"--interactive\" help=\"Prompt for missing inputs and open guided configure/auth forms (forms fall back to line prompts on stdin off-TTY)\" global=#true default=#true"}, {key: "no-interactive", text: "flag \"--no-interactive\" help=\"Disable all interactive features (auto-prompting, explorer auto-launch, TUI forms)\" global=#true default=#false"}, {key: "dry-run", text: "flag \"--dry-run\" help=\"Preview API requests without sending them (no network, no OS keychain). Human preview on stderr; with -o json or --jq, one JSON object per request on stdout. Local mutation commands (auth login, auth logout and configure) make no request: they skip prompts and writes and report a no-op (stderr, or one JSON object on stdout in the machine form)\" global=#true default=#false"}, {key: "debug", text: "flag \"-d --debug\" help=\"Log request and response diagnostics to stderr\" global=#true default=#false"}, {key: "agent-mode", text: "flag \"--agent-mode\" help=\"Enable structured errors and default TOON output for AI coding agents. Automatically enabled when a known agent environment is detected (CLAUDECODE, CURSOR_AGENT, etc.). Use --agent-mode=false to disable.\" global=#true default=#false"}, {key: "secret-key", text: "flag \"--secret-key \" help=\"HTTP Bearer\" global=#true env=\"MODAPI_SECRET_KEY\" config=\"security.secret_key\""}, {key: "no-keyring", text: "flag \"--no-keyring\" help=\"Never read or write the OS keychain; store secrets in the config file instead\" global=#true env=\"MODAPI_NO_KEYRING\" config=\"no_keyring\" default=#false"}}, children: []string{"account", "actions", "analytics", "auth", "authors", "channels", "completion", "configure", "content", "explore", "feedback", "help", "projects", "queue", "version", "webhook-secret", "webhooks", "whoami", "wordlist"}}, + "": {head: "name \"modapi\"\nbin \"modapi\"\nabout \"Moderation API: Moderate text, images, video and audio in real time, manage authors, review queues and actions, and receive webhooks.\"\nversion \"0.7.0\"\nconfig {\n file \"~/.config/modapi/config.yaml\"\n}", flags: []usageItem{{key: "usage", text: "flag \"--usage\" help=\"Print the CLI Usage schema in KDL format\" global=#true"}, {key: "version", text: "flag \"-v --version\" help=\"Print the CLI version\""}, {key: "output-format", text: "flag \"-o --output-format \" help=\"Specify the output format. Options: pretty, json, yaml, table, toon.\" global=#true config=\"output_format\" default=\"pretty\""}, {key: "color", text: "flag \"--color \" help=\"Control colored output: auto (color when output is a TTY), always, or never. Respects NO_COLOR and FORCE_COLOR env vars.\" global=#true default=\"auto\""}, {key: "jq", text: "flag \"-q --jq \" help=\"Filter and transform output using a jq expression (e.g., '.name', '.items[] | .id')\" global=#true"}, {key: "raw-output", text: "flag \"--raw-output\" help=\"Write --jq string results as raw text instead of JSON strings (like jq -r); non-string results stay JSON\" global=#true default=#false"}, {key: "server-url", text: "flag \"--server-url \" help=\"Override the default server URL\" global=#true"}, {key: "server", text: "flag \"--server \" help=\"Select a server by index (for indexed servers) or name (for named servers)\" global=#true"}, {key: "header", text: "flag \"-H --header \" help=\"Set a custom HTTP request header (format: \\\"Key: Value\\\"). Can be specified multiple times.\" global=#true var=#true"}, {key: "include-headers", text: "flag \"--include-headers\" help=\"Include HTTP response headers in the output\" global=#true default=#false"}, {key: "timeout", text: "flag \"--timeout \" help=\"HTTP request timeout (e.g., 30s, 5m, 100ms)\" global=#true config=\"timeout\""}, {key: "interactive", text: "flag \"--interactive\" help=\"Prompt for missing inputs and open guided configure/auth forms (forms fall back to line prompts on stdin off-TTY)\" global=#true default=#true"}, {key: "no-interactive", text: "flag \"--no-interactive\" help=\"Disable all interactive features (auto-prompting, explorer auto-launch, TUI forms)\" global=#true default=#false"}, {key: "dry-run", text: "flag \"--dry-run\" help=\"Preview API requests without sending them (no network, no OS keychain). Human preview on stderr; with -o json or --jq, one JSON object per request on stdout. Local mutation commands (auth login, auth logout and configure) make no request: they skip prompts and writes and report a no-op (stderr, or one JSON object on stdout in the machine form)\" global=#true default=#false"}, {key: "debug", text: "flag \"-d --debug\" help=\"Log request and response diagnostics to stderr\" global=#true default=#false"}, {key: "agent-mode", text: "flag \"--agent-mode\" help=\"Enable structured errors and default TOON output for AI coding agents. Automatically enabled when a known agent environment is detected (CLAUDECODE, CURSOR_AGENT, etc.). Use --agent-mode=false to disable.\" global=#true default=#false"}, {key: "secret-key", text: "flag \"--secret-key \" help=\"HTTP Bearer\" global=#true env=\"MODAPI_SECRET_KEY\" config=\"security.secret_key\""}, {key: "no-keyring", text: "flag \"--no-keyring\" help=\"Never read or write the OS keychain; store secrets in the config file instead\" global=#true env=\"MODAPI_NO_KEYRING\" config=\"no_keyring\" default=#false"}}, children: []string{"account", "actions", "analytics", "auth", "authors", "channels", "completion", "configure", "content", "explore", "feedback", "help", "projects", "queue", "version", "webhook-secret", "webhooks", "whoami", "wordlist"}}, "account": {head: "cmd \"account\" help=\"Operations for account\"", children: []string{"retrieve"}}, "account retrieve": {head: "cmd \"retrieve\" help=\"Get account details\"", flags: []usageItem{{key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}}}, "actions": {head: "cmd \"actions\" help=\"Operations for actions\"", children: []string{"create", "delete", "execute", "list", "retrieve", "update"}}, @@ -92,9 +92,9 @@ var usageNodes = map[string]usageNode{ "queue get-stats": {head: "cmd \"get-stats\" help=\"Get queue statistics\"", aliases: []usageItem{{key: "gs", text: " alias \"gs\""}}, args: []usageItem{{key: "id", text: " arg \"id\" help=\"The queue ID. (or use --id)\""}}, flags: []usageItem{{key: "id", text: " flag \"-i --id \" help=\"The queue ID. (or pass it as the [id] argument)\""}, {key: "within-days", text: " flag \"-w --within-days \" help=\"integer value\" default=30"}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}}}, "queue items": {head: "cmd \"items\" help=\"Operations for items\"", children: []string{"list", "resolve", "resolve-many", "unresolve"}}, "queue items list": {head: "cmd \"list\" help=\"List queue items\"", args: []usageItem{{key: "id", text: " arg \"id\" help=\"The queue ID. (or use --id)\""}}, flags: []usageItem{{key: "id", text: " flag \"-i --id \" help=\"The queue ID. (or pass it as the [id] argument)\""}, {key: "page", text: " flag \"--page \" help=\"integer value\" default=1"}, {key: "page-size", text: " flag \"--page-size \" help=\"integer value\" default=20"}, {key: "sort", text: " flag \"-s --sort \" help=\"options: FIFO, LIFO, RANDOM, SEVERITY_DESC\""}, {key: "include-resolved", text: " flag \"--include-resolved\" help=\"boolean flag\""}, {key: "conversation-ids", text: " flag \"-c --conversation-ids \" help=\"list of values\" var=#true"}, {key: "labels", text: " flag \"-l --labels \" help=\"list of values\" var=#true"}, {key: "author-id", text: " flag \"--author-id \" help=\"string value\""}, {key: "filtered-action-ids", text: " flag \"-f --filtered-action-ids \" help=\"list of values\" var=#true"}, {key: "after-date", text: " flag \"--after-date \" help=\"string value\""}, {key: "before-date", text: " flag \"-b --before-date \" help=\"string value\""}, {key: "project-id", text: " flag \"--project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}, {key: "all", text: " flag \"-a --all\" help=\"Automatically paginate and fetch all results (streams NDJSON for JSON output)\""}, {key: "max-pages", text: " flag \"--max-pages \" help=\"Maximum number of pages to fetch when using --all (0 = no limit)\" default=0"}}}, - "queue items resolve": {head: "cmd \"resolve\" help=\"Resolve a queue item\"", flags: []usageItem{{key: "id", text: " flag \"--id \" help=\"The queue ID. [required]\""}, {key: "item-id", text: " flag \"--item-id \" help=\"The item ID to resolve. [required]\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}}}, - "queue items resolve-many": {head: "cmd \"resolve-many\" help=\"Resolve queue items\"", aliases: []usageItem{{key: "rm", text: " alias \"rm\""}}, args: []usageItem{{key: "id", text: " arg \"id\" help=\"The queue ID. (or use --id)\""}}, flags: []usageItem{{key: "id", text: " flag \"-i --id \" help=\"The queue ID. (or pass it as the [id] argument)\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}, {key: "item-ids", text: " flag \"--item-ids \" help=\"The item IDs to resolve. Up to 100. [required]\" var=#true"}, {key: "body", text: " flag \"--body \" help=\"Request body as JSON (alternative to individual flags). Can also be provided via stdin; @path reads a file, @- reads stdin to EOF. Use --schema to print the exact JSON Schema.\""}, {key: "schema", text: " flag \"--schema\" help=\"Print the exact JSON Schema of the request body and exit\""}}}, - "queue items unresolve": {head: "cmd \"unresolve\" help=\"Unresolve a queue item\"", flags: []usageItem{{key: "id", text: " flag \"--id \" help=\"The queue ID. [required]\""}, {key: "item-id", text: " flag \"--item-id \" help=\"The item ID to unresolve. [required]\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}}}, + "queue items resolve": {head: "cmd \"resolve\" help=\"Resolve a queue item\"", flags: []usageItem{{key: "id", text: " flag \"--id \" help=\"The queue ID. [required]\""}, {key: "item-id", text: " flag \"--item-id \" help=\"The item to resolve: the item 'id' from 'GET /queue/{id}/items', or the 'contentId' you sent to 'POST /moderate' (webhooks send it as 'item.id'). For a content ID, the newest submission with that content ID is used, as the queue shows it. [required]\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}}}, + "queue items resolve-many": {head: "cmd \"resolve-many\" help=\"Resolve queue items\"", aliases: []usageItem{{key: "rm", text: " alias \"rm\""}}, args: []usageItem{{key: "id", text: " arg \"id\" help=\"The queue ID. (or use --id)\""}}, flags: []usageItem{{key: "id", text: " flag \"-i --id \" help=\"The queue ID. (or pass it as the [id] argument)\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}, {key: "item-ids", text: " flag \"--item-ids \" help=\"The items to resolve, up to 100. Each is the item 'id' from 'GET /queue/{id}/items', or the 'contentId' you sent to 'POST /moderate' (webhooks send it as 'item.id'). For a content ID, the newest submission with that content ID is used, as the queue shows it. [required]\" var=#true"}, {key: "body", text: " flag \"--body \" help=\"Request body as JSON (alternative to individual flags). Can also be provided via stdin; @path reads a file, @- reads stdin to EOF. Use --schema to print the exact JSON Schema.\""}, {key: "schema", text: " flag \"--schema\" help=\"Print the exact JSON Schema of the request body and exit\""}}}, + "queue items unresolve": {head: "cmd \"unresolve\" help=\"Unresolve a queue item\"", flags: []usageItem{{key: "id", text: " flag \"--id \" help=\"The queue ID. [required]\""}, {key: "item-id", text: " flag \"--item-id \" help=\"The item to unresolve: the item 'id' from 'GET /queue/{id}/items', or the 'contentId' you sent to 'POST /moderate' (webhooks send it as 'item.id'). For a content ID, the newest submission with that content ID is used, as the queue shows it. [required]\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}}}, "queue list": {head: "cmd \"list\" help=\"List queues\"", flags: []usageItem{{key: "page", text: " flag \"--page \" help=\"integer value\" default=1"}, {key: "page-size", text: " flag \"--page-size \" help=\"integer value\" default=20"}, {key: "project-id", text: " flag \"--project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}, {key: "all", text: " flag \"-a --all\" help=\"Automatically paginate and fetch all results (streams NDJSON for JSON output)\""}, {key: "max-pages", text: " flag \"--max-pages \" help=\"Maximum number of pages to fetch when using --all (0 = no limit)\" default=0"}}}, "queue retrieve": {head: "cmd \"retrieve\" help=\"Get a queue\"", args: []usageItem{{key: "id", text: " arg \"id\" help=\"The queue ID. (or use --id)\""}}, flags: []usageItem{{key: "id", text: " flag \"-i --id \" help=\"The queue ID. (or pass it as the [id] argument)\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}}}, "version": {head: "cmd \"version\" help=\"Print the CLI version\""}, @@ -107,8 +107,9 @@ var usageNodes = map[string]usageNode{ "webhooks retrieve": {head: "cmd \"retrieve\" help=\"Get a webhook\"", args: []usageItem{{key: "id", text: " arg \"id\" help=\"The ID of the webhook to get. (or use --id)\""}}, flags: []usageItem{{key: "id", text: " flag \"-i --id \" help=\"The ID of the webhook to get. (or pass it as the [id] argument)\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}}}, "webhooks update": {head: "cmd \"update\" help=\"Update a webhook\"", args: []usageItem{{key: "id", text: " arg \"id\" help=\"The ID of the webhook to update. (or use --id)\""}}, flags: []usageItem{{key: "id", text: " flag \"-i --id \" help=\"The ID of the webhook to update. (or pass it as the [id] argument)\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}, {key: "name", text: " flag \"-n --name \" help=\"The webhook's name, used to identify it in the dashboard\""}, {key: "description", text: " flag \"--description \" help=\"The webhook's description\""}, {key: "url", text: " flag \"-u --url \" help=\"The webhook's URL. We'll call this URL when an event occurs.\""}, {key: "event-types", text: " flag \"-e --event-types \" help=\"Event types this webhook subscribes to. One webhook URL receives all events you list here. (options: QUEUE_ITEM_NEW, QUEUE_ITEM_COMPLETED, QUEUE_ITEM_ACTION, QUEUE_ITEM_REJECTED, QUEUE_ITEM_ALLOWED, AUTHOR_BLOCKED, AUTHOR_UNBLOCKED, AUTHOR_SUSPENDED, AUTHOR_UPDATED, AUTHOR_TRUST_LEVEL_CHANGED, AUTHOR_ACTION)\" var=#true"}, {key: "body", text: " flag \"--body \" help=\"Request body as JSON (alternative to individual flags). Can also be provided via stdin; @path reads a file, @- reads stdin to EOF. Use --schema to print the exact JSON Schema.\""}, {key: "schema", text: " flag \"--schema\" help=\"Print the exact JSON Schema of the request body and exit\""}}}, "whoami": {head: "cmd \"whoami\" help=\"Display current authentication configuration\""}, - "wordlist": {head: "cmd \"wordlist\" help=\"Operations for wordlist\"", children: []string{"create", "get-embedding-status", "list", "retrieve", "update", "words"}}, + "wordlist": {head: "cmd \"wordlist\" help=\"Operations for wordlist\"", children: []string{"create", "delete", "get-embedding-status", "list", "retrieve", "update", "words"}}, "wordlist create": {head: "cmd \"create\" help=\"Create wordlist\"", flags: []usageItem{{key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}, {key: "name", text: " flag \"-n --name \" help=\"Name of the wordlist [required]\""}, {key: "key", text: " flag \"-k --key \" help=\"Key of the wordlist. Default: made from the name. If the key is already in use, a suffix is added.\""}, {key: "description", text: " flag \"--description \" help=\"Description of the wordlist\""}, {key: "words", text: " flag \"-w --words \" help=\"Words to add to the wordlist. Words are converted to lowercase. Duplicate and empty words are ignored. Maximum 5000 words.\" var=#true"}, {key: "body", text: " flag \"--body \" help=\"Request body as JSON (alternative to individual flags). Can also be provided via stdin; @path reads a file, @- reads stdin to EOF. Use --schema to print the exact JSON Schema.\""}, {key: "schema", text: " flag \"--schema\" help=\"Print the exact JSON Schema of the request body and exit\""}}}, + "wordlist delete": {head: "cmd \"delete\" help=\"Delete wordlist\"", args: []usageItem{{key: "id", text: " arg \"id\" help=\"ID of the wordlist to delete (or use --id)\""}}, flags: []usageItem{{key: "id", text: " flag \"-i --id \" help=\"ID of the wordlist to delete (or pass it as the [id] argument)\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}}}, "wordlist get-embedding-status": {head: "cmd \"get-embedding-status\" help=\"Get embedding status\"", aliases: []usageItem{{key: "ges", text: " alias \"ges\""}}, args: []usageItem{{key: "id", text: " arg \"id\" help=\"ID of the wordlist to check embedding status for (or use --id)\""}}, flags: []usageItem{{key: "id", text: " flag \"-i --id \" help=\"ID of the wordlist to check embedding status for (or pass it as the [id] argument)\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}}}, "wordlist list": {head: "cmd \"list\" help=\"List wordlists\"", flags: []usageItem{{key: "page", text: " flag \"--page \" help=\"integer value\" default=1"}, {key: "page-size", text: " flag \"--page-size \" help=\"integer value\" default=20"}, {key: "project-id", text: " flag \"--project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}, {key: "all", text: " flag \"-a --all\" help=\"Automatically paginate and fetch all results (streams NDJSON for JSON output)\""}, {key: "max-pages", text: " flag \"--max-pages \" help=\"Maximum number of pages to fetch when using --all (0 = no limit)\" default=0"}}}, "wordlist retrieve": {head: "cmd \"retrieve\" help=\"Get wordlist\"", args: []usageItem{{key: "id", text: " arg \"id\" help=\"ID of the wordlist to get (or use --id)\""}}, flags: []usageItem{{key: "id", text: " flag \"-i --id \" help=\"ID of the wordlist to get (or pass it as the [id] argument)\""}, {key: "project-id", text: " flag \"-p --project-id \" help=\"The project to act on, by ID or name. Required with an organization key unless the key reaches only one project; list projects with GET /projects. A project key acts on its own project and needs no value: naming another one is an error.\""}}},